diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..07843745db --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,14 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/dotnet +{ + "name": "C# (.NET)", + "image": "mcr.microsoft.com/devcontainers/dotnet:1-7.0-jammy", + "postCreateCommand": "dotnet restore && dotnet build", + "customizations": { + "vscode": { + "extensions": [ + "ms-dotnettools.csdevkit" + ] + } + } +} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig index 175ec7b14e..269a7e444b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,14 +6,297 @@ # dotnet tool update -g dotnet-format # remember to have: git config --global core.autocrlf false #(which is usually default) +# top-most EditorConfig file root = true -# Every file - +# Don't use tabs for indentation. [*] +indent_style = space insert_final_newline = true trim_trailing_whitespace = true charset = utf-8 end_of_line = lf +# (Please don't specify an indent_size here; that has too many unintended consequences.) +spelling_exclusion_path = SpellingExclusions.dic + +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 + +# XML project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +# XML config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_size = 2 + +# JSON files +[*.json] +indent_size = 2 + +# Powershell files +[*.ps1] +indent_size = 2 + +# Shell script files +[*.sh] +end_of_line = lf +indent_size = 2 + +# Dotnet code style settings: +[*.{cs,vb}] +# Member can be made 'readonly' +csharp_style_prefer_readonly_struct_member = true +dotnet_diagnostic.IDE0251.severity = warning +dotnet_diagnostic.IDE0044.severity = warning + dotnet_diagnostic.CS1591.severity = silent + +# Sort using and Import directives with System.* appearing first +dotnet_sort_system_directives_first = false +dotnet_separate_import_directive_groups = false +# Avoid "this." and "Me." if not necessary +dotnet_style_qualification_for_field = false:refactoring +dotnet_style_qualification_for_property = false:refactoring +dotnet_style_qualification_for_method = false:refactoring +dotnet_style_qualification_for_event = false:refactoring + +# Use language keywords instead of framework type names for type references +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Suggest more modern language features when available +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion + +# Whitespace options +dotnet_style_allow_multiple_blank_lines_experimental = false + +# Non-private static fields are PascalCase +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style + +dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field +dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected +dotnet_naming_symbols.non_private_static_fields.required_modifiers = static + +dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case + +# Non-private readonly fields are PascalCase +dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.symbols = non_private_readonly_fields +dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = non_private_readonly_field_style + +dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field +dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected +dotnet_naming_symbols.non_private_readonly_fields.required_modifiers = readonly + +dotnet_naming_style.non_private_readonly_field_style.capitalization = pascal_case + +# Constants are PascalCase +dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants +dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style + +dotnet_naming_symbols.constants.applicable_kinds = field, local +dotnet_naming_symbols.constants.required_modifiers = const + +dotnet_naming_style.constant_style.capitalization = pascal_case + +# Static fields are camelCase and start with s_ +dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion +dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields +dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style + +dotnet_naming_symbols.static_fields.applicable_kinds = field +dotnet_naming_symbols.static_fields.required_modifiers = static + +dotnet_naming_style.static_field_style.capitalization = camel_case +dotnet_naming_style.static_field_style.required_prefix = s_ + +# Instance fields are camelCase and start with _ +dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion +dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields +dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style + +dotnet_naming_symbols.instance_fields.applicable_kinds = field + +dotnet_naming_style.instance_field_style.capitalization = camel_case +dotnet_naming_style.instance_field_style.required_prefix = _ + +# Locals and parameters are camelCase +dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion +dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters +dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style + +dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local + +dotnet_naming_style.camel_case_style.capitalization = camel_case + +# Local functions are PascalCase +dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions +dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style + +dotnet_naming_symbols.local_functions.applicable_kinds = local_function + +dotnet_naming_style.local_function_style.capitalization = pascal_case + +# By default, name items with PascalCase +dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members +dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style + +dotnet_naming_symbols.all_members.applicable_kinds = * + +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +file_header_template = Copyright (C) 2015-2024 The Neo Project.\n\n{fileName} file belongs to the neo project and is free\nsoftware distributed under the MIT software license, see the\naccompanying file LICENSE in the main directory of the\nrepository or http://www.opensource.org/licenses/mit-license.php\nfor more details.\n\nRedistribution and use in source and binary forms with or without\nmodifications are permitted. + +# Require file header +dotnet_diagnostic.IDE0073.severity = error + +# RS0016: Only enable if API files are present +dotnet_public_api_analyzer.require_api_files = true + +# IDE0055: Fix formatting +# Workaround for https://github.com/dotnet/roslyn/issues/70570 +dotnet_diagnostic.IDE0055.severity = warning + +# CSharp code style settings: +[*.cs] +# Newline settings +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left + +# Whitespace options +csharp_style_allow_embedded_statements_on_same_line_experimental = false +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = false +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = false + +# Prefer "var" everywhere +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = true:suggestion + +# Prefer method-like constructs to have a block body +csharp_style_expression_bodied_methods = false:none +csharp_style_expression_bodied_constructors = false:none +csharp_style_expression_bodied_operators = false:none + +# Prefer property-like constructs to have an expression-body +csharp_style_expression_bodied_properties = true:none +csharp_style_expression_bodied_indexers = true:none +csharp_style_expression_bodied_accessors = true:none + +# Suggest more modern language features when available +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = do_not_ignore +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Blocks are allowed +csharp_prefer_braces = true:silent +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +# IDE0060: Remove unused parameter +dotnet_diagnostic.IDE0060.severity = none + +[src/{Analyzers,CodeStyle,Features,Workspaces,EditorFeatures,VisualStudio}/**/*.{cs,vb}] + +# IDE0011: Add braces +csharp_prefer_braces = when_multiline:warning +# NOTE: We need the below severity entry for Add Braces due to https://github.com/dotnet/roslyn/issues/44201 +dotnet_diagnostic.IDE0011.severity = warning + +# IDE0040: Add accessibility modifiers +dotnet_diagnostic.IDE0040.severity = warning + +# IDE0052: Remove unread private member +dotnet_diagnostic.IDE0052.severity = warning + +# IDE0059: Unnecessary assignment to a value +dotnet_diagnostic.IDE0059.severity = warning + +# CA1012: Abstract types should not have public constructors +dotnet_diagnostic.CA1012.severity = warning + +# CA1822: Make member static +dotnet_diagnostic.CA1822.severity = warning + +# Prefer "var" everywhere +dotnet_diagnostic.IDE0007.severity = warning +csharp_style_var_for_built_in_types = true:warning +csharp_style_var_when_type_is_apparent = true:warning +csharp_style_var_elsewhere = true:warning + +# csharp_style_allow_embedded_statements_on_same_line_experimental +dotnet_diagnostic.IDE2001.severity = warning + +# csharp_style_allow_blank_lines_between_consecutive_braces_experimental +dotnet_diagnostic.IDE2002.severity = warning + +# csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental +dotnet_diagnostic.IDE2004.severity = warning + +# csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental +dotnet_diagnostic.IDE2005.severity = warning + +# csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental +dotnet_diagnostic.IDE2006.severity = warning + +[src/{VisualStudio}/**/*.{cs,vb}] +# CA1822: Make member static +# There is a risk of accidentally breaking an internal API that partners rely on though IVT. +dotnet_code_quality.CA1822.api_surface = private \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..0c37abbc2d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,64 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text eol=lf + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +*.sln text eol=crlf +#*.csproj text eol=crlf +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +*.jpg binary +*.png binary +*.gif binary +*.ico binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md b/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md index 4c2e7a8977..9b98d24f99 100644 --- a/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md +++ b/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md @@ -12,10 +12,6 @@ A summary of the problem you want to solve or metric you want to improve **Do you have any solution you want to propose?** A clear and concise description of what you expect with this change. -**Neo Version** -- Neo 2 -- Neo 3 - **Where in the software does this update applies to?** - Compiler - Consensus diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..7b043f14a7 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,35 @@ +# Description + + + +Fixes # (issue) + +## Type of change + + + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +# How Has This Been Tested? + + + +- [ ] Test A +- [ ] Test B + +**Test Configuration**: + + +# Checklist: + +- [ ] My code follows the style guidelines of this project +- [ ] I have performed a self-review of my code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes +- [ ] Any dependent changes have been merged and published in downstream modules diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 743c8febe6..da9e47f20b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,77 +2,100 @@ name: .NET Core Test and Publish on: push: - branches: master + branches: [master] pull_request: env: - DOTNET_VERSION: 6.0.x + DOTNET_VERSION: 7.0.x jobs: Test: - runs-on: ubuntu-latest + timeout-minutes: 15 + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} steps: - name: Checkout - uses: actions/checkout@v2 - - name: Setup .NET Core - uses: actions/setup-dotnet@v1 + uses: actions/checkout@v4 + - name: Setup .NET + uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} - name: Check format + if: matrix.os == 'ubuntu-latest' run: | dotnet format --verify-no-changes --verbosity diagnostic - name: Test + if: matrix.os != 'ubuntu-latest' + run: | + dotnet test + - name: Test for coverall + if: matrix.os == 'ubuntu-latest' run: | find tests -name *.csproj | xargs -I % dotnet add % package coverlet.msbuild - dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=lcov /p:CoverletOutput=${GITHUB_WORKSPACE}/coverage/lcov + dotnet test ./tests/Neo.Cryptography.BLS12_381.Tests /p:CollectCoverage=true /p:CoverletOutput=${GITHUB_WORKSPACE}/coverage-join/ + dotnet test ./tests/Neo.ConsoleService.Tests /p:CollectCoverage=true /p:CoverletOutput=${GITHUB_WORKSPACE}/coverage-join/ /p:MergeWith=${GITHUB_WORKSPACE}/coverage-join/coverage.json + dotnet test ./tests/Neo.UnitTests /p:CollectCoverage=true /p:CoverletOutput=${GITHUB_WORKSPACE}/coverage-join/ /p:MergeWith=${GITHUB_WORKSPACE}/coverage-join/coverage.net7.0.json + dotnet test ./tests/Neo.VM.Tests /p:CollectCoverage=true /p:CoverletOutput=${GITHUB_WORKSPACE}/coverage-join/ /p:MergeWith=${GITHUB_WORKSPACE}/coverage-join/coverage.net7.0.json + dotnet test ./tests/Neo.Json.UnitTests /p:CollectCoverage=true /p:CoverletOutput=${GITHUB_WORKSPACE}/coverage/lcov /p:MergeWith=${GITHUB_WORKSPACE}/coverage-join/coverage.net7.0.json /p:CoverletOutputFormat=lcov - name: Coveralls - uses: coverallsapp/github-action@master + if: matrix.os == 'ubuntu-latest' + uses: coverallsapp/github-action@v2.2.3 with: github-token: ${{ secrets.GITHUB_TOKEN }} + format: lcov + file: ${GITHUB_WORKSPACE}/coverage/lcov.net7.0.info - PublishGithub: - # Because sometimes this action is not working as expected we will disable it until determine that it's more stable - if: false && github.ref == 'refs/heads/master' && startsWith(github.repository, 'neo-project/') - needs: Test - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Setup .NET Core - uses: actions/setup-dotnet@v1 - with: - dotnet-version: ${{ env.DOTNET_VERSION }} - - name: Setup NuGet.exe for use with actions - uses: NuGet/setup-nuget@v1 - - name: Pack with dotnet - run: git rev-list --count HEAD |xargs printf "CI%05d" |xargs dotnet pack -c Debug -o out --include-source --version-suffix - - name: Publish to Github Packages - run: | - nuget source Add -Name "GitHub" -Source "https://nuget.pkg.github.com/neo-project/index.json" -UserName neo-project -Password ${GITHUB_TOKEN} - nuget push out/*.nupkg -Source "GitHub" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - PublishMyGet: + PublishPackage: if: github.ref == 'refs/heads/master' && startsWith(github.repository, 'neo-project/') needs: Test runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Setup .NET Core - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} - - name: Pack with dotnet - run: git rev-list --count HEAD |xargs printf "CI%05d" |xargs dotnet pack -c Debug -o out --include-source --version-suffix - - name: Publish to MyGet - run: dotnet nuget push out/*.nupkg -s https://www.myget.org/F/neo/api/v2/package -k ${MYGET_TOKEN} -ss https://www.myget.org/F/neo/symbols/api/v2/package -sk ${MYGET_TOKEN} - env: - MYGET_TOKEN: ${{ secrets.MYGET_TOKEN }} + + - name: Set Version + run: git rev-list --count HEAD | xargs printf 'CI%05d' | xargs -I{} echo 'VERSION_SUFFIX={}' >> $GITHUB_ENV + + - name : Pack (Neo) + 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: | + dotnet nuget push * \ + --source https://nuget.pkg.github.com/neo-project/index.json \ + --api-key "${{ secrets.GITHUB_TOKEN }}" \ + --disable-buffering \ + --no-service-endpoint; + + - name: Publish to myGet + working-directory: ./out + run: | + dotnet nuget push * \ + --source https://www.myget.org/F/neo/api/v3/index.json \ + --api-key "${{ secrets.MYGET_TOKEN }}" \ + --disable-buffering \ + --no-service-endpoint; Release: if: github.ref == 'refs/heads/master' && startsWith(github.repository, 'neo-project/') @@ -80,12 +103,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Get version id: get_version run: | sudo apt install xmlstarlet - find src -name *.csproj | xargs xmlstarlet sel -t -v "concat('::set-output name=version::v',//VersionPrefix/text())" | xargs echo + find src -name Directory.Build.props | xargs xmlstarlet sel -N i=http://schemas.microsoft.com/developer/msbuild/2003 -t -v "concat('::set-output name=version::v',//i:VersionPrefix/text())" | xargs echo - name: Check tag id: check_tag run: curl -s -I ${{ format('https://github.com/{0}/releases/tag/{1}', github.repository, steps.get_version.outputs.version) }} | head -n 1 | cut -d$' ' -f2 | xargs printf "::set-output name=statusCode::%s" | xargs echo @@ -99,15 +122,56 @@ jobs: tag_name: ${{ steps.get_version.outputs.version }} release_name: ${{ steps.get_version.outputs.version }} prerelease: ${{ contains(steps.get_version.outputs.version, '-') }} - - name: Setup .NET Core + - name: Setup .NET if: steps.check_tag.outputs.statusCode == '404' - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} + - name : Pack (Neo) + if: steps.check_tag.outputs.statusCode == '404' + run: | + dotnet pack ./src/Neo \ + --configuration Release \ + --output ./out + - name : Pack (Neo.IO) + if: steps.check_tag.outputs.statusCode == '404' + run: | + dotnet pack ./src/Neo.IO \ + --configuration Release \ + --output ./out + - name : Pack (Neo.Extensions) + if: steps.check_tag.outputs.statusCode == '404' + run: | + dotnet pack ./src/Neo.Extensions \ + --configuration Release \ + --output ./out + - name : Pack (Neo.Json) + if: steps.check_tag.outputs.statusCode == '404' + run: | + dotnet pack ./src/Neo.Json \ + --configuration Release \ + --output ./out + - name : Pack (Neo.VM) + if: steps.check_tag.outputs.statusCode == '404' + run: | + dotnet pack ./src/Neo.VM \ + --configuration Release \ + --output ./out + - name : Pack (Neo.ConsoleService) + if: steps.check_tag.outputs.statusCode == '404' + run: | + dotnet pack ./src/Neo.ConsoleService \ + --configuration Release \ + --output ./out + - name : Pack (Neo.Cryptography.BLS12_381) + if: steps.check_tag.outputs.statusCode == '404' + run: | + dotnet pack ./src/Neo.Cryptography.BLS12_381 \ + --configuration Release \ + --output ./out - name: Publish to NuGet if: steps.check_tag.outputs.statusCode == '404' run: | - dotnet pack -o out -c Release dotnet nuget push out/*.nupkg -s https://api.nuget.org/v3/index.json -k ${NUGET_TOKEN} env: NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }} diff --git a/.github/workflows/pkgs-delete.yml b/.github/workflows/pkgs-delete.yml new file mode 100644 index 0000000000..cf3471b551 --- /dev/null +++ b/.github/workflows/pkgs-delete.yml @@ -0,0 +1,128 @@ +name: Package Cleanup + +on: + schedule: + - cron: '0 0 * * *' # Run every day at 24:00 + +jobs: + + delete-myget-pkgs: + name: Delete Old MyGet Packages + runs-on: ubuntu-latest + steps: + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - name: Install Requests + run: pip install requests + - name: Install Packaging + run: pip install packaging + - name: Delete versions below 3.6.1 + env: + MYGET_FEED: 'neo' + PACKAGE_NAMES: 'Neo.VM,Neo.Json,Neo.IO,Neo,Neo.ConsoleService,Neo.Extensions' # Neo.Cryptography.BLS12_381 version is 0.x + MYGET_API_KEY: ${{ secrets.MYGET_TOKEN }} + run: | + import requests + from packaging import version + import os + + def get_versions(feed, package_name, api_key): + url = f"https://www.myget.org/F/{feed}/api/v2/Packages?$select=Version&$filter=Id eq '{package_name}'&$format=json" + headers = {'Accept': 'application/json'} + response = requests.get(url, headers=headers) + if response.status_code == 200: + versions = response.json()['d']['results'] + return [ver['Version'] for ver in versions] + else: + return [] + + def delete_version(feed, package_name, ver, api_key): + url = f"https://www.myget.org/F/{feed}/api/v2/package/{package_name}/{ver}?hardDelete=true" + headers = {"X-NuGet-ApiKey": api_key} + response = requests.delete(url, headers=headers) + return response.status_code == 200 # Success + + feed = os.environ['MYGET_FEED'] + package_names = os.environ['PACKAGE_NAMES'].split(',') + api_key = os.environ['MYGET_API_KEY'] + + for package_name in package_names: + versions_to_delete = get_versions(feed, package_name, api_key) + for ver in versions_to_delete: + if version.parse(ver.split("-", 1)[0]) >= version.Version("3.6.1"): + print(f"Omited {ver} of package {package_name}.") + continue + if delete_version(feed, package_name, ver, api_key): + print(f"Deleted version {ver} of package {package_name}.") + else: + print(f"Failed to delete version {ver} of package {package_name}.") + + shell: python + + delete-git-pkgs: + name: Delete Old Nuget Packages + runs-on: ubuntu-latest + + steps: + - name: Delete Neo.Cryptography.BLS12_381 Package + uses: actions/delete-package-versions@v4 + 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" + 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 }}" + + - 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 + uses: actions/delete-package-versions@v4 + with: + package-name: Neo.Extensions + package-type: nuget + min-versions-to-keep: 3 + delete-only-pre-release-versions: "true" + token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.gitignore b/.gitignore index cb49b769de..1358af3bde 100644 --- a/.gitignore +++ b/.gitignore @@ -256,3 +256,4 @@ paket-files/ PublishProfiles /.vscode +launchSettings.json diff --git a/NuGet.Config b/NuGet.Config index c06788942f..53fc635420 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -2,7 +2,8 @@ + - \ No newline at end of file + diff --git a/README.md b/README.md index 14557da887..37d1f00827 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,9 @@
Neo · - Neo VM - · Neo Modules · Neo DevPack - · - Neo Node

@@ -81,9 +77,6 @@

- - Current TravisCI build status. - Current neo version. @@ -95,7 +88,11 @@

- +

+ + Open in GitHub Codespaces. + +

## Table of Contents @@ -112,10 +109,6 @@ This repository contain main classes of the Visit the [documentation](https://docs.neo.org/docs/en-us/index.html) to get started. -*Note: This is Neo 3 branch, currently under development. For the current stable version, please [click here.](https://github.com/neo-project/neo/tree/master-2.x)* - - - ## Project structure An overview of the project folders can be seen below. @@ -137,8 +130,6 @@ An overview of the project folders can be seen below. Code references are provided for all platform building blocks. That includes the base library, the VM, a command line application and the compiler. * [neo:](https://github.com/neo-project/neo/) Neo core library, contains base classes, including ledger, p2p and IO modules. -* [neo-vm:](https://github.com/neo-project/neo-vm/) Neo Virtual Machine is a decoupled VM that Neo uses to execute its scripts. It also uses the `InteropService` layer to extend its functionalities. -* [neo-node:](https://github.com/neo-project/neo-node/) Executable version of the Neo library, exposing features using a command line application or GUI. * [neo-modules:](https://github.com/neo-project/neo-modules/) Neo modules include additional tools and plugins to be used with Neo. * [neo-devpack-dotnet:](https://github.com/neo-project/neo-devpack-dotnet/) These are the official tools used to convert a C# smart-contract into a *neo executable file*. diff --git a/SpellingExclusions.dic b/SpellingExclusions.dic new file mode 100644 index 0000000000..e69de29bb2 diff --git a/benchmarks/Neo.Benchmarks/Benchmarks.cs b/benchmarks/Neo.Benchmarks/Benchmarks.cs new file mode 100644 index 0000000000..081f806622 --- /dev/null +++ b/benchmarks/Neo.Benchmarks/Benchmarks.cs @@ -0,0 +1,79 @@ +// 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 Neo.Network.P2P.Payloads; +using Neo.SmartContract; +using Neo.VM; +using System.Diagnostics; + +namespace Neo; + +static class Benchmarks +{ + private static readonly ProtocolSettings protocol = ProtocolSettings.Load("config.json"); + private static readonly NeoSystem system = new(protocol, (string)null); + + public static void NeoIssue2725() + { + // https://github.com/neo-project/neo/issues/2725 + // L00: INITSSLOT 1 + // L01: NEWARRAY0 + // L02: PUSHDATA1 6161616161 //"aaaaa" + // L03: PUSHINT16 500 + // L04: STSFLD0 + // L05: OVER + // L06: OVER + // L07: SYSCALL 95016f61 //System.Runtime.Notify + // L08: LDSFLD0 + // L09: DEC + // L10: DUP + // L11: STSFLD0 + // L12: JMPIF L05 + // L13: CLEAR + // L14: SYSCALL dbfea874 //System.Runtime.GetExecutingScriptHash + // L15: PUSHINT16 8000 + // L16: STSFLD0 + // L17: DUP + // L18: SYSCALL 274335f1 //System.Runtime.GetNotifications + // L19: DROP + // L20: LDSFLD0 + // L21: DEC + // L22: DUP + // L23: STSFLD0 + // L24: JMPIF L17 + Run(nameof(NeoIssue2725), "VgHCDAVhYWFhYQH0AWBLS0GVAW9hWJ1KYCT1SUHb/qh0AUAfYEpBJ0M18UVYnUpgJPU="); + } + + private static void Run(string name, string poc) + { + Random random = new(); + Transaction tx = new() + { + Version = 0, + Nonce = (uint)random.Next(), + SystemFee = 20_00000000, + NetworkFee = 1_00000000, + ValidUntilBlock = ProtocolSettings.Default.MaxTraceableBlocks, + Signers = Array.Empty(), + Attributes = Array.Empty(), + Script = Convert.FromBase64String(poc), + Witnesses = Array.Empty() + }; + using var snapshot = system.GetSnapshot(); + 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/Neo.Benchmarks.csproj b/benchmarks/Neo.Benchmarks/Neo.Benchmarks.csproj new file mode 100644 index 0000000000..17698eed98 --- /dev/null +++ b/benchmarks/Neo.Benchmarks/Neo.Benchmarks.csproj @@ -0,0 +1,22 @@ + + + + Exe + net7.0 + Neo + enable + false + + + + + + + + + PreserveNewest + PreserveNewest + + + + diff --git a/benchmarks/Neo.Benchmarks/Program.cs b/benchmarks/Neo.Benchmarks/Program.cs new file mode 100644 index 0000000000..9d4125bb9f --- /dev/null +++ b/benchmarks/Neo.Benchmarks/Program.cs @@ -0,0 +1,18 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Program.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; +using System.Reflection; + +foreach (var method in typeof(Benchmarks).GetMethods(BindingFlags.Public | BindingFlags.Static)) +{ + method.CreateDelegate().Invoke(); +} diff --git a/benchmarks/Neo.Benchmarks/config.json b/benchmarks/Neo.Benchmarks/config.json new file mode 100644 index 0000000000..01471e4c76 --- /dev/null +++ b/benchmarks/Neo.Benchmarks/config.json @@ -0,0 +1,71 @@ +{ + "ApplicationConfiguration": { + "Logger": { + "Path": "Logs", + "ConsoleOutput": false, + "Active": false + }, + "Storage": { + "Engine": "LevelDBStore", // Candidates [MemoryStore, LevelDBStore, RocksDBStore] + "Path": "Data_LevelDB_{0}" // {0} is a placeholder for the network id + }, + "P2P": { + "Port": 10333, + "MinDesiredConnections": 10, + "MaxConnections": 40, + "MaxConnectionsPerAddress": 3 + }, + "UnlockWallet": { + "Path": "", + "Password": "", + "IsActive": false + }, + "Contracts": { + "NeoNameService": "0x50ac1c37690cc2cfc594472833cf57505d5f46de" + } + }, + "ProtocolConfiguration": { + "Network": 860833102, + "AddressVersion": 53, + "MillisecondsPerBlock": 15000, + "MaxTransactionsPerBlock": 512, + "MemoryPoolMaxTransactions": 50000, + "MaxTraceableBlocks": 2102400, + "Hardforks": { + "HF_Aspidochelone": 1730000, + "HF_Basilisk": 4120000 + }, + "InitialGasDistribution": 5200000000000000, + "ValidatorsCount": 7, + "StandbyCommittee": [ + "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", + "02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", + "03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", + "02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", + "024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", + "02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", + "02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", + "023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe", + "03708b860c1de5d87f5b151a12c2a99feebd2e8b315ee8e7cf8aa19692a9e18379", + "03c6aa6e12638b36e88adc1ccdceac4db9929575c3e03576c617c49cce7114a050", + "03204223f8c86b8cd5c89ef12e4f0dbb314172e9241e30c9ef2293790793537cf0", + "02a62c915cf19c7f19a50ec217e79fac2439bbaad658493de0c7d8ffa92ab0aa62", + "03409f31f0d66bdc2f70a9730b66fe186658f84a8018204db01c106edc36553cd0", + "0288342b141c30dc8ffcde0204929bb46aed5756b41ef4a56778d15ada8f0c6654", + "020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639", + "0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30", + "03d281b42002647f0113f36c7b8efb30db66078dfaaa9ab3ff76d043a98d512fde", + "02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad", + "0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d", + "03cdcea66032b82f5c30450e381e5295cae85c5e6943af716cc6b646352a6067dc", + "02cd5a5547119e24feaa7c2a0f37b8c9366216bab7054de0065c9be42084003c8a" + ], + "SeedList": [ + "seed1.neo.org:10333", + "seed2.neo.org:10333", + "seed3.neo.org:10333", + "seed4.neo.org:10333", + "seed5.neo.org:10333" + ] + } +} diff --git a/benchmarks/Neo.VM.Benchmarks/Benchmarks.cs b/benchmarks/Neo.VM.Benchmarks/Benchmarks.cs new file mode 100644 index 0000000000..6eab691a7d --- /dev/null +++ b/benchmarks/Neo.VM.Benchmarks/Benchmarks.cs @@ -0,0 +1,112 @@ +// 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/Neo.VM.Benchmarks.csproj b/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj new file mode 100644 index 0000000000..bdca77ae89 --- /dev/null +++ b/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj @@ -0,0 +1,16 @@ + + + + Exe + net7.0 + Neo.VM + enable + enable + false + + + + + + + diff --git a/benchmarks/Neo.VM.Benchmarks/Program.cs b/benchmarks/Neo.VM.Benchmarks/Program.cs new file mode 100644 index 0000000000..a9c86f405d --- /dev/null +++ b/benchmarks/Neo.VM.Benchmarks/Program.cs @@ -0,0 +1,18 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Program.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.VM; +using System.Reflection; + +foreach (var method in typeof(Benchmarks).GetMethods(BindingFlags.Public | BindingFlags.Static)) +{ + method.CreateDelegate().Invoke(); +} diff --git a/global.json b/global.json new file mode 100644 index 0000000000..423c2e2269 --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "7.0.404", + "rollForward": "latestFeature", + "allowPrerelease": false + } +} diff --git a/neo.sln b/neo.sln index 79e13f0185..b62de06a6e 100644 --- a/neo.sln +++ b/neo.sln @@ -1,15 +1,45 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29519.87 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32516.85 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "neo", "src\neo\neo.csproj", "{36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo", "src\Neo\Neo.csproj", "{36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "neo.UnitTests", "tests\neo.UnitTests\neo.UnitTests.csproj", "{5B783B30-B422-4C2F-AC22-187A8D1993F4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Json", "src\Neo.Json\Neo.Json.csproj", "{6B709ED6-64C0-451D-B07F-8F49185AE191}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.UnitTests", "tests\Neo.UnitTests\Neo.UnitTests.csproj", "{5B783B30-B422-4C2F-AC22-187A8D1993F4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Json.UnitTests", "tests\Neo.Json.UnitTests\Neo.Json.UnitTests.csproj", "{AE6C32EE-8447-4E01-8187-2AE02BB64251}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Benchmarks", "benchmarks\Neo.Benchmarks\Neo.Benchmarks.csproj", "{BCD03521-5F8F-4775-9ADF-FA361480804F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B5339DF7-5D1D-43BA-B332-74B825E1770E}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{EDE05FA8-8E73-4924-BC63-DD117127EEE1}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{C25EB0B0-0CAC-4CC1-8F36-F9229EFB99EC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.VM.Benchmarks", "benchmarks\Neo.VM.Benchmarks\Neo.VM.Benchmarks.csproj", "{E83633BA-FCF0-4A1A-B5BC-42000E24D437}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.VM", "src\Neo.VM\Neo.VM.csproj", "{0603710E-E0BA-494C-AA0F-6FB0C8A8C754}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.VM.Tests", "tests\Neo.VM.Tests\Neo.VM.Tests.csproj", "{005F84EB-EA2E-449F-930A-7B4173DDC7EC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.ConsoleService", "src\Neo.ConsoleService\Neo.ConsoleService.csproj", "{9E886812-7243-48D8-BEAF-47AADC11C054}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.GUI", "src\Neo.GUI\Neo.GUI.csproj", "{02ABDE42-9880-43B4-B6F7-8D618602A277}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.CLI", "src\Neo.CLI\Neo.CLI.csproj", "{BDFBE455-4C1F-4FC4-B5FC-1387B93A8687}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.ConsoleService.Tests", "tests\Neo.ConsoleService.Tests\Neo.ConsoleService.Tests.csproj", "{B40F8584-5AFB-452C-AEFA-009C80CC23A9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Cryptography.BLS12_381", "src\Neo.Cryptography.BLS12_381\Neo.Cryptography.BLS12_381.csproj", "{D48C1FAB-3471-4CA0-8688-25E6F43F2C25}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Cryptography.BLS12_381.Tests", "tests\Neo.Cryptography.BLS12_381.Tests\Neo.Cryptography.BLS12_381.Tests.csproj", "{387CCF6C-9A26-43F6-A639-0A82E91E10D8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Neo.IO", "src\Neo.IO\Neo.IO.csproj", "{4CDAC1AA-45C6-4157-8D8E-199050433048}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Neo.Extensions", "src\Neo.Extensions\Neo.Extensions.csproj", "{9C5213D6-3833-4570-8AE2-47E9F9017A8F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -20,17 +50,87 @@ Global {36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}.Debug|Any CPU.Build.0 = Debug|Any CPU {36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}.Release|Any CPU.ActiveCfg = Release|Any CPU {36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}.Release|Any CPU.Build.0 = Release|Any CPU + {6B709ED6-64C0-451D-B07F-8F49185AE191}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B709ED6-64C0-451D-B07F-8F49185AE191}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B709ED6-64C0-451D-B07F-8F49185AE191}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B709ED6-64C0-451D-B07F-8F49185AE191}.Release|Any CPU.Build.0 = Release|Any CPU {5B783B30-B422-4C2F-AC22-187A8D1993F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5B783B30-B422-4C2F-AC22-187A8D1993F4}.Debug|Any CPU.Build.0 = Debug|Any CPU {5B783B30-B422-4C2F-AC22-187A8D1993F4}.Release|Any CPU.ActiveCfg = Release|Any CPU {5B783B30-B422-4C2F-AC22-187A8D1993F4}.Release|Any CPU.Build.0 = Release|Any CPU + {AE6C32EE-8447-4E01-8187-2AE02BB64251}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE6C32EE-8447-4E01-8187-2AE02BB64251}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE6C32EE-8447-4E01-8187-2AE02BB64251}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE6C32EE-8447-4E01-8187-2AE02BB64251}.Release|Any CPU.Build.0 = Release|Any CPU + {BCD03521-5F8F-4775-9ADF-FA361480804F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BCD03521-5F8F-4775-9ADF-FA361480804F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BCD03521-5F8F-4775-9ADF-FA361480804F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BCD03521-5F8F-4775-9ADF-FA361480804F}.Release|Any CPU.Build.0 = Release|Any CPU + {E83633BA-FCF0-4A1A-B5BC-42000E24D437}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E83633BA-FCF0-4A1A-B5BC-42000E24D437}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E83633BA-FCF0-4A1A-B5BC-42000E24D437}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E83633BA-FCF0-4A1A-B5BC-42000E24D437}.Release|Any CPU.Build.0 = Release|Any CPU + {0603710E-E0BA-494C-AA0F-6FB0C8A8C754}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0603710E-E0BA-494C-AA0F-6FB0C8A8C754}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0603710E-E0BA-494C-AA0F-6FB0C8A8C754}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0603710E-E0BA-494C-AA0F-6FB0C8A8C754}.Release|Any CPU.Build.0 = Release|Any CPU + {005F84EB-EA2E-449F-930A-7B4173DDC7EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {005F84EB-EA2E-449F-930A-7B4173DDC7EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {005F84EB-EA2E-449F-930A-7B4173DDC7EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {005F84EB-EA2E-449F-930A-7B4173DDC7EC}.Release|Any CPU.Build.0 = Release|Any CPU + {9E886812-7243-48D8-BEAF-47AADC11C054}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9E886812-7243-48D8-BEAF-47AADC11C054}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9E886812-7243-48D8-BEAF-47AADC11C054}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9E886812-7243-48D8-BEAF-47AADC11C054}.Release|Any CPU.Build.0 = Release|Any CPU + {02ABDE42-9880-43B4-B6F7-8D618602A277}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {02ABDE42-9880-43B4-B6F7-8D618602A277}.Debug|Any CPU.Build.0 = Debug|Any CPU + {02ABDE42-9880-43B4-B6F7-8D618602A277}.Release|Any CPU.ActiveCfg = Release|Any CPU + {02ABDE42-9880-43B4-B6F7-8D618602A277}.Release|Any CPU.Build.0 = Release|Any CPU + {BDFBE455-4C1F-4FC4-B5FC-1387B93A8687}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BDFBE455-4C1F-4FC4-B5FC-1387B93A8687}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BDFBE455-4C1F-4FC4-B5FC-1387B93A8687}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BDFBE455-4C1F-4FC4-B5FC-1387B93A8687}.Release|Any CPU.Build.0 = Release|Any CPU + {B40F8584-5AFB-452C-AEFA-009C80CC23A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B40F8584-5AFB-452C-AEFA-009C80CC23A9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B40F8584-5AFB-452C-AEFA-009C80CC23A9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B40F8584-5AFB-452C-AEFA-009C80CC23A9}.Release|Any CPU.Build.0 = Release|Any CPU + {D48C1FAB-3471-4CA0-8688-25E6F43F2C25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D48C1FAB-3471-4CA0-8688-25E6F43F2C25}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D48C1FAB-3471-4CA0-8688-25E6F43F2C25}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D48C1FAB-3471-4CA0-8688-25E6F43F2C25}.Release|Any CPU.Build.0 = Release|Any CPU + {387CCF6C-9A26-43F6-A639-0A82E91E10D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {387CCF6C-9A26-43F6-A639-0A82E91E10D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {387CCF6C-9A26-43F6-A639-0A82E91E10D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {387CCF6C-9A26-43F6-A639-0A82E91E10D8}.Release|Any CPU.Build.0 = Release|Any CPU + {4CDAC1AA-45C6-4157-8D8E-199050433048}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4CDAC1AA-45C6-4157-8D8E-199050433048}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4CDAC1AA-45C6-4157-8D8E-199050433048}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4CDAC1AA-45C6-4157-8D8E-199050433048}.Release|Any CPU.Build.0 = Release|Any CPU + {9C5213D6-3833-4570-8AE2-47E9F9017A8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9C5213D6-3833-4570-8AE2-47E9F9017A8F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C5213D6-3833-4570-8AE2-47E9F9017A8F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9C5213D6-3833-4570-8AE2-47E9F9017A8F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {36447A9B-0311-4D4D-A3D5-AECBE9C15BBC} = {B5339DF7-5D1D-43BA-B332-74B825E1770E} + {6B709ED6-64C0-451D-B07F-8F49185AE191} = {B5339DF7-5D1D-43BA-B332-74B825E1770E} {5B783B30-B422-4C2F-AC22-187A8D1993F4} = {EDE05FA8-8E73-4924-BC63-DD117127EEE1} + {AE6C32EE-8447-4E01-8187-2AE02BB64251} = {EDE05FA8-8E73-4924-BC63-DD117127EEE1} + {BCD03521-5F8F-4775-9ADF-FA361480804F} = {C25EB0B0-0CAC-4CC1-8F36-F9229EFB99EC} + {E83633BA-FCF0-4A1A-B5BC-42000E24D437} = {C25EB0B0-0CAC-4CC1-8F36-F9229EFB99EC} + {0603710E-E0BA-494C-AA0F-6FB0C8A8C754} = {B5339DF7-5D1D-43BA-B332-74B825E1770E} + {005F84EB-EA2E-449F-930A-7B4173DDC7EC} = {EDE05FA8-8E73-4924-BC63-DD117127EEE1} + {9E886812-7243-48D8-BEAF-47AADC11C054} = {B5339DF7-5D1D-43BA-B332-74B825E1770E} + {02ABDE42-9880-43B4-B6F7-8D618602A277} = {B5339DF7-5D1D-43BA-B332-74B825E1770E} + {BDFBE455-4C1F-4FC4-B5FC-1387B93A8687} = {B5339DF7-5D1D-43BA-B332-74B825E1770E} + {B40F8584-5AFB-452C-AEFA-009C80CC23A9} = {EDE05FA8-8E73-4924-BC63-DD117127EEE1} + {D48C1FAB-3471-4CA0-8688-25E6F43F2C25} = {B5339DF7-5D1D-43BA-B332-74B825E1770E} + {387CCF6C-9A26-43F6-A639-0A82E91E10D8} = {EDE05FA8-8E73-4924-BC63-DD117127EEE1} + {4CDAC1AA-45C6-4157-8D8E-199050433048} = {B5339DF7-5D1D-43BA-B332-74B825E1770E} + {9C5213D6-3833-4570-8AE2-47E9F9017A8F} = {B5339DF7-5D1D-43BA-B332-74B825E1770E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BCBA19D9-F868-4C6D-8061-A2B91E06E3EC} diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 0000000000..35a8fa289c --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,29 @@ + + + + + 2015-2023 The Neo Project + 3.6.2 + 11.0 + The Neo Project + neo.png + https://github.com/neo-project/neo + MIT + README.md + git + https://github.com/neo-project/neo.git + true + snupkg + The Neo Project + true + + + + + + + + + + + diff --git a/src/IsExternalInit.cs b/src/IsExternalInit.cs new file mode 100644 index 0000000000..cf4ea93da2 --- /dev/null +++ b/src/IsExternalInit.cs @@ -0,0 +1,28 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// IsExternalInit.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. + +#if !NET5_0_OR_GREATER + +using System.ComponentModel; + +namespace System.Runtime.CompilerServices +{ + /// + /// Reserved to be used by the compiler for tracking metadata. + /// This class should not be used by developers in source code. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal static class IsExternalInit + { + } +} + +#endif diff --git a/src/Neo.CLI/CLI/CommandLineOption.cs b/src/Neo.CLI/CLI/CommandLineOption.cs new file mode 100644 index 0000000000..617856cee9 --- /dev/null +++ b/src/Neo.CLI/CLI/CommandLineOption.cs @@ -0,0 +1,36 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// CommandLineOption.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.CLI +{ + public class CommandLineOptions + { + public string? Config { get; init; } + public string? Wallet { get; init; } + public string? Password { get; init; } + public string[]? Plugins { get; set; } + public string? DBEngine { get; init; } + public string? DBPath { get; init; } + public bool? NoVerify { get; init; } + + /// + /// Check if CommandLineOptions was configured + /// + public bool IsValid => + !string.IsNullOrEmpty(Config) || + !string.IsNullOrEmpty(Wallet) || + !string.IsNullOrEmpty(Password) || + !string.IsNullOrEmpty(DBEngine) || + !string.IsNullOrEmpty(DBPath) || + (Plugins?.Length > 0) || + NoVerify is not null; + } +} diff --git a/src/Neo.CLI/CLI/ConsolePercent.cs b/src/Neo.CLI/CLI/ConsolePercent.cs new file mode 100644 index 0000000000..703ca38740 --- /dev/null +++ b/src/Neo.CLI/CLI/ConsolePercent.cs @@ -0,0 +1,146 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ConsolePercent.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; + +namespace Neo.CLI +{ + public class ConsolePercent : IDisposable + { + #region Variables + + private readonly long _maxValue; + private long _value; + private decimal _lastFactor; + private string? _lastPercent; + + private readonly int _x, _y; + + private readonly bool _inputRedirected; + + #endregion + + #region Properties + + /// + /// Value + /// + public long Value + { + get => _value; + set + { + if (value == _value) return; + + _value = Math.Min(value, _maxValue); + Invalidate(); + } + } + + /// + /// Maximum value + /// + public long MaxValue + { + get => _maxValue; + init + { + if (value == _maxValue) return; + + _maxValue = value; + + if (_value > _maxValue) + _value = _maxValue; + + Invalidate(); + } + } + + /// + /// Percent + /// + public decimal Percent + { + get + { + if (_maxValue == 0) return 0; + return (_value * 100M) / _maxValue; + } + } + + #endregion + + /// + /// Constructor + /// + /// Value + /// Maximum value + public ConsolePercent(long value = 0, long maxValue = 100) + { + _inputRedirected = Console.IsInputRedirected; + _lastFactor = -1; + _x = _inputRedirected ? 0 : Console.CursorLeft; + _y = _inputRedirected ? 0 : Console.CursorTop; + + MaxValue = maxValue; + Value = value; + Invalidate(); + } + + /// + /// Invalidate + /// + public void Invalidate() + { + var factor = Math.Round(Percent / 100M, 1); + var percent = Percent.ToString("0.0").PadLeft(5, ' '); + + if (_lastFactor == factor && _lastPercent == percent) + { + return; + } + + _lastFactor = factor; + _lastPercent = percent; + + var fill = string.Empty.PadLeft((int)(10 * factor), '■'); + var clean = string.Empty.PadLeft(10 - fill.Length, _inputRedirected ? '□' : '■'); + + if (_inputRedirected) + { + Console.WriteLine("[" + fill + clean + "] (" + percent + "%)"); + } + else + { + Console.SetCursorPosition(_x, _y); + + var prevColor = Console.ForegroundColor; + + Console.ForegroundColor = ConsoleColor.White; + Console.Write("["); + Console.ForegroundColor = Percent > 50 ? ConsoleColor.Green : ConsoleColor.DarkGreen; + Console.Write(fill); + Console.ForegroundColor = ConsoleColor.White; + Console.Write(clean + "] (" + percent + "%)"); + + Console.ForegroundColor = prevColor; + } + } + + /// + /// Free console + /// + public void Dispose() + { + Console.WriteLine(""); + } + } +} diff --git a/src/Neo.CLI/CLI/Helper.cs b/src/Neo.CLI/CLI/Helper.cs new file mode 100644 index 0000000000..e33f075539 --- /dev/null +++ b/src/Neo.CLI/CLI/Helper.cs @@ -0,0 +1,42 @@ +// 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 Neo.SmartContract.Manifest; +using System; + +namespace Neo.CLI +{ + internal static class Helper + { + public static bool IsYes(this string input) + { + if (input == null) return false; + + input = input.ToLowerInvariant(); + + return input == "yes" || input == "y"; + } + + public static string ToBase64String(this byte[] input) => System.Convert.ToBase64String(input); + + public static void IsScriptValid(this ReadOnlyMemory script, ContractAbi abi) + { + try + { + SmartContract.Helper.Check(script.ToArray(), abi); + } + catch (Exception e) + { + throw new FormatException($"Bad Script or Manifest Format: {e.Message}"); + } + } + } +} diff --git a/src/Neo.CLI/CLI/MainService.Blockchain.cs b/src/Neo.CLI/CLI/MainService.Blockchain.cs new file mode 100644 index 0000000000..4f896d63e3 --- /dev/null +++ b/src/Neo.CLI/CLI/MainService.Blockchain.cs @@ -0,0 +1,318 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MainService.Blockchain.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.ConsoleService; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using System; +using System.Linq; + +namespace Neo.CLI +{ + partial class MainService + { + /// + /// Process "export blocks" command + /// + /// Start + /// Number of blocks + /// Path + [ConsoleCommand("export blocks", Category = "Blockchain Commands")] + private void OnExportBlocksStartCountCommand(uint start, uint count = uint.MaxValue, string? path = null) + { + uint height = NativeContract.Ledger.CurrentIndex(NeoSystem.StoreView); + if (height < start) + { + ConsoleHelper.Error("invalid start height."); + return; + } + + count = Math.Min(count, height - start + 1); + + if (string.IsNullOrEmpty(path)) + { + path = $"chain.{start}.acc"; + } + + WriteBlocks(start, count, path, true); + } + + [ConsoleCommand("show block", Category = "Blockchain Commands")] + private void OnShowBlockCommand(string indexOrHash) + { + lock (syncRoot) + { + Block? block = null; + + if (uint.TryParse(indexOrHash, out var index)) + block = NativeContract.Ledger.GetBlock(NeoSystem.StoreView, index); + else if (UInt256.TryParse(indexOrHash, out var hash)) + block = NativeContract.Ledger.GetBlock(NeoSystem.StoreView, hash); + else + { + ConsoleHelper.Error("Enter a valid block index or hash."); + return; + } + + if (block is null) + { + ConsoleHelper.Error($"Block {indexOrHash} doesn't exist."); + return; + } + + DateTime blockDatetime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + blockDatetime = blockDatetime.AddMilliseconds(block.Timestamp).ToLocalTime(); + + ConsoleHelper.Info("", "-------------", "Block", "-------------"); + ConsoleHelper.Info(); + ConsoleHelper.Info("", " Timestamp: ", $"{blockDatetime}"); + ConsoleHelper.Info("", " Index: ", $"{block.Index}"); + ConsoleHelper.Info("", " Hash: ", $"{block.Hash}"); + ConsoleHelper.Info("", " Nonce: ", $"{block.Nonce}"); + ConsoleHelper.Info("", " MerkleRoot: ", $"{block.MerkleRoot}"); + ConsoleHelper.Info("", " PrevHash: ", $"{block.PrevHash}"); + ConsoleHelper.Info("", " NextConsensus: ", $"{block.NextConsensus}"); + ConsoleHelper.Info("", " PrimaryIndex: ", $"{block.PrimaryIndex}"); + ConsoleHelper.Info("", " PrimaryPubKey: ", $"{NativeContract.NEO.GetCommittee(NeoSystem.GetSnapshot())[block.PrimaryIndex]}"); + ConsoleHelper.Info("", " Version: ", $"{block.Version}"); + ConsoleHelper.Info("", " Size: ", $"{block.Size} Byte(s)"); + ConsoleHelper.Info(); + + ConsoleHelper.Info("", "-------------", "Witness", "-------------"); + ConsoleHelper.Info(); + ConsoleHelper.Info("", " Invocation Script: ", $"{Convert.ToBase64String(block.Witness.InvocationScript.Span)}"); + ConsoleHelper.Info("", " Verification Script: ", $"{Convert.ToBase64String(block.Witness.VerificationScript.Span)}"); + ConsoleHelper.Info("", " ScriptHash: ", $"{block.Witness.ScriptHash}"); + ConsoleHelper.Info("", " Size: ", $"{block.Witness.Size} Byte(s)"); + ConsoleHelper.Info(); + + ConsoleHelper.Info("", "-------------", "Transactions", "-------------"); + ConsoleHelper.Info(); + + if (block.Transactions.Length == 0) + { + ConsoleHelper.Info("", " No Transaction(s)"); + } + else + { + foreach (var tx in block.Transactions) + ConsoleHelper.Info($" {tx.Hash}"); + } + ConsoleHelper.Info(); + ConsoleHelper.Info("", "--------------------------------------"); + } + } + + [ConsoleCommand("show tx", Category = "Blockchain Commands")] + public void OnShowTransactionCommand(UInt256 hash) + { + lock (syncRoot) + { + var tx = NativeContract.Ledger.GetTransactionState(NeoSystem.StoreView, hash); + + if (tx is null) + { + ConsoleHelper.Error($"Transaction {hash} doesn't exist."); + return; + } + + var block = NativeContract.Ledger.GetHeader(NeoSystem.StoreView, tx.BlockIndex); + + DateTime transactionDatetime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + transactionDatetime = transactionDatetime.AddMilliseconds(block.Timestamp).ToLocalTime(); + + ConsoleHelper.Info("", "-------------", "Transaction", "-------------"); + ConsoleHelper.Info(); + ConsoleHelper.Info("", " Timestamp: ", $"{transactionDatetime}"); + ConsoleHelper.Info("", " Hash: ", $"{tx.Transaction.Hash}"); + ConsoleHelper.Info("", " Nonce: ", $"{tx.Transaction.Nonce}"); + ConsoleHelper.Info("", " Sender: ", $"{tx.Transaction.Sender}"); + ConsoleHelper.Info("", " ValidUntilBlock: ", $"{tx.Transaction.ValidUntilBlock}"); + ConsoleHelper.Info("", " FeePerByte: ", $"{tx.Transaction.FeePerByte}"); + ConsoleHelper.Info("", " NetworkFee: ", $"{tx.Transaction.NetworkFee}"); + ConsoleHelper.Info("", " SystemFee: ", $"{tx.Transaction.SystemFee}"); + ConsoleHelper.Info("", " Script: ", $"{Convert.ToBase64String(tx.Transaction.Script.Span)}"); + ConsoleHelper.Info("", " Version: ", $"{tx.Transaction.Version}"); + ConsoleHelper.Info("", " BlockIndex: ", $"{block.Index}"); + ConsoleHelper.Info("", " BlockHash: ", $"{block.Hash}"); + ConsoleHelper.Info("", " Size: ", $"{tx.Transaction.Size} Byte(s)"); + ConsoleHelper.Info(); + + ConsoleHelper.Info("", "-------------", "Signers", "-------------"); + ConsoleHelper.Info(); + + foreach (var signer in tx.Transaction.Signers) + { + if (signer.Rules.Length == 0) + ConsoleHelper.Info("", " Rules: ", "[]"); + else + ConsoleHelper.Info("", " Rules: ", $"[{string.Join(", ", signer.Rules.Select(s => $"\"{s.ToJson()}\""))}]"); + ConsoleHelper.Info("", " Account: ", $"{signer.Account}"); + ConsoleHelper.Info("", " Scopes: ", $"{signer.Scopes}"); + if (signer.AllowedContracts.Length == 0) + ConsoleHelper.Info("", " AllowedContracts: ", "[]"); + else + ConsoleHelper.Info("", " AllowedContracts: ", $"[{string.Join(", ", signer.AllowedContracts.Select(s => s.ToString()))}]"); + if (signer.AllowedGroups.Length == 0) + ConsoleHelper.Info("", " AllowedGroups: ", "[]"); + else + ConsoleHelper.Info("", " AllowedGroups: ", $"[{string.Join(", ", signer.AllowedGroups.Select(s => s.ToString()))}]"); + ConsoleHelper.Info("", " Size: ", $"{signer.Size} Byte(s)"); + ConsoleHelper.Info(); + } + + ConsoleHelper.Info("", "-------------", "Witnesses", "-------------"); + ConsoleHelper.Info(); + foreach (var witness in tx.Transaction.Witnesses) + { + ConsoleHelper.Info("", " InvocationScript: ", $"{Convert.ToBase64String(witness.InvocationScript.Span)}"); + ConsoleHelper.Info("", " VerificationScript: ", $"{Convert.ToBase64String(witness.VerificationScript.Span)}"); + ConsoleHelper.Info("", " ScriptHash: ", $"{witness.ScriptHash}"); + ConsoleHelper.Info("", " Size: ", $"{witness.Size} Byte(s)"); + ConsoleHelper.Info(); + } + + ConsoleHelper.Info("", "-------------", "Attributes", "-------------"); + ConsoleHelper.Info(); + if (tx.Transaction.Attributes.Length == 0) + { + ConsoleHelper.Info("", " No Attribute(s)."); + } + else + { + foreach (var attribute in tx.Transaction.Attributes) + { + switch (attribute) + { + case Conflicts c: + ConsoleHelper.Info("", " Type: ", $"{c.Type}"); + ConsoleHelper.Info("", " Hash: ", $"{c.Hash}"); + ConsoleHelper.Info("", " Size: ", $"{c.Size} Byte(s)"); + break; + case OracleResponse o: + ConsoleHelper.Info("", " Type: ", $"{o.Type}"); + ConsoleHelper.Info("", " Id: ", $"{o.Id}"); + ConsoleHelper.Info("", " Code: ", $"{o.Code}"); + ConsoleHelper.Info("", " Result: ", $"{Convert.ToBase64String(o.Result.Span)}"); + ConsoleHelper.Info("", " Size: ", $"{o.Size} Byte(s)"); + break; + case HighPriorityAttribute p: + ConsoleHelper.Info("", " Type: ", $"{p.Type}"); + break; + case NotValidBefore n: + ConsoleHelper.Info("", " Type: ", $"{n.Type}"); + ConsoleHelper.Info("", " Height: ", $"{n.Height}"); + break; + default: + ConsoleHelper.Info("", " Type: ", $"{attribute.Type}"); + ConsoleHelper.Info("", " Size: ", $"{attribute.Size} Byte(s)"); + break; + } + } + } + ConsoleHelper.Info(); + ConsoleHelper.Info("", "--------------------------------------"); + } + } + + [ConsoleCommand("show contract", Category = "Blockchain Commands")] + public void OnShowContractCommand(string nameOrHash) + { + lock (syncRoot) + { + ContractState? contract = null; + + if (UInt160.TryParse(nameOrHash, out var scriptHash)) + contract = NativeContract.ContractManagement.GetContract(NeoSystem.StoreView, scriptHash); + else + { + var nativeContract = NativeContract.Contracts.SingleOrDefault(s => s.Name.Equals(nameOrHash, StringComparison.InvariantCultureIgnoreCase)); + + if (nativeContract != null) + contract = NativeContract.ContractManagement.GetContract(NeoSystem.StoreView, nativeContract.Hash); + } + + if (contract is null) + { + ConsoleHelper.Error($"Contract {nameOrHash} doesn't exist."); + return; + } + + ConsoleHelper.Info("", "-------------", "Contract", "-------------"); + ConsoleHelper.Info(); + ConsoleHelper.Info("", " Name: ", $"{contract.Manifest.Name}"); + ConsoleHelper.Info("", " Hash: ", $"{contract.Hash}"); + ConsoleHelper.Info("", " Id: ", $"{contract.Id}"); + ConsoleHelper.Info("", " UpdateCounter: ", $"{contract.UpdateCounter}"); + ConsoleHelper.Info("", " SupportedStandards: ", $"{string.Join(" ", contract.Manifest.SupportedStandards)}"); + ConsoleHelper.Info("", " Checksum: ", $"{contract.Nef.CheckSum}"); + ConsoleHelper.Info("", " Compiler: ", $"{contract.Nef.Compiler}"); + ConsoleHelper.Info("", " SourceCode: ", $"{contract.Nef.Source}"); + ConsoleHelper.Info("", " Trusts: ", $"[{string.Join(", ", contract.Manifest.Trusts.Select(s => s.ToJson()?.GetString()))}]"); + if (contract.Manifest.Extra is not null) + { + foreach (var extra in contract.Manifest.Extra.Properties) + { + ConsoleHelper.Info("", $" {extra.Key,18}: ", $"{extra.Value?.GetString()}"); + } + } + ConsoleHelper.Info(); + + ConsoleHelper.Info("", "-------------", "Groups", "-------------"); + ConsoleHelper.Info(); + if (contract.Manifest.Groups.Length == 0) + { + ConsoleHelper.Info("", " No Group(s)."); + } + else + { + foreach (var group in contract.Manifest.Groups) + { + ConsoleHelper.Info("", " PubKey: ", $"{group.PubKey}"); + ConsoleHelper.Info("", " Signature: ", $"{Convert.ToBase64String(group.Signature)}"); + } + } + ConsoleHelper.Info(); + + ConsoleHelper.Info("", "-------------", "Permissions", "-------------"); + ConsoleHelper.Info(); + foreach (var permission in contract.Manifest.Permissions) + { + ConsoleHelper.Info("", " Contract: ", $"{permission.Contract.ToJson()?.GetString()}"); + if (permission.Methods.IsWildcard) + ConsoleHelper.Info("", " Methods: ", "*"); + else + ConsoleHelper.Info("", " Methods: ", $"{string.Join(", ", permission.Methods)}"); + ConsoleHelper.Info(); + } + + ConsoleHelper.Info("", "-------------", "Methods", "-------------"); + ConsoleHelper.Info(); + foreach (var method in contract.Manifest.Abi.Methods) + { + ConsoleHelper.Info("", " Name: ", $"{method.Name}"); + ConsoleHelper.Info("", " Safe: ", $"{method.Safe}"); + ConsoleHelper.Info("", " Offset: ", $"{method.Offset}"); + ConsoleHelper.Info("", " Parameters: ", $"[{string.Join(", ", method.Parameters.Select(s => s.Type.ToString()))}]"); + ConsoleHelper.Info("", " ReturnType: ", $"{method.ReturnType}"); + ConsoleHelper.Info(); + } + + ConsoleHelper.Info("", "-------------", "Script", "-------------"); + ConsoleHelper.Info(); + ConsoleHelper.Info($" {Convert.ToBase64String(contract.Nef.Script.Span)}"); + ConsoleHelper.Info(); + ConsoleHelper.Info("", "--------------------------------"); + } + } + } +} diff --git a/src/Neo.CLI/CLI/MainService.CommandLine.cs b/src/Neo.CLI/CLI/MainService.CommandLine.cs new file mode 100644 index 0000000000..074f252f35 --- /dev/null +++ b/src/Neo.CLI/CLI/MainService.CommandLine.cs @@ -0,0 +1,93 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MainService.CommandLine.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.Extensions.Configuration; +using System.CommandLine; +using System.CommandLine.Invocation; +using System.CommandLine.NamingConventionBinder; +using System.Reflection; + +namespace Neo.CLI +{ + public partial class MainService + { + public int OnStartWithCommandLine(string[] args) + { + RootCommand rootCommand = new(Assembly.GetExecutingAssembly().GetCustomAttribute()!.Title) + { + new Option(new[] { "-c", "--config","/config" }, "Specifies the config file."), + new Option(new[] { "-w", "--wallet","/wallet" }, "The path of the neo3 wallet [*.json]."), + new Option(new[] { "-p", "--password" ,"/password" }, "Password to decrypt the wallet, either from the command line or config file."), + new Option(new[] { "--db-engine","/db-engine" }, "Specify the db engine."), + new Option(new[] { "--db-path","/db-path" }, "Specify the db path."), + new Option(new[] { "--noverify","/noverify" }, "Indicates whether the blocks need to be verified when importing."), + new Option(new[] { "--plugins","/plugins" }, "The list of plugins, if not present, will be installed [plugin1 plugin2]."), + }; + + rootCommand.Handler = CommandHandler.Create(Handle); + return rootCommand.Invoke(args); + } + + private void Handle(RootCommand command, CommandLineOptions options, InvocationContext context) + { + Start(options); + } + + private static void CustomProtocolSettings(CommandLineOptions options, ProtocolSettings settings) + { + var tempSetting = settings; + // if specified config, then load the config and check the network + if (!string.IsNullOrEmpty(options.Config)) + { + tempSetting = ProtocolSettings.Load(options.Config); + } + + var customSetting = new ProtocolSettings + { + Network = tempSetting.Network, + AddressVersion = tempSetting.AddressVersion, + StandbyCommittee = tempSetting.StandbyCommittee, + ValidatorsCount = tempSetting.ValidatorsCount, + SeedList = tempSetting.SeedList, + MillisecondsPerBlock = tempSetting.MillisecondsPerBlock, + MaxTransactionsPerBlock = tempSetting.MaxTransactionsPerBlock, + MemoryPoolMaxTransactions = tempSetting.MemoryPoolMaxTransactions, + MaxTraceableBlocks = tempSetting.MaxTraceableBlocks, + InitialGasDistribution = tempSetting.InitialGasDistribution, + Hardforks = tempSetting.Hardforks + }; + + if (!string.IsNullOrEmpty(options.Config)) ProtocolSettings.Custom = customSetting; + } + + private static void CustomApplicationSettings(CommandLineOptions options, Settings settings) + { + var tempSetting = string.IsNullOrEmpty(options.Config) ? settings : new Settings(new ConfigurationBuilder().AddJsonFile(options.Config, optional: true).Build().GetSection("ApplicationConfiguration")); + var customSetting = new Settings + { + Logger = tempSetting.Logger, + Storage = new StorageSettings + { + Engine = options.DBEngine ?? tempSetting.Storage.Engine, + Path = options.DBPath ?? tempSetting.Storage.Path + }, + P2P = tempSetting.P2P, + UnlockWallet = new UnlockWalletSettings + { + Path = options.Wallet ?? tempSetting.UnlockWallet.Path, + Password = options.Password ?? tempSetting.UnlockWallet.Password + }, + Contracts = tempSetting.Contracts + }; + if (options.IsValid) Settings.Custom = customSetting; + } + } +} diff --git a/src/Neo.CLI/CLI/MainService.Contracts.cs b/src/Neo.CLI/CLI/MainService.Contracts.cs new file mode 100644 index 0000000000..94fbe32f89 --- /dev/null +++ b/src/Neo.CLI/CLI/MainService.Contracts.cs @@ -0,0 +1,189 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MainService.Contracts.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.ConsoleService; +using Neo.Json; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using System; +using System.Linq; +using System.Numerics; + +namespace Neo.CLI +{ + partial class MainService + { + /// + /// Process "deploy" command + /// + /// File path + /// Manifest path + /// Extra data for deploy + [ConsoleCommand("deploy", Category = "Contract Commands")] + private void OnDeployCommand(string filePath, string? manifestPath = null, JObject? data = null) + { + if (NoWallet()) return; + byte[] script = LoadDeploymentScript(filePath, manifestPath, data, out var nef, out var manifest); + Transaction tx; + try + { + tx = CurrentWallet!.MakeTransaction(NeoSystem.StoreView, script); + } + catch (InvalidOperationException e) + { + ConsoleHelper.Error(GetExceptionMessage(e)); + return; + } + UInt160 hash = SmartContract.Helper.GetContractHash(tx.Sender, nef.CheckSum, manifest.Name); + + ConsoleHelper.Info("Contract hash: ", $"{hash}"); + ConsoleHelper.Info("Gas consumed: ", $"{new BigDecimal((BigInteger)tx.SystemFee, NativeContract.GAS.Decimals)}"); + ConsoleHelper.Info("Network fee: ", $"{new BigDecimal((BigInteger)tx.NetworkFee, NativeContract.GAS.Decimals)}"); + ConsoleHelper.Info("Total fee: ", $"{new BigDecimal((BigInteger)(tx.SystemFee + tx.NetworkFee), NativeContract.GAS.Decimals)} GAS"); + if (!ConsoleHelper.ReadUserInput("Relay tx? (no|yes)").IsYes()) // Add this in case just want to get hash but not relay + { + return; + } + SignAndSendTx(NeoSystem.StoreView, tx); + } + + /// + /// Process "update" command + /// + /// Script hash + /// File path + /// Manifest path + /// Sender + /// Signer Accounts + /// Extra data for update + [ConsoleCommand("update", Category = "Contract Commands")] + private void OnUpdateCommand(UInt160 scriptHash, string filePath, string manifestPath, UInt160 sender, UInt160[]? signerAccounts = null, JObject? data = null) + { + Signer[] signers = Array.Empty(); + + if (NoWallet()) return; + if (sender != null) + { + if (signerAccounts == null) + signerAccounts = new[] { sender }; + else if (signerAccounts.Contains(sender) && signerAccounts[0] != sender) + { + var signersList = signerAccounts.ToList(); + signersList.Remove(sender); + signerAccounts = signersList.Prepend(sender).ToArray(); + } + else if (!signerAccounts.Contains(sender)) + { + signerAccounts = signerAccounts.Prepend(sender).ToArray(); + } + signers = signerAccounts.Select(p => new Signer() { Account = p, Scopes = WitnessScope.CalledByEntry }).ToArray(); + } + + Transaction tx; + try + { + byte[] script = LoadUpdateScript(scriptHash, filePath, manifestPath, data, out var nef, out var manifest); + tx = CurrentWallet!.MakeTransaction(NeoSystem.StoreView, script, sender, signers); + } + catch (InvalidOperationException e) + { + ConsoleHelper.Error(GetExceptionMessage(e)); + return; + } + ContractState contract = NativeContract.ContractManagement.GetContract(NeoSystem.StoreView, scriptHash); + if (contract == null) + { + ConsoleHelper.Warning($"Can't upgrade, contract hash not exist: {scriptHash}"); + } + else + { + ConsoleHelper.Info("Contract hash: ", $"{scriptHash}"); + ConsoleHelper.Info("Updated times: ", $"{contract.UpdateCounter}"); + ConsoleHelper.Info("Gas consumed: ", $"{new BigDecimal((BigInteger)tx.SystemFee, NativeContract.GAS.Decimals)}"); + ConsoleHelper.Info("Network fee: ", $"{new BigDecimal((BigInteger)tx.NetworkFee, NativeContract.GAS.Decimals)}"); + ConsoleHelper.Info("Total fee: ", $"{new BigDecimal((BigInteger)(tx.SystemFee + tx.NetworkFee), NativeContract.GAS.Decimals)} GAS"); + if (!ConsoleHelper.ReadUserInput("Relay tx? (no|yes)").IsYes()) // Add this in case just want to get hash but not relay + { + return; + } + SignAndSendTx(NeoSystem.StoreView, tx); + } + } + + /// + /// Process "invoke" command + /// + /// Script hash + /// Operation + /// Contract parameters + /// Transaction's sender + /// Signer's accounts + /// Max fee for running the script + [ConsoleCommand("invoke", Category = "Contract Commands")] + private void OnInvokeCommand(UInt160 scriptHash, string operation, JArray? contractParameters = null, UInt160? sender = null, UInt160[]? signerAccounts = null, decimal maxGas = 20) + { + var gas = new BigDecimal(maxGas, NativeContract.GAS.Decimals); + Signer[] signers = Array.Empty(); + if (!NoWallet()) + { + if (sender == null) + sender = CurrentWallet!.GetDefaultAccount()?.ScriptHash; + + if (sender != null) + { + if (signerAccounts == null) + signerAccounts = new UInt160[1] { sender }; + else if (signerAccounts.Contains(sender) && signerAccounts[0] != sender) + { + var signersList = signerAccounts.ToList(); + signersList.Remove(sender); + signerAccounts = signersList.Prepend(sender).ToArray(); + } + else if (!signerAccounts.Contains(sender)) + { + signerAccounts = signerAccounts.Prepend(sender).ToArray(); + } + signers = signerAccounts.Select(p => new Signer() { Account = p, Scopes = WitnessScope.CalledByEntry }).ToArray(); + } + } + + Transaction tx = new Transaction + { + Signers = signers, + Attributes = Array.Empty(), + Witnesses = Array.Empty(), + }; + + if (!OnInvokeWithResult(scriptHash, operation, out _, tx, contractParameters, gas: (long)gas.Value)) return; + + if (NoWallet()) return; + try + { + tx = CurrentWallet!.MakeTransaction(NeoSystem.StoreView, tx.Script, sender, signers, maxGas: (long)gas.Value); + } + catch (InvalidOperationException e) + { + ConsoleHelper.Error(GetExceptionMessage(e)); + return; + } + ConsoleHelper.Info("Network fee: ", + $"{new BigDecimal((BigInteger)tx.NetworkFee, NativeContract.GAS.Decimals)}\t", + "Total fee: ", + $"{new BigDecimal((BigInteger)(tx.SystemFee + tx.NetworkFee), NativeContract.GAS.Decimals)} GAS"); + if (!ConsoleHelper.ReadUserInput("Relay tx? (no|yes)").IsYes()) + { + return; + } + SignAndSendTx(NeoSystem.StoreView, tx); + } + } +} diff --git a/src/Neo.CLI/CLI/MainService.Logger.cs b/src/Neo.CLI/CLI/MainService.Logger.cs new file mode 100644 index 0000000000..80624779c7 --- /dev/null +++ b/src/Neo.CLI/CLI/MainService.Logger.cs @@ -0,0 +1,176 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MainService.Logger.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.ConsoleService; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using static System.IO.Path; + +namespace Neo.CLI +{ + partial class MainService + { + private static readonly ConsoleColorSet DebugColor = new(ConsoleColor.Cyan); + private static readonly ConsoleColorSet InfoColor = new(ConsoleColor.White); + private static readonly ConsoleColorSet WarningColor = new(ConsoleColor.Yellow); + private static readonly ConsoleColorSet ErrorColor = new(ConsoleColor.Red); + private static readonly ConsoleColorSet FatalColor = new(ConsoleColor.Red); + + private readonly object syncRoot = new(); + private bool _showLog = Settings.Default.Logger.ConsoleOutput; + + private void Initialize_Logger() + { + Utility.Logging += OnLog; + } + + private void Dispose_Logger() + { + Utility.Logging -= OnLog; + } + + /// + /// Process "console log off" command to turn off console log + /// + [ConsoleCommand("console log off", Category = "Log Commands")] + private void OnLogOffCommand() + { + _showLog = false; + } + + /// + /// Process "console log on" command to turn on the console log + /// + [ConsoleCommand("console log on", Category = "Log Commands")] + private void OnLogOnCommand() + { + _showLog = true; + } + + private static void GetErrorLogs(StringBuilder sb, Exception ex) + { + sb.AppendLine(ex.GetType().ToString()); + sb.AppendLine(ex.Message); + sb.AppendLine(ex.StackTrace); + if (ex is AggregateException ex2) + { + foreach (Exception inner in ex2.InnerExceptions) + { + sb.AppendLine(); + GetErrorLogs(sb, inner); + } + } + else if (ex.InnerException != null) + { + sb.AppendLine(); + GetErrorLogs(sb, ex.InnerException); + } + } + + private void OnLog(string source, LogLevel level, object message) + { + if (!Settings.Default.Logger.Active) + return; + + if (message is Exception ex) + { + var sb = new StringBuilder(); + GetErrorLogs(sb, ex); + message = sb.ToString(); + } + + lock (syncRoot) + { + DateTime now = DateTime.Now; + var log = $"[{now.TimeOfDay:hh\\:mm\\:ss\\.fff}]"; + if (_showLog) + { + var currentColor = new ConsoleColorSet(); + var messages = message is string msg ? Parse(msg) : new[] { message.ToString() }; + ConsoleColorSet logColor; + string logLevel; + switch (level) + { + case LogLevel.Debug: logColor = DebugColor; logLevel = "DEBUG"; break; + case LogLevel.Error: logColor = ErrorColor; logLevel = "ERROR"; break; + case LogLevel.Fatal: logColor = FatalColor; logLevel = "FATAL"; break; + case LogLevel.Info: logColor = InfoColor; logLevel = "INFO"; break; + case LogLevel.Warning: logColor = WarningColor; logLevel = "WARN"; break; + default: logColor = InfoColor; logLevel = "INFO"; break; + } + logColor.Apply(); + Console.Write($"{logLevel} {log} \t{messages[0],-20}"); + for (var i = 1; i < messages.Length; i++) + { + if (messages[i]?.Length > 20) + { + messages[i] = $"{messages[i]![..10]}...{messages[i]![(messages[i]!.Length - 10)..]}"; + } + Console.Write(i % 2 == 0 ? $"={messages[i]} " : $" {messages[i]}"); + } + currentColor.Apply(); + Console.WriteLine(); + } + + if (string.IsNullOrEmpty(Settings.Default.Logger.Path)) return; + var sb = new StringBuilder(source); + foreach (var c in GetInvalidFileNameChars()) + sb.Replace(c, '-'); + var path = Combine(Settings.Default.Logger.Path, sb.ToString()); + Directory.CreateDirectory(path); + path = Combine(path, $"{now:yyyy-MM-dd}.log"); + try + { + File.AppendAllLines(path, new[] { $"[{level}]{log} {message}" }); + } + catch (IOException) + { + Console.WriteLine("Error writing the log file: " + path); + } + } + } + + /// + /// Parse the log message + /// + /// expected format [key1 = msg1 key2 = msg2] + /// + private static string[] Parse(string message) + { + var equals = message.Trim().Split('='); + + if (equals.Length == 1) return new[] { message }; + + var messages = new List(); + foreach (var t in @equals) + { + var msg = t.Trim(); + var parts = msg.Split(' '); + var d = parts.Take(parts.Length - 1); + + if (parts.Length > 1) + { + messages.Add(string.Join(" ", d)); + } + var last = parts.LastOrDefault(); + if (last is not null) + { + messages.Add(last); + } + } + + return messages.ToArray(); + } + } +} diff --git a/src/Neo.CLI/CLI/MainService.NEP17.cs b/src/Neo.CLI/CLI/MainService.NEP17.cs new file mode 100644 index 0000000000..131d171dc6 --- /dev/null +++ b/src/Neo.CLI/CLI/MainService.NEP17.cs @@ -0,0 +1,140 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MainService.NEP17.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.ConsoleService; +using Neo.Json; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM.Types; +using Neo.Wallets; +using System; +using System.Linq; +using Array = System.Array; + +namespace Neo.CLI +{ + partial class MainService + { + /// + /// Process "transfer" command + /// + /// Script hash + /// To + /// Amount + /// From + /// Data + /// Signer's accounts + [ConsoleCommand("transfer", Category = "NEP17 Commands")] + private void OnTransferCommand(UInt160 tokenHash, UInt160 to, decimal amount, UInt160? from = null, string? data = null, UInt160[]? signersAccounts = null) + { + var snapshot = NeoSystem.StoreView; + var asset = new AssetDescriptor(snapshot, NeoSystem.Settings, tokenHash); + var value = new BigDecimal(amount, asset.Decimals); + + if (NoWallet()) return; + + Transaction tx; + try + { + tx = CurrentWallet!.MakeTransaction(snapshot, new[] + { + new TransferOutput + { + AssetId = tokenHash, + Value = value, + ScriptHash = to, + Data = data + } + }, from: from, cosigners: signersAccounts?.Select(p => new Signer + { + // default access for transfers should be valid only for first invocation + Scopes = WitnessScope.CalledByEntry, + Account = p + }) + .ToArray() ?? Array.Empty()); + } + catch (InvalidOperationException e) + { + ConsoleHelper.Error(GetExceptionMessage(e)); + return; + } + if (!ConsoleHelper.ReadUserInput("Relay tx(no|yes)").IsYes()) + { + return; + } + SignAndSendTx(snapshot, tx); + } + + /// + /// Process "balanceOf" command + /// + /// Script hash + /// Address + [ConsoleCommand("balanceOf", Category = "NEP17 Commands")] + private void OnBalanceOfCommand(UInt160 tokenHash, UInt160 address) + { + var arg = new JObject + { + ["type"] = "Hash160", + ["value"] = address.ToString() + }; + + var asset = new AssetDescriptor(NeoSystem.StoreView, NeoSystem.Settings, tokenHash); + + if (!OnInvokeWithResult(tokenHash, "balanceOf", out StackItem balanceResult, null, new JArray(arg))) return; + + var balance = new BigDecimal(((PrimitiveType)balanceResult).GetInteger(), asset.Decimals); + + Console.WriteLine(); + ConsoleHelper.Info($"{asset.AssetName} balance: ", $"{balance}"); + } + + /// + /// Process "name" command + /// + /// Script hash + [ConsoleCommand("name", Category = "NEP17 Commands")] + private void OnNameCommand(UInt160 tokenHash) + { + ContractState contract = NativeContract.ContractManagement.GetContract(NeoSystem.StoreView, tokenHash); + if (contract == null) Console.WriteLine($"Contract hash not exist: {tokenHash}"); + else ConsoleHelper.Info("Result: ", contract.Manifest.Name); + } + + /// + /// Process "decimals" command + /// + /// Script hash + [ConsoleCommand("decimals", Category = "NEP17 Commands")] + private void OnDecimalsCommand(UInt160 tokenHash) + { + if (!OnInvokeWithResult(tokenHash, "decimals", out StackItem result)) return; + + ConsoleHelper.Info("Result: ", $"{((PrimitiveType)result).GetInteger()}"); + } + + /// + /// Process "totalSupply" command + /// + /// Script hash + [ConsoleCommand("totalSupply", Category = "NEP17 Commands")] + private void OnTotalSupplyCommand(UInt160 tokenHash) + { + if (!OnInvokeWithResult(tokenHash, "totalSupply", out StackItem result)) return; + + var asset = new AssetDescriptor(NeoSystem.StoreView, NeoSystem.Settings, tokenHash); + var totalSupply = new BigDecimal(((PrimitiveType)result).GetInteger(), asset.Decimals); + + ConsoleHelper.Info("Result: ", $"{totalSupply}"); + } + } +} diff --git a/src/Neo.CLI/CLI/MainService.Native.cs b/src/Neo.CLI/CLI/MainService.Native.cs new file mode 100644 index 0000000000..b7562f3368 --- /dev/null +++ b/src/Neo.CLI/CLI/MainService.Native.cs @@ -0,0 +1,29 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MainService.Native.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.ConsoleService; +using Neo.SmartContract.Native; +using System.Linq; + +namespace Neo.CLI +{ + partial class MainService + { + /// + /// Process "list nativecontract" command + /// + [ConsoleCommand("list nativecontract", Category = "Native Contract")] + private void OnListNativeContract() + { + NativeContract.Contracts.ToList().ForEach(p => ConsoleHelper.Info($"\t{p.Name,-20}", $"{p.Hash}")); + } + } +} diff --git a/src/Neo.CLI/CLI/MainService.Network.cs b/src/Neo.CLI/CLI/MainService.Network.cs new file mode 100644 index 0000000000..38d6fd1d28 --- /dev/null +++ b/src/Neo.CLI/CLI/MainService.Network.cs @@ -0,0 +1,165 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MainService.Network.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 Akka.Actor; +using Neo.ConsoleService; +using Neo.IO; +using Neo.Json; +using Neo.Network.P2P; +using Neo.Network.P2P.Capabilities; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using System; +using System.Net; + +namespace Neo.CLI +{ + partial class MainService + { + /// + /// Process "broadcast addr" command + /// + /// Payload + /// Port + [ConsoleCommand("broadcast addr", Category = "Network Commands")] + private void OnBroadcastAddressCommand(IPAddress payload, ushort port) + { + if (payload == null) + { + ConsoleHelper.Warning("You must input the payload to relay."); + return; + } + + OnBroadcastCommand(MessageCommand.Addr, + AddrPayload.Create( + NetworkAddressWithTime.Create( + payload, DateTime.UtcNow.ToTimestamp(), + new FullNodeCapability(), + new ServerCapability(NodeCapabilityType.TcpServer, port)) + )); + } + + /// + /// Process "broadcast block" command + /// + /// Hash + [ConsoleCommand("broadcast block", Category = "Network Commands")] + private void OnBroadcastGetBlocksByHashCommand(UInt256 hash) + { + OnBroadcastCommand(MessageCommand.Block, NativeContract.Ledger.GetBlock(NeoSystem.StoreView, hash)); + } + + /// + /// Process "broadcast block" command + /// + /// Block index + [ConsoleCommand("broadcast block", Category = "Network Commands")] + private void OnBroadcastGetBlocksByHeightCommand(uint height) + { + OnBroadcastCommand(MessageCommand.Block, NativeContract.Ledger.GetBlock(NeoSystem.StoreView, height)); + } + + /// + /// Process "broadcast getblocks" command + /// + /// Hash + [ConsoleCommand("broadcast getblocks", Category = "Network Commands")] + private void OnBroadcastGetBlocksCommand(UInt256 hash) + { + OnBroadcastCommand(MessageCommand.GetBlocks, GetBlocksPayload.Create(hash)); + } + + /// + /// Process "broadcast getheaders" command + /// + /// Index + [ConsoleCommand("broadcast getheaders", Category = "Network Commands")] + private void OnBroadcastGetHeadersCommand(uint index) + { + OnBroadcastCommand(MessageCommand.GetHeaders, GetBlockByIndexPayload.Create(index)); + } + + /// + /// Process "broadcast getdata" command + /// + /// Type + /// Payload + [ConsoleCommand("broadcast getdata", Category = "Network Commands")] + private void OnBroadcastGetDataCommand(InventoryType type, UInt256[] payload) + { + OnBroadcastCommand(MessageCommand.GetData, InvPayload.Create(type, payload)); + } + + /// + /// Process "broadcast inv" command + /// + /// Type + /// Payload + [ConsoleCommand("broadcast inv", Category = "Network Commands")] + private void OnBroadcastInvCommand(InventoryType type, UInt256[] payload) + { + OnBroadcastCommand(MessageCommand.Inv, InvPayload.Create(type, payload)); + } + + /// + /// Process "broadcast transaction" command + /// + /// Hash + [ConsoleCommand("broadcast transaction", Category = "Network Commands")] + private void OnBroadcastTransactionCommand(UInt256 hash) + { + if (NeoSystem.MemPool.TryGetValue(hash, out Transaction tx)) + OnBroadcastCommand(MessageCommand.Transaction, tx); + } + + private void OnBroadcastCommand(MessageCommand command, ISerializable ret) + { + NeoSystem.LocalNode.Tell(Message.Create(command, ret)); + } + + /// + /// Process "relay" command + /// + /// Json object + [ConsoleCommand("relay", Category = "Network Commands")] + private void OnRelayCommand(JObject jsonObjectToRelay) + { + if (jsonObjectToRelay == null) + { + ConsoleHelper.Warning("You must input JSON object to relay."); + return; + } + + try + { + ContractParametersContext context = ContractParametersContext.Parse(jsonObjectToRelay.ToString(), NeoSystem.StoreView); + if (!context.Completed) + { + ConsoleHelper.Error("The signature is incomplete."); + return; + } + if (!(context.Verifiable is Transaction tx)) + { + ConsoleHelper.Warning("Only support to relay transaction."); + return; + } + tx.Witnesses = context.GetWitnesses(); + NeoSystem.Blockchain.Tell(tx); + Console.WriteLine($"Data relay success, the hash is shown as follows: {Environment.NewLine}{tx.Hash}"); + } + catch (Exception e) + { + ConsoleHelper.Error(GetExceptionMessage(e)); + } + } + } +} diff --git a/src/Neo.CLI/CLI/MainService.Node.cs b/src/Neo.CLI/CLI/MainService.Node.cs new file mode 100644 index 0000000000..5dd16f53e8 --- /dev/null +++ b/src/Neo.CLI/CLI/MainService.Node.cs @@ -0,0 +1,119 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MainService.Node.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 Akka.Actor; +using Neo.ConsoleService; +using Neo.Network.P2P; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract.Native; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Neo.CLI +{ + partial class MainService + { + /// + /// Process "show pool" command + /// + [ConsoleCommand("show pool", Category = "Node Commands", Description = "Show the current state of the mempool")] + private void OnShowPoolCommand(bool verbose = false) + { + int verifiedCount, unverifiedCount; + if (verbose) + { + NeoSystem.MemPool.GetVerifiedAndUnverifiedTransactions( + out IEnumerable verifiedTransactions, + out IEnumerable unverifiedTransactions); + ConsoleHelper.Info("Verified Transactions:"); + foreach (Transaction tx in verifiedTransactions) + Console.WriteLine($" {tx.Hash} {tx.GetType().Name} {tx.NetworkFee} GAS_NetFee"); + ConsoleHelper.Info("Unverified Transactions:"); + foreach (Transaction tx in unverifiedTransactions) + Console.WriteLine($" {tx.Hash} {tx.GetType().Name} {tx.NetworkFee} GAS_NetFee"); + + verifiedCount = verifiedTransactions.Count(); + unverifiedCount = unverifiedTransactions.Count(); + } + else + { + verifiedCount = NeoSystem.MemPool.VerifiedCount; + unverifiedCount = NeoSystem.MemPool.UnVerifiedCount; + } + Console.WriteLine($"total: {NeoSystem.MemPool.Count}, verified: {verifiedCount}, unverified: {unverifiedCount}"); + } + + /// + /// Process "show state" command + /// + [ConsoleCommand("show state", Category = "Node Commands", Description = "Show the current state of the node")] + private void OnShowStateCommand() + { + var cancel = new CancellationTokenSource(); + + Console.CursorVisible = false; + Console.Clear(); + + Task broadcast = Task.Run(async () => + { + while (!cancel.Token.IsCancellationRequested) + { + NeoSystem.LocalNode.Tell(Message.Create(MessageCommand.Ping, PingPayload.Create(NativeContract.Ledger.CurrentIndex(NeoSystem.StoreView)))); + await Task.Delay(NeoSystem.Settings.TimePerBlock, cancel.Token); + } + }); + Task task = Task.Run(async () => + { + int maxLines = 0; + while (!cancel.Token.IsCancellationRequested) + { + uint height = NativeContract.Ledger.CurrentIndex(NeoSystem.StoreView); + uint headerHeight = NeoSystem.HeaderCache.Last?.Index ?? height; + + Console.SetCursorPosition(0, 0); + WriteLineWithoutFlicker($"block: {height}/{headerHeight} connected: {LocalNode.ConnectedCount} unconnected: {LocalNode.UnconnectedCount}", Console.WindowWidth - 1); + + int linesWritten = 1; + foreach (RemoteNode node in LocalNode.GetRemoteNodes().OrderByDescending(u => u.LastBlockIndex).Take(Console.WindowHeight - 2).ToArray()) + { + ConsoleHelper.Info(" ip: ", + $"{node.Remote.Address,-15}\t", + "port: ", + $"{node.Remote.Port,-5}\t", + "listen: ", + $"{node.ListenerTcpPort,-5}\t", + "height: ", + $"{node.LastBlockIndex,-7}"); + linesWritten++; + } + + maxLines = Math.Max(maxLines, linesWritten); + + while (linesWritten < maxLines) + { + WriteLineWithoutFlicker("", Console.WindowWidth - 1); + maxLines--; + } + + await Task.Delay(500, cancel.Token); + } + }); + ReadLine(); + cancel.Cancel(); + try { Task.WaitAll(task, broadcast); } catch { } + Console.WriteLine(); + Console.CursorVisible = true; + } + } +} diff --git a/src/Neo.CLI/CLI/MainService.Plugins.cs b/src/Neo.CLI/CLI/MainService.Plugins.cs new file mode 100644 index 0000000000..5b346fc33c --- /dev/null +++ b/src/Neo.CLI/CLI/MainService.Plugins.cs @@ -0,0 +1,272 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MainService.Plugins.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 Akka.Util.Internal; +using Microsoft.Extensions.Configuration; +using Neo.ConsoleService; +using Neo.Plugins; +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Json; +using System.Reflection; +using System.Text.Json.Nodes; +using System.Threading.Tasks; + +namespace Neo.CLI +{ + partial class MainService + { + /// + /// Process "install" command + /// + /// Plugin name + [ConsoleCommand("install", Category = "Plugin Commands")] + private void OnInstallCommand(string pluginName) + { + if (PluginExists(pluginName)) + { + ConsoleHelper.Warning($"Plugin already exist."); + return; + } + + var result = InstallPluginAsync(pluginName).GetAwaiter().GetResult(); + if (result) + { + var asmName = Assembly.GetExecutingAssembly().GetName().Name; + ConsoleHelper.Info("", $"Install successful, please restart \"{asmName}\"."); + } + } + + /// + /// Force to install a plugin again. This will overwrite + /// existing plugin files, in case of any file missing or + /// damage to the old version. + /// + /// name of the plugin + [ConsoleCommand("reinstall", Category = "Plugin Commands", Description = "Overwrite existing plugin by force.")] + private void OnReinstallCommand(string pluginName) + { + var result = InstallPluginAsync(pluginName, overWrite: true).GetAwaiter().GetResult(); + if (result) + { + var asmName = Assembly.GetExecutingAssembly().GetName().Name; + ConsoleHelper.Info("", $"Reinstall successful, please restart \"{asmName}\"."); + } + } + + /// + /// Download plugin from github release + /// The function of download and install are divided + /// for the consideration of `update` command that + /// might be added in the future. + /// + /// name of the plugin + /// + /// + /// Downloaded content + private static async Task DownloadPluginAsync(string pluginName, Version pluginVersion, bool prerelease = false) + { + using var httpClient = new HttpClient(); + + var asmName = Assembly.GetExecutingAssembly().GetName(); + httpClient.DefaultRequestHeaders.UserAgent.Add(new(asmName.Name!, asmName.Version!.ToString(3))); + + var json = await httpClient.GetFromJsonAsync(Settings.Default.Plugins.DownloadUrl) ?? throw new HttpRequestException($"Failed: {Settings.Default.Plugins.DownloadUrl}"); + var jsonRelease = json.AsArray() + .SingleOrDefault(s => + s != null && + s["tag_name"]!.GetValue() == $"v{pluginVersion.ToString(3)}" && + s["prerelease"]!.GetValue() == prerelease) ?? throw new Exception($"Could not find Release {pluginVersion}"); + + var jsonAssets = jsonRelease + .AsObject() + .SingleOrDefault(s => s.Key == "assets").Value ?? throw new Exception("Could not find any Plugins"); + + var jsonPlugin = jsonAssets + .AsArray() + .SingleOrDefault(s => + Path.GetFileNameWithoutExtension( + s!["name"]!.GetValue()).Equals(pluginName, StringComparison.InvariantCultureIgnoreCase)) + ?? throw new Exception($"Could not find {pluginName}"); + + var downloadUrl = jsonPlugin["browser_download_url"]!.GetValue(); + + return await httpClient.GetStreamAsync(downloadUrl); + } + + /// + /// Install plugin from stream + /// + /// Name of the plugin + /// Dependency set + /// Install by force for `update` + private async Task InstallPluginAsync( + string pluginName, + HashSet? installed = null, + bool overWrite = false) + { + installed ??= new HashSet(); + if (!installed.Add(pluginName)) return false; + if (!overWrite && PluginExists(pluginName)) return false; + + try + { + + using var stream = await DownloadPluginAsync(pluginName, Settings.Default.Plugins.Version, Settings.Default.Plugins.Prerelease); + + using var zip = new ZipArchive(stream, ZipArchiveMode.Read); + var entry = zip.Entries.FirstOrDefault(p => p.Name == "config.json"); + if (entry is not null) + { + await using var es = entry.Open(); + await InstallDependenciesAsync(es, installed); + } + zip.ExtractToDirectory("./", true); + return true; + } + catch (Exception ex) + { + ConsoleHelper.Error(ex?.InnerException?.Message ?? ex!.Message); + } + return false; + } + + /// + /// Install the dependency of the plugin + /// + /// plugin config path in temp + /// Dependency set + private async Task InstallDependenciesAsync(Stream config, HashSet installed) + { + var dependency = new ConfigurationBuilder() + .AddJsonStream(config) + .Build() + .GetSection("Dependency"); + + if (!dependency.Exists()) return; + var dependencies = dependency.GetChildren().Select(p => p.Get()).ToArray(); + if (dependencies.Length == 0) return; + + foreach (var plugin in dependencies.Where(p => p is not null && !PluginExists(p))) + { + ConsoleHelper.Info($"Installing dependency: {plugin}"); + await InstallPluginAsync(plugin!, installed); + } + } + + /// + /// Check that the plugin has all necessary files + /// + /// Name of the plugin + /// + private static bool PluginExists(string pluginName) + { + return Plugin.Plugins.Any(p => p.Name.Equals(pluginName, StringComparison.InvariantCultureIgnoreCase)); + } + + /// + /// Process "uninstall" command + /// + /// Plugin name + [ConsoleCommand("uninstall", Category = "Plugin Commands")] + private void OnUnInstallCommand(string pluginName) + { + if (!PluginExists(pluginName)) + { + ConsoleHelper.Error("Plugin not found"); + return; + } + + foreach (var p in Plugin.Plugins) + { + try + { + using var reader = File.OpenRead($"Plugins/{p.Name}/config.json"); + if (new ConfigurationBuilder() + .AddJsonStream(reader) + .Build() + .GetSection("Dependency") + .GetChildren() + .Select(s => s.Get()) + .Any(a => a is not null && a.Equals(pluginName, StringComparison.InvariantCultureIgnoreCase))) + { + ConsoleHelper.Error($"{pluginName} is required by other plugins."); + ConsoleHelper.Info("Info: ", $"If plugin is damaged try to reinstall."); + return; + } + } + catch (Exception) + { + // ignored + } + } + try + { + Directory.Delete($"Plugins/{pluginName}", true); + } + catch (IOException) { } + ConsoleHelper.Info("", "Uninstall successful, please restart neo-cli."); + } + + /// + /// Process "plugins" command + /// + [ConsoleCommand("plugins", Category = "Plugin Commands")] + private void OnPluginsCommand() + { + try + { + var plugins = GetPluginListAsync().GetAwaiter().GetResult(); + if (plugins == null) return; + plugins + .Order() + .ForEach(f => + { + var installedPlugin = Plugin.Plugins.SingleOrDefault(pp => string.Equals(pp.Name, f, StringComparison.CurrentCultureIgnoreCase)); + if (installedPlugin != null) + { + var maxLength = plugins.Select(s => s.Length).OrderDescending().First(); + string tabs = string.Empty; + if (f.Length < maxLength) + tabs = "\t"; + ConsoleHelper.Info("", $"[Installed]\t {f,6}{tabs}", " @", $"{installedPlugin.Version.ToString(3)} {installedPlugin.Description}"); + } + else + ConsoleHelper.Info($"[Not Installed]\t {f}"); + }); + } + catch (Exception ex) + { + ConsoleHelper.Error(ex!.InnerException?.Message ?? ex!.Message); + } + } + + private async Task> GetPluginListAsync() + { + using var httpClient = new HttpClient(); + + var asmName = Assembly.GetExecutingAssembly().GetName(); + httpClient.DefaultRequestHeaders.UserAgent.Add(new(asmName.Name!, asmName.Version!.ToString(3))); + + var json = await httpClient.GetFromJsonAsync(Settings.Default.Plugins.DownloadUrl) ?? throw new HttpRequestException($"Failed: {Settings.Default.Plugins.DownloadUrl}"); + return json.AsArray() + .Where(w => + w != null && + w["tag_name"]!.GetValue() == $"v{Settings.Default.Plugins.Version.ToString(3)}") + .SelectMany(s => s!["assets"]!.AsArray()) + .Select(s => Path.GetFileNameWithoutExtension(s!["name"]!.GetValue())); + } + } +} diff --git a/src/Neo.CLI/CLI/MainService.Tools.cs b/src/Neo.CLI/CLI/MainService.Tools.cs new file mode 100644 index 0000000000..f4a5b762a9 --- /dev/null +++ b/src/Neo.CLI/CLI/MainService.Tools.cs @@ -0,0 +1,463 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MainService.Tools.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.ConsoleService; +using Neo.IO; +using Neo.Wallets; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; + +namespace Neo.CLI +{ + partial class MainService + { + /// + /// Process "parse" command + /// + [ConsoleCommand("parse", Category = "Base Commands", Description = "Parse a value to its possible conversions.")] + private void OnParseCommand(string value) + { + var parseFunctions = new Dictionary>() + { + { "Address to ScriptHash", AddressToScripthash }, + { "Address to Base64", AddressToBase64 }, + { "ScriptHash to Address", ScripthashToAddress }, + { "Base64 to Address", Base64ToAddress }, + { "Base64 to String", Base64ToString }, + { "Base64 to Big Integer", Base64ToNumber }, + { "Big Integer to Hex String", NumberToHex }, + { "Big Integer to Base64", NumberToBase64 }, + { "Hex String to String", HexToString }, + { "Hex String to Big Integer", HexToNumber }, + { "String to Hex String", StringToHex }, + { "String to Base64", StringToBase64 } + }; + + bool any = false; + + foreach (var pair in parseFunctions) + { + var parseMethod = pair.Value; + var result = parseMethod(value); + + if (result != null) + { + Console.WriteLine($"{pair.Key,-30}\t{result}"); + any = true; + } + } + + if (!any) + { + ConsoleHelper.Warning($"Was not possible to convert: '{value}'"); + } + } + + /// + /// Converts an hexadecimal value to an UTF-8 string + /// + /// + /// Hexadecimal value to be converted + /// + /// + /// Returns null when is not possible to parse the hexadecimal value to a UTF-8 + /// string or when the converted string is not printable; otherwise, returns + /// the string represented by the hexadecimal value + /// + private string? HexToString(string hexString) + { + try + { + var clearHexString = ClearHexString(hexString); + var bytes = clearHexString.HexToBytes(); + var utf8String = Utility.StrictUTF8.GetString(bytes); + return IsPrintable(utf8String) ? utf8String : null; + } + catch + { + return null; + } + } + + /// + /// Converts an hex value to a big integer + /// + /// + /// Hexadecimal value to be converted + /// + /// + /// Returns null when is not possible to parse the hex value to big integer value; + /// otherwise, returns the string that represents the converted big integer. + /// + private string? HexToNumber(string hexString) + { + try + { + var clearHexString = ClearHexString(hexString); + var bytes = clearHexString.HexToBytes(); + var number = new BigInteger(bytes); + + return number.ToString(); + } + catch + { + return null; + } + } + + /// + /// Formats a string value to a default hexadecimal representation of a byte array + /// + /// + /// The string value to be formatted + /// + /// + /// Returns the formatted string. + /// + /// + /// Throw when is the string is not a valid hex representation of a byte array. + /// + private string ClearHexString(string hexString) + { + bool hasHexPrefix = hexString.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase); + + try + { + if (hasHexPrefix) + { + hexString = hexString.Substring(2); + } + + if (hexString.Length % 2 == 1) + { + // if the length is an odd number, it cannot be parsed to a byte array + // it may be a valid hex string, so include a leading zero to parse correctly + hexString = "0" + hexString; + } + + if (hasHexPrefix) + { + // if the input value starts with '0x', the first byte is the less significant + // to parse correctly, reverse the byte array + return hexString.HexToBytes().Reverse().ToArray().ToHexString(); + } + } + catch (FormatException) + { + throw new ArgumentException(); + } + + return hexString; + } + + /// + /// Converts a string in a hexadecimal value + /// + /// + /// String value to be converted + /// + /// + /// Returns null when it is not possible to parse the string value to a hexadecimal + /// value; otherwise returns the hexadecimal value that represents the converted string + /// + private string? StringToHex(string strParam) + { + try + { + var bytesParam = Utility.StrictUTF8.GetBytes(strParam); + return bytesParam.ToHexString(); + } + catch + { + return null; + } + } + + /// + /// Converts a string in Base64 string + /// + /// + /// String value to be converted + /// + /// + /// Returns null when is not possible to parse the string value to a Base64 value; + /// otherwise returns the Base64 value that represents the converted string + /// + /// + /// Throw . + /// + private string? StringToBase64(string strParam) + { + try + { + byte[] bytearray = Utility.StrictUTF8.GetBytes(strParam); + string base64 = Convert.ToBase64String(bytearray.AsSpan()); + return base64; + } + catch + { + return null; + } + } + + /// + /// Converts a string number in hexadecimal format + /// + /// + /// String that represents the number to be converted + /// + /// + /// Returns null when the string does not represent a big integer value or when + /// it is not possible to parse the big integer value to hexadecimal; otherwise, + /// returns the string that represents the converted hexadecimal value + /// + private string? NumberToHex(string strParam) + { + try + { + if (!BigInteger.TryParse(strParam, out var numberParam)) + { + return null; + } + return numberParam.ToByteArray().ToHexString(); + } + catch + { + return null; + } + } + + /// + /// Converts a string number in Base64 byte array + /// + /// + /// String that represents the number to be converted + /// + /// + /// Returns null when the string does not represent a big integer value or when + /// it is not possible to parse the big integer value to Base64 value; otherwise, + /// returns the string that represents the converted Base64 value + /// + private string? NumberToBase64(string strParam) + { + try + { + if (!BigInteger.TryParse(strParam, out var number)) + { + return null; + } + byte[] bytearray = number.ToByteArray(); + string base64 = Convert.ToBase64String(bytearray.AsSpan()); + + return base64; + } + catch + { + return null; + } + } + + /// + /// Converts an address to its corresponding scripthash + /// + /// + /// String that represents the address to be converted + /// + /// + /// Returns null when the string does not represent an address or when + /// it is not possible to parse the address to scripthash; otherwise returns + /// the string that represents the converted scripthash + /// + private string? AddressToScripthash(string address) + { + try + { + var bigEndScript = address.ToScriptHash(NeoSystem.Settings.AddressVersion); + + return bigEndScript.ToString(); + } + catch + { + return null; + } + } + + /// + /// Converts an address to Base64 byte array + /// + /// + /// String that represents the address to be converted + /// + /// + /// Returns null when the string does not represent an address or when it is + /// not possible to parse the address to Base64 value; otherwise returns + /// the string that represents the converted Base64 value. + /// + private string? AddressToBase64(string address) + { + try + { + var script = address.ToScriptHash(NeoSystem.Settings.AddressVersion); + string base64 = Convert.ToBase64String(script.ToArray().AsSpan()); + + return base64; + } + catch + { + return null; + } + } + + /// + /// Converts a big end script hash to its equivalent address + /// + /// + /// String that represents the scripthash to be converted + /// + /// + /// Returns null when the string does not represent an scripthash; + /// otherwise, returns the string that represents the converted address + /// + private string? ScripthashToAddress(string script) + { + try + { + UInt160 scriptHash; + if (script.StartsWith("0x")) + { + if (!UInt160.TryParse(script, out scriptHash)) + { + return null; + } + } + else + { + if (!UInt160.TryParse(script, out UInt160 littleEndScript)) + { + return null; + } + string bigEndScript = littleEndScript.ToArray().ToHexString(); + if (!UInt160.TryParse(bigEndScript, out scriptHash)) + { + return null; + } + } + + var hexScript = scriptHash.ToAddress(NeoSystem.Settings.AddressVersion); + return hexScript; + } + catch + { + return null; + } + } + + /// + /// Converts an Base64 byte array to address + /// + /// + /// String that represents the Base64 value + /// + /// + /// Returns null when the string does not represent an Base64 value or when + /// it is not possible to parse the Base64 value to address; otherwise, + /// returns the string that represents the converted address + /// + private string? Base64ToAddress(string bytearray) + { + try + { + byte[] result = Convert.FromBase64String(bytearray).Reverse().ToArray(); + string hex = result.ToHexString(); + + if (!UInt160.TryParse(hex, out var scripthash)) + { + return null; + } + + string address = scripthash.ToAddress(NeoSystem.Settings.AddressVersion); + return address; + } + catch + { + return null; + } + } + + /// + /// Converts an Base64 hex string to string + /// + /// + /// String that represents the Base64 value + /// + /// + /// Returns null when the string does not represent an Base64 value or when + /// it is not possible to parse the Base64 value to string value or the converted + /// string is not printable; otherwise, returns the string that represents + /// the Base64 value. + /// + private string? Base64ToString(string bytearray) + { + try + { + byte[] result = Convert.FromBase64String(bytearray); + string utf8String = Utility.StrictUTF8.GetString(result); + return IsPrintable(utf8String) ? utf8String : null; + } + catch + { + return null; + } + } + + /// + /// Converts an Base64 hex string to big integer value + /// + /// + /// String that represents the Base64 value + /// + /// + /// Returns null when the string does not represent an Base64 value or when + /// it is not possible to parse the Base64 value to big integer value; otherwise + /// returns the string that represents the converted big integer + /// + private string? Base64ToNumber(string bytearray) + { + try + { + var bytes = Convert.FromBase64String(bytearray); + var number = new BigInteger(bytes); + return number.ToString(); + } + catch + { + return null; + } + } + + /// + /// Checks if the string is null or cannot be printed. + /// + /// + /// The string to test + /// + /// + /// Returns false if the string is null, or if it is empty, or if each character cannot be printed; + /// otherwise, returns true. + /// + private bool IsPrintable(string value) + { + return !string.IsNullOrWhiteSpace(value) && value.Any(c => !char.IsControl(c)); + } + } +} diff --git a/src/Neo.CLI/CLI/MainService.Vote.cs b/src/Neo.CLI/CLI/MainService.Vote.cs new file mode 100644 index 0000000000..12cd48b3a9 --- /dev/null +++ b/src/Neo.CLI/CLI/MainService.Vote.cs @@ -0,0 +1,246 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MainService.Vote.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.ConsoleService; +using Neo.Cryptography.ECC; +using Neo.Json; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.VM.Types; +using Neo.Wallets; +using System; +using System.Numerics; + +namespace Neo.CLI +{ + partial class MainService + { + /// + /// Process "register candidate" command + /// + /// register account scriptHash + [ConsoleCommand("register candidate", Category = "Vote Commands")] + private void OnRegisterCandidateCommand(UInt160 account) + { + var testGas = NativeContract.NEO.GetRegisterPrice(NeoSystem.StoreView) + (BigInteger)Math.Pow(10, NativeContract.GAS.Decimals) * 10; + if (NoWallet()) return; + WalletAccount currentAccount = CurrentWallet!.GetAccount(account); + + if (currentAccount == null) + { + ConsoleHelper.Warning("This address isn't in your wallet!"); + return; + } + else + { + if (currentAccount.Lock || currentAccount.WatchOnly) + { + ConsoleHelper.Warning("Locked or WatchOnly address."); + return; + } + } + + ECPoint? publicKey = currentAccount.GetKey()?.PublicKey; + byte[] script; + using (ScriptBuilder scriptBuilder = new()) + { + scriptBuilder.EmitDynamicCall(NativeContract.NEO.Hash, "registerCandidate", publicKey); + script = scriptBuilder.ToArray(); + } + + SendTransaction(script, account, (long)testGas); + } + + /// + /// Process "unregister candidate" command + /// + /// unregister account scriptHash + [ConsoleCommand("unregister candidate", Category = "Vote Commands")] + private void OnUnregisterCandidateCommand(UInt160 account) + { + if (NoWallet()) return; + WalletAccount currentAccount = CurrentWallet!.GetAccount(account); + + if (currentAccount == null) + { + ConsoleHelper.Warning("This address isn't in your wallet!"); + return; + } + else + { + if (currentAccount.Lock || currentAccount.WatchOnly) + { + ConsoleHelper.Warning("Locked or WatchOnly address."); + return; + } + } + + ECPoint? publicKey = currentAccount?.GetKey()?.PublicKey; + byte[] script; + using (ScriptBuilder scriptBuilder = new()) + { + scriptBuilder.EmitDynamicCall(NativeContract.NEO.Hash, "unregisterCandidate", publicKey); + script = scriptBuilder.ToArray(); + } + + SendTransaction(script, account); + } + + /// + /// Process "vote" command + /// + /// Sender account + /// Voting publicKey + [ConsoleCommand("vote", Category = "Vote Commands")] + private void OnVoteCommand(UInt160 senderAccount, ECPoint publicKey) + { + if (NoWallet()) return; + byte[] script; + using (ScriptBuilder scriptBuilder = new()) + { + scriptBuilder.EmitDynamicCall(NativeContract.NEO.Hash, "vote", senderAccount, publicKey); + script = scriptBuilder.ToArray(); + } + + SendTransaction(script, senderAccount); + } + + /// + /// Process "unvote" command + /// + /// Sender account + [ConsoleCommand("unvote", Category = "Vote Commands")] + private void OnUnvoteCommand(UInt160 senderAccount) + { + if (NoWallet()) return; + byte[] script; + using (ScriptBuilder scriptBuilder = new()) + { + scriptBuilder.EmitDynamicCall(NativeContract.NEO.Hash, "vote", senderAccount, null); + script = scriptBuilder.ToArray(); + } + + SendTransaction(script, senderAccount); + } + + /// + /// Process "get candidates" + /// + [ConsoleCommand("get candidates", Category = "Vote Commands")] + private void OnGetCandidatesCommand() + { + if (!OnInvokeWithResult(NativeContract.NEO.Hash, "getCandidates", out StackItem result, null, null, false)) return; + + var resJArray = (VM.Types.Array)result; + + if (resJArray.Count > 0) + { + Console.WriteLine(); + ConsoleHelper.Info("Candidates:"); + + foreach (var item in resJArray) + { + var value = (VM.Types.Array)item; + if (value is null) continue; + + Console.Write(((ByteString)value[0])?.GetSpan().ToHexString() + "\t"); + Console.WriteLine(((Integer)value[1]).GetInteger()); + } + } + } + + /// + /// Process "get committee" + /// + [ConsoleCommand("get committee", Category = "Vote Commands")] + private void OnGetCommitteeCommand() + { + if (!OnInvokeWithResult(NativeContract.NEO.Hash, "getCommittee", out StackItem result, null, null, false)) return; + + var resJArray = (VM.Types.Array)result; + + if (resJArray.Count > 0) + { + Console.WriteLine(); + ConsoleHelper.Info("Committee:"); + + foreach (var item in resJArray) + { + Console.WriteLine(((ByteString)item)?.GetSpan().ToHexString()); + } + } + } + + /// + /// Process "get next validators" + /// + [ConsoleCommand("get next validators", Category = "Vote Commands")] + private void OnGetNextBlockValidatorsCommand() + { + if (!OnInvokeWithResult(NativeContract.NEO.Hash, "getNextBlockValidators", out StackItem result, null, null, false)) return; + + var resJArray = (VM.Types.Array)result; + + if (resJArray.Count > 0) + { + Console.WriteLine(); + ConsoleHelper.Info("Next validators:"); + + foreach (var item in resJArray) + { + Console.WriteLine(((ByteString)item)?.GetSpan().ToHexString()); + } + } + } + + /// + /// Process "get accountstate" + /// + [ConsoleCommand("get accountstate", Category = "Vote Commands")] + private void OnGetAccountState(UInt160 address) + { + string notice = "No vote record!"; + var arg = new JObject + { + ["type"] = "Hash160", + ["value"] = address.ToString() + }; + + if (!OnInvokeWithResult(NativeContract.NEO.Hash, "getAccountState", out StackItem result, null, new JArray(arg))) return; + Console.WriteLine(); + if (result.IsNull) + { + ConsoleHelper.Warning(notice); + return; + } + var resJArray = (VM.Types.Array)result; + if (resJArray is null) + { + ConsoleHelper.Warning(notice); + return; + } + + foreach (StackItem value in resJArray) + { + if (value.IsNull) + { + ConsoleHelper.Warning(notice); + return; + } + } + var publickey = ECPoint.Parse(((ByteString)resJArray[2])?.GetSpan().ToHexString(), ECCurve.Secp256r1); + ConsoleHelper.Info("Voted: ", Contract.CreateSignatureRedeemScript(publickey).ToScriptHash().ToAddress(NeoSystem.Settings.AddressVersion)); + ConsoleHelper.Info("Amount: ", new BigDecimal(((Integer)resJArray[0]).GetInteger(), NativeContract.NEO.Decimals).ToString()); + ConsoleHelper.Info("Block: ", ((Integer)resJArray[1]).GetInteger().ToString()); + } + } +} diff --git a/src/Neo.CLI/CLI/MainService.Wallet.cs b/src/Neo.CLI/CLI/MainService.Wallet.cs new file mode 100644 index 0000000000..4f65390f85 --- /dev/null +++ b/src/Neo.CLI/CLI/MainService.Wallet.cs @@ -0,0 +1,749 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MainService.Wallet.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 Akka.Actor; +using Neo.ConsoleService; +using Neo.Cryptography.ECC; +using Neo.Json; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.Wallets; +using Neo.Wallets.NEP6; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Numerics; +using System.Threading.Tasks; +using static Neo.SmartContract.Helper; + +namespace Neo.CLI +{ + partial class MainService + { + /// + /// Process "open wallet" command + /// + /// Path + [ConsoleCommand("open wallet", Category = "Wallet Commands")] + private void OnOpenWallet(string path) + { + if (!File.Exists(path)) + { + ConsoleHelper.Error("File does not exist"); + return; + } + string password = ConsoleHelper.ReadUserInput("password", true); + if (password.Length == 0) + { + ConsoleHelper.Info("Cancelled"); + return; + } + try + { + OpenWallet(path, password); + } + catch (System.Security.Cryptography.CryptographicException) + { + ConsoleHelper.Error($"Failed to open file \"{path}\""); + } + } + + /// + /// Process "close wallet" command + /// + [ConsoleCommand("close wallet", Category = "Wallet Commands")] + private void OnCloseWalletCommand() + { + if (NoWallet()) return; + CurrentWallet = null; + ConsoleHelper.Info("Wallet is closed"); + } + + /// + /// Process "upgrade wallet" command + /// + [ConsoleCommand("upgrade wallet", Category = "Wallet Commands")] + private void OnUpgradeWalletCommand(string path) + { + if (Path.GetExtension(path).ToLowerInvariant() != ".db3") + { + ConsoleHelper.Warning("Can't upgrade the wallet file. Check if your wallet is in db3 format."); + return; + } + if (!File.Exists(path)) + { + ConsoleHelper.Error("File does not exist."); + return; + } + string password = ConsoleHelper.ReadUserInput("password", true); + if (password.Length == 0) + { + ConsoleHelper.Info("Cancelled"); + return; + } + string pathNew = Path.ChangeExtension(path, ".json"); + if (File.Exists(pathNew)) + { + ConsoleHelper.Warning($"File '{pathNew}' already exists"); + return; + } + NEP6Wallet.Migrate(pathNew, path, password, NeoSystem.Settings).Save(); + Console.WriteLine($"Wallet file upgrade complete. New wallet file has been auto-saved at: {pathNew}"); + } + + /// + /// Process "create address" command + /// + /// Count + [ConsoleCommand("create address", Category = "Wallet Commands")] + private void OnCreateAddressCommand(ushort count = 1) + { + if (NoWallet()) return; + string path = "address.txt"; + if (File.Exists(path)) + { + if (!ConsoleHelper.ReadUserInput($"The file '{path}' already exists, do you want to overwrite it? (yes|no)", false).IsYes()) + { + return; + } + } + + List addresses = new List(); + using (var percent = new ConsolePercent(0, count)) + { + Parallel.For(0, count, (i) => + { + WalletAccount account = CurrentWallet!.CreateAccount(); + lock (addresses) + { + addresses.Add(account.Address); + percent.Value++; + } + }); + } + + if (CurrentWallet is NEP6Wallet wallet) + wallet.Save(); + + Console.WriteLine($"Export addresses to {path}"); + File.WriteAllLines(path, addresses); + } + + /// + /// Process "delete address" command + /// + /// Address + [ConsoleCommand("delete address", Category = "Wallet Commands")] + private void OnDeleteAddressCommand(UInt160 address) + { + if (NoWallet()) return; + + if (ConsoleHelper.ReadUserInput($"Warning: Irrevocable operation!\nAre you sure to delete account {address.ToAddress(NeoSystem.Settings.AddressVersion)}? (no|yes)").IsYes()) + { + if (CurrentWallet!.DeleteAccount(address)) + { + if (CurrentWallet is NEP6Wallet wallet) + { + wallet.Save(); + } + ConsoleHelper.Info($"Address {address} deleted."); + } + else + { + ConsoleHelper.Warning($"Address {address} doesn't exist."); + } + } + } + + /// + /// Process "export key" command + /// + /// Path + /// ScriptHash + [ConsoleCommand("export key", Category = "Wallet Commands")] + private void OnExportKeyCommand(string? path = null, UInt160? scriptHash = null) + { + if (NoWallet()) return; + if (path != null && File.Exists(path)) + { + ConsoleHelper.Error($"File '{path}' already exists"); + return; + } + string password = ConsoleHelper.ReadUserInput("password", true); + if (password.Length == 0) + { + ConsoleHelper.Info("Cancelled"); + return; + } + if (!CurrentWallet!.VerifyPassword(password)) + { + ConsoleHelper.Error("Incorrect password"); + return; + } + IEnumerable keys; + if (scriptHash == null) + keys = CurrentWallet.GetAccounts().Where(p => p.HasKey).Select(p => p.GetKey()); + else + { + var account = CurrentWallet.GetAccount(scriptHash); + keys = account?.HasKey != true ? Array.Empty() : new[] { account.GetKey() }; + } + if (path == null) + foreach (KeyPair key in keys) + Console.WriteLine(key.Export()); + else + File.WriteAllLines(path, keys.Select(p => p.Export())); + } + + /// + /// Process "create wallet" command + /// + [ConsoleCommand("create wallet", Category = "Wallet Commands")] + private void OnCreateWalletCommand(string path, string? wifOrFile = null) + { + string password = ConsoleHelper.ReadUserInput("password", true); + if (password.Length == 0) + { + ConsoleHelper.Info("Cancelled"); + return; + } + string password2 = ConsoleHelper.ReadUserInput("repeat password", true); + if (password != password2) + { + ConsoleHelper.Error("Two passwords not match."); + return; + } + if (File.Exists(path)) + { + Console.WriteLine("This wallet already exists, please create another one."); + return; + } + bool createDefaultAccount = wifOrFile is null; + CreateWallet(path, password, createDefaultAccount); + if (!createDefaultAccount) OnImportKeyCommand(wifOrFile!); + } + + /// + /// Process "import multisigaddress" command + /// + /// Required signatures + /// Public keys + [ConsoleCommand("import multisigaddress", Category = "Wallet Commands")] + private void OnImportMultisigAddress(ushort m, ECPoint[] publicKeys) + { + if (NoWallet()) return; + int n = publicKeys.Length; + + if (m < 1 || m > n || n > 1024) + { + ConsoleHelper.Error("Invalid parameters."); + return; + } + + Contract multiSignContract = Contract.CreateMultiSigContract(m, publicKeys); + KeyPair? keyPair = CurrentWallet!.GetAccounts().FirstOrDefault(p => p.HasKey && publicKeys.Contains(p.GetKey().PublicKey))?.GetKey(); + + CurrentWallet.CreateAccount(multiSignContract, keyPair); + if (CurrentWallet is NEP6Wallet wallet) + wallet.Save(); + + ConsoleHelper.Info("Multisig. Addr.: ", multiSignContract.ScriptHash.ToAddress(NeoSystem.Settings.AddressVersion)); + } + + /// + /// Process "import key" command + /// + [ConsoleCommand("import key", Category = "Wallet Commands")] + private void OnImportKeyCommand(string wifOrFile) + { + if (NoWallet()) return; + byte[]? prikey = null; + try + { + prikey = Wallet.GetPrivateKeyFromWIF(wifOrFile); + } + catch (FormatException) { } + if (prikey == null) + { + var fileInfo = new FileInfo(wifOrFile); + + if (!fileInfo.Exists) + { + ConsoleHelper.Error($"File '{fileInfo.FullName}' doesn't exists"); + return; + } + + if (wifOrFile.Length > 1024 * 1024) + { + if (!ConsoleHelper.ReadUserInput($"The file '{fileInfo.FullName}' is too big, do you want to continue? (yes|no)", false).IsYes()) + { + return; + } + } + + string[] lines = File.ReadAllLines(fileInfo.FullName).Where(u => !string.IsNullOrEmpty(u)).ToArray(); + using (var percent = new ConsolePercent(0, lines.Length)) + { + for (int i = 0; i < lines.Length; i++) + { + if (lines[i].Length == 64) + prikey = lines[i].HexToBytes(); + else + prikey = Wallet.GetPrivateKeyFromWIF(lines[i]); + CurrentWallet!.CreateAccount(prikey); + Array.Clear(prikey, 0, prikey.Length); + percent.Value++; + } + } + } + else + { + WalletAccount account = CurrentWallet!.CreateAccount(prikey); + Array.Clear(prikey, 0, prikey.Length); + ConsoleHelper.Info("Address: ", account.Address); + ConsoleHelper.Info(" Pubkey: ", account.GetKey().PublicKey.EncodePoint(true).ToHexString()); + } + if (CurrentWallet is NEP6Wallet wallet) + wallet.Save(); + } + + /// + /// Process "import watchonly" command + /// + [ConsoleCommand("import watchonly", Category = "Wallet Commands")] + private void OnImportWatchOnlyCommand(string addressOrFile) + { + if (NoWallet()) return; + UInt160? address = null; + try + { + address = StringToAddress(addressOrFile, NeoSystem.Settings.AddressVersion); + } + catch (FormatException) { } + if (address is null) + { + var fileInfo = new FileInfo(addressOrFile); + + if (!fileInfo.Exists) + { + ConsoleHelper.Warning($"File '{fileInfo.FullName}' doesn't exists"); + return; + } + + if (fileInfo.Length > 1024 * 1024) + { + if (!ConsoleHelper.ReadUserInput($"The file '{fileInfo.FullName}' is too big, do you want to continue? (yes|no)", false).IsYes()) + { + return; + } + } + + string[] lines = File.ReadAllLines(fileInfo.FullName).Where(u => !string.IsNullOrEmpty(u)).ToArray(); + using (var percent = new ConsolePercent(0, lines.Length)) + { + for (int i = 0; i < lines.Length; i++) + { + address = StringToAddress(lines[i], NeoSystem.Settings.AddressVersion); + CurrentWallet!.CreateAccount(address); + percent.Value++; + } + } + } + else + { + WalletAccount account = CurrentWallet!.GetAccount(address); + if (account is not null) + { + ConsoleHelper.Warning("This address is already in your wallet"); + } + else + { + account = CurrentWallet.CreateAccount(address); + ConsoleHelper.Info("Address: ", account.Address); + } + } + if (CurrentWallet is NEP6Wallet wallet) + wallet.Save(); + } + + /// + /// Process "list address" command + /// + [ConsoleCommand("list address", Category = "Wallet Commands")] + private void OnListAddressCommand() + { + if (NoWallet()) return; + var snapshot = NeoSystem.StoreView; + foreach (var account in CurrentWallet!.GetAccounts()) + { + var contract = account.Contract; + var type = "Nonstandard"; + + if (account.WatchOnly) + { + type = "WatchOnly"; + } + else if (IsMultiSigContract(contract.Script)) + { + type = "MultiSignature"; + } + else if (IsSignatureContract(contract.Script)) + { + type = "Standard"; + } + else if (NativeContract.ContractManagement.GetContract(snapshot, account.ScriptHash) != null) + { + type = "Deployed-Nonstandard"; + } + + ConsoleHelper.Info(" Address: ", $"{account.Address}\t{type}"); + ConsoleHelper.Info("ScriptHash: ", $"{account.ScriptHash}\n"); + } + } + + /// + /// Process "list asset" command + /// + [ConsoleCommand("list asset", Category = "Wallet Commands")] + private void OnListAssetCommand() + { + var snapshot = NeoSystem.StoreView; + if (NoWallet()) return; + foreach (UInt160 account in CurrentWallet!.GetAccounts().Select(p => p.ScriptHash)) + { + Console.WriteLine(account.ToAddress(NeoSystem.Settings.AddressVersion)); + ConsoleHelper.Info("NEO: ", $"{CurrentWallet.GetBalance(snapshot, NativeContract.NEO.Hash, account)}"); + ConsoleHelper.Info("GAS: ", $"{CurrentWallet.GetBalance(snapshot, NativeContract.GAS.Hash, account)}"); + Console.WriteLine(); + } + Console.WriteLine("----------------------------------------------------"); + ConsoleHelper.Info("Total: NEO: ", $"{CurrentWallet.GetAvailable(snapshot, NativeContract.NEO.Hash),10} ", "GAS: ", $"{CurrentWallet.GetAvailable(snapshot, NativeContract.GAS.Hash),18}"); + Console.WriteLine(); + ConsoleHelper.Info("NEO hash: ", NativeContract.NEO.Hash.ToString()); + ConsoleHelper.Info("GAS hash: ", NativeContract.GAS.Hash.ToString()); + } + + /// + /// Process "list key" command + /// + [ConsoleCommand("list key", Category = "Wallet Commands")] + private void OnListKeyCommand() + { + if (NoWallet()) return; + foreach (WalletAccount account in CurrentWallet!.GetAccounts().Where(p => p.HasKey)) + { + ConsoleHelper.Info(" Address: ", account.Address); + ConsoleHelper.Info("ScriptHash: ", account.ScriptHash.ToString()); + ConsoleHelper.Info(" PublicKey: ", account.GetKey().PublicKey.EncodePoint(true).ToHexString()); + Console.WriteLine(); + } + } + + /// + /// Process "sign" command + /// + /// Json object to sign + [ConsoleCommand("sign", Category = "Wallet Commands")] + private void OnSignCommand(JObject jsonObjectToSign) + { + if (NoWallet()) return; + + if (jsonObjectToSign == null) + { + ConsoleHelper.Warning("You must input JSON object pending signature data."); + return; + } + try + { + var snapshot = NeoSystem.StoreView; + ContractParametersContext context = ContractParametersContext.Parse(jsonObjectToSign.ToString(), snapshot); + if (context.Network != NeoSystem.Settings.Network) + { + ConsoleHelper.Warning("Network mismatch."); + return; + } + else if (!CurrentWallet!.Sign(context)) + { + ConsoleHelper.Warning("Non-existent private key in wallet."); + return; + } + ConsoleHelper.Info("Signed Output: ", $"{Environment.NewLine}{context}"); + } + catch (Exception e) + { + ConsoleHelper.Error(GetExceptionMessage(e)); + } + } + + /// + /// Process "send" command + /// + /// Asset id + /// To + /// Amount + /// From + /// Data + /// Signer's accounts + [ConsoleCommand("send", Category = "Wallet Commands")] + private void OnSendCommand(UInt160 asset, UInt160 to, string amount, UInt160? from = null, string? data = null, UInt160[]? signerAccounts = null) + { + if (NoWallet()) return; + string password = ConsoleHelper.ReadUserInput("password", true); + if (password.Length == 0) + { + ConsoleHelper.Info("Cancelled"); + return; + } + if (!CurrentWallet!.VerifyPassword(password)) + { + ConsoleHelper.Error("Incorrect password"); + return; + } + + var snapshot = NeoSystem.StoreView; + Transaction tx; + AssetDescriptor descriptor = new(snapshot, NeoSystem.Settings, asset); + if (!BigDecimal.TryParse(amount, descriptor.Decimals, out BigDecimal decimalAmount) || decimalAmount.Sign <= 0) + { + ConsoleHelper.Error("Incorrect Amount Format"); + return; + } + try + { + tx = CurrentWallet.MakeTransaction(snapshot, new[] + { + new TransferOutput + { + AssetId = asset, + Value = decimalAmount, + ScriptHash = to, + Data = data + } + }, from: from, cosigners: signerAccounts?.Select(p => new Signer + { + // default access for transfers should be valid only for first invocation + Scopes = WitnessScope.CalledByEntry, + Account = p + }) + .ToArray() ?? Array.Empty()); + } + catch (Exception e) + { + ConsoleHelper.Error(GetExceptionMessage(e)); + return; + } + + if (tx == null) + { + ConsoleHelper.Warning("Insufficient funds"); + return; + } + + ConsoleHelper.Info( + "Send To: ", $"{to.ToAddress(NeoSystem.Settings.AddressVersion)}\n", + "Network fee: ", $"{new BigDecimal((BigInteger)tx.NetworkFee, NativeContract.GAS.Decimals)}\t", + "Total fee: ", $"{new BigDecimal((BigInteger)(tx.SystemFee + tx.NetworkFee), NativeContract.GAS.Decimals)} GAS"); + if (!ConsoleHelper.ReadUserInput("Relay tx? (no|yes)").IsYes()) + { + return; + } + SignAndSendTx(NeoSystem.StoreView, tx); + } + + /// + /// Process "cancel" command + /// + /// conflict txid + /// Transaction's sender + /// Signer's accounts + [ConsoleCommand("cancel", Category = "Wallet Commands")] + private void OnCancelCommand(UInt256 txid, UInt160? sender = null, UInt160[]? signerAccounts = null) + { + if (NoWallet()) return; + + TransactionState state = NativeContract.Ledger.GetTransactionState(NeoSystem.StoreView, txid); + if (state != null) + { + ConsoleHelper.Error("This tx is already confirmed, can't be cancelled."); + return; + } + + var conflict = new TransactionAttribute[] { new Conflicts() { Hash = txid } }; + Signer[] signers = Array.Empty(); + if (sender != null) + { + if (signerAccounts == null) + signerAccounts = new UInt160[1] { sender }; + else if (signerAccounts.Contains(sender) && signerAccounts[0] != sender) + { + var signersList = signerAccounts.ToList(); + signersList.Remove(sender); + signerAccounts = signersList.Prepend(sender).ToArray(); + } + else if (!signerAccounts.Contains(sender)) + { + signerAccounts = signerAccounts.Prepend(sender).ToArray(); + } + signers = signerAccounts.Select(p => new Signer() { Account = p, Scopes = WitnessScope.None }).ToArray(); + } + + Transaction tx = new() + { + Signers = signers, + Attributes = conflict, + Witnesses = Array.Empty(), + }; + + try + { + using ScriptBuilder scriptBuilder = new(); + scriptBuilder.Emit(OpCode.RET); + tx = CurrentWallet!.MakeTransaction(NeoSystem.StoreView, scriptBuilder.ToArray(), sender, signers, conflict); + } + catch (InvalidOperationException e) + { + ConsoleHelper.Error(GetExceptionMessage(e)); + return; + } + + if (NeoSystem.MemPool.TryGetValue(txid, out Transaction conflictTx)) + { + tx.NetworkFee = Math.Max(tx.NetworkFee, conflictTx.NetworkFee) + 1; + } + else + { + var snapshot = NeoSystem.StoreView; + AssetDescriptor descriptor = new(snapshot, NeoSystem.Settings, NativeContract.GAS.Hash); + string extracFee = ConsoleHelper.ReadUserInput("This tx is not in mempool, please input extra fee manually"); + if (!BigDecimal.TryParse(extracFee, descriptor.Decimals, out BigDecimal decimalExtraFee) || decimalExtraFee.Sign <= 0) + { + ConsoleHelper.Error("Incorrect Amount Format"); + return; + } + tx.NetworkFee += (long)decimalExtraFee.Value; + }; + + ConsoleHelper.Info("Network fee: ", + $"{new BigDecimal((BigInteger)tx.NetworkFee, NativeContract.GAS.Decimals)}\t", + "Total fee: ", + $"{new BigDecimal((BigInteger)(tx.SystemFee + tx.NetworkFee), NativeContract.GAS.Decimals)} GAS"); + if (!ConsoleHelper.ReadUserInput("Relay tx? (no|yes)").IsYes()) + { + return; + } + SignAndSendTx(NeoSystem.StoreView, tx); + } + + /// + /// Process "show gas" command + /// + [ConsoleCommand("show gas", Category = "Wallet Commands")] + private void OnShowGasCommand() + { + if (NoWallet()) return; + BigInteger gas = BigInteger.Zero; + var snapshot = NeoSystem.StoreView; + uint height = NativeContract.Ledger.CurrentIndex(snapshot) + 1; + foreach (UInt160 account in CurrentWallet!.GetAccounts().Select(p => p.ScriptHash)) + gas += NativeContract.NEO.UnclaimedGas(snapshot, account, height); + ConsoleHelper.Info("Unclaimed gas: ", new BigDecimal(gas, NativeContract.GAS.Decimals).ToString()); + } + + /// + /// Process "change password" command + /// + [ConsoleCommand("change password", Category = "Wallet Commands")] + private void OnChangePasswordCommand() + { + if (NoWallet()) return; + string oldPassword = ConsoleHelper.ReadUserInput("password", true); + if (oldPassword.Length == 0) + { + ConsoleHelper.Info("Cancelled"); + return; + } + if (!CurrentWallet!.VerifyPassword(oldPassword)) + { + ConsoleHelper.Error("Incorrect password"); + return; + } + string newPassword = ConsoleHelper.ReadUserInput("New password", true); + string newPasswordReEntered = ConsoleHelper.ReadUserInput("Re-Enter Password", true); + if (!newPassword.Equals(newPasswordReEntered)) + { + ConsoleHelper.Error("Two passwords entered are inconsistent!"); + return; + } + + if (CurrentWallet is NEP6Wallet wallet) + { + string backupFile = wallet.Path + ".bak"; + if (!File.Exists(wallet.Path) || File.Exists(backupFile)) + { + ConsoleHelper.Error("Wallet backup fail"); + return; + } + try + { + File.Copy(wallet.Path, backupFile); + } + catch (IOException) + { + ConsoleHelper.Error("Wallet backup fail"); + return; + } + } + + bool succeed = CurrentWallet.ChangePassword(oldPassword, newPassword); + if (succeed) + { + if (CurrentWallet is NEP6Wallet nep6Wallet) + nep6Wallet.Save(); + Console.WriteLine("Password changed successfully"); + } + else + { + ConsoleHelper.Error("Failed to change password"); + } + } + + private void SignAndSendTx(DataCache snapshot, Transaction tx) + { + if (NoWallet()) return; + + ContractParametersContext context; + try + { + context = new ContractParametersContext(snapshot, tx, NeoSystem.Settings.Network); + } + catch (InvalidOperationException e) + { + ConsoleHelper.Error("Failed creating contract params: " + GetExceptionMessage(e)); + throw; + } + CurrentWallet!.Sign(context); + if (context.Completed) + { + tx.Witnesses = context.GetWitnesses(); + NeoSystem.Blockchain.Tell(tx); + ConsoleHelper.Info("Signed and relayed transaction with hash:\n", $"{tx.Hash}"); + } + else + { + ConsoleHelper.Info("Incomplete signature:\n", $"{context}"); + } + } + } +} diff --git a/src/Neo.CLI/CLI/MainService.cs b/src/Neo.CLI/CLI/MainService.cs new file mode 100644 index 0000000000..b8d52a6a02 --- /dev/null +++ b/src/Neo.CLI/CLI/MainService.cs @@ -0,0 +1,686 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MainService.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 Akka.Actor; +using Neo.ConsoleService; +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.Json; +using Neo.Ledger; +using Neo.Network.P2P; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using Neo.Plugins; +using Neo.SmartContract; +using Neo.SmartContract.Manifest; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.VM.Types; +using Neo.Wallets; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Net; +using System.Numerics; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using Array = System.Array; + +namespace Neo.CLI +{ + public partial class MainService : ConsoleServiceBase, IWalletProvider + { + public event EventHandler? WalletChanged = null; + + public const long TestModeGas = 20_00000000; + + private Wallet? _currentWallet; + + public Wallet? CurrentWallet + { + get => _currentWallet; + private set + { + _currentWallet = value; + WalletChanged?.Invoke(this, value); + } + } + + private NeoSystem? _neoSystem; + public NeoSystem NeoSystem + { + get => _neoSystem!; + private set => _neoSystem = value; + } + + private LocalNode? _localNode; + + public LocalNode LocalNode + { + get => _localNode!; + private set => _localNode = value; + } + + protected override string Prompt => "neo"; + public override string ServiceName => "NEO-CLI"; + + /// + /// Constructor + /// + public MainService() : base() + { + RegisterCommandHandler(false, str => StringToAddress(str, NeoSystem.Settings.AddressVersion)); + RegisterCommandHandler(false, UInt256.Parse); + RegisterCommandHandler(str => str.Select(u => UInt256.Parse(u.Trim())).ToArray()); + RegisterCommandHandler(arr => arr.Select(str => StringToAddress(str, NeoSystem.Settings.AddressVersion)).ToArray()); + RegisterCommandHandler(str => ECPoint.Parse(str.Trim(), ECCurve.Secp256r1)); + RegisterCommandHandler(str => str.Select(u => ECPoint.Parse(u.Trim(), ECCurve.Secp256r1)).ToArray()); + RegisterCommandHandler(str => JToken.Parse(str)!); + RegisterCommandHandler(str => (JObject)JToken.Parse(str)!); + RegisterCommandHandler(str => decimal.Parse(str, CultureInfo.InvariantCulture)); + RegisterCommandHandler(obj => (JArray)obj); + + RegisterCommand(this); + + Initialize_Logger(); + } + + internal UInt160 StringToAddress(string input, byte version) + { + switch (input.ToLowerInvariant()) + { + case "neo": return NativeContract.NEO.Hash; + case "gas": return NativeContract.GAS.Hash; + } + + if (input.IndexOf('.') > 0 && input.LastIndexOf('.') < input.Length) + { + return ResolveNeoNameServiceAddress(input); + } + + // Try to parse as UInt160 + + if (UInt160.TryParse(input, out var addr)) + { + return addr; + } + + // Accept wallet format + + return input.ToScriptHash(version); + } + + Wallet? IWalletProvider.GetWallet() + { + return CurrentWallet; + } + + public override void RunConsole() + { + Console.ForegroundColor = ConsoleColor.DarkGreen; + + var cliV = Assembly.GetAssembly(typeof(Program))!.GetVersion(); + var neoV = Assembly.GetAssembly(typeof(NeoSystem))!.GetVersion(); + var vmV = Assembly.GetAssembly(typeof(ExecutionEngine))!.GetVersion(); + Console.WriteLine($"{ServiceName} v{cliV} - NEO v{neoV} - NEO-VM v{vmV}"); + Console.WriteLine(); + + base.RunConsole(); + } + + public void CreateWallet(string path, string password, bool createDefaultAccount = true) + { + Wallet wallet = Wallet.Create(null, path, password, NeoSystem.Settings); + if (wallet == null) + { + ConsoleHelper.Warning("Wallet files in that format are not supported, please use a .json or .db3 file extension."); + return; + } + if (createDefaultAccount) + { + WalletAccount account = wallet.CreateAccount(); + ConsoleHelper.Info(" Address: ", account.Address); + ConsoleHelper.Info(" Pubkey: ", account.GetKey().PublicKey.EncodePoint(true).ToHexString()); + ConsoleHelper.Info("ScriptHash: ", $"{account.ScriptHash}"); + } + wallet.Save(); + CurrentWallet = wallet; + } + + private IEnumerable GetBlocks(Stream stream, bool read_start = false) + { + using BinaryReader r = new BinaryReader(stream); + uint start = read_start ? r.ReadUInt32() : 0; + uint count = r.ReadUInt32(); + uint end = start + count - 1; + uint currentHeight = NativeContract.Ledger.CurrentIndex(NeoSystem.StoreView); + if (end <= currentHeight) yield break; + for (uint height = start; height <= end; height++) + { + var size = r.ReadInt32(); + if (size > Message.PayloadMaxSize) + throw new ArgumentException($"Block {height} exceeds the maximum allowed size"); + + byte[] array = r.ReadBytes(size); + if (height > currentHeight) + { + Block block = array.AsSerializable(); + yield return block; + } + } + } + + private IEnumerable GetBlocksFromFile() + { + const string pathAcc = "chain.acc"; + if (File.Exists(pathAcc)) + using (FileStream fs = new(pathAcc, FileMode.Open, FileAccess.Read, FileShare.Read)) + foreach (var block in GetBlocks(fs)) + yield return block; + + const string pathAccZip = pathAcc + ".zip"; + if (File.Exists(pathAccZip)) + using (FileStream fs = new(pathAccZip, FileMode.Open, FileAccess.Read, FileShare.Read)) + using (ZipArchive zip = new(fs, ZipArchiveMode.Read)) + using (Stream? zs = zip.GetEntry(pathAcc)?.Open()) + { + if (zs is not null) + { + foreach (var block in GetBlocks(zs)) + yield return block; + } + } + + var paths = Directory.EnumerateFiles(".", "chain.*.acc", SearchOption.TopDirectoryOnly).Concat(Directory.EnumerateFiles(".", "chain.*.acc.zip", SearchOption.TopDirectoryOnly)).Select(p => new + { + FileName = Path.GetFileName(p), + Start = uint.Parse(Regex.Match(p, @"\d+").Value), + IsCompressed = p.EndsWith(".zip") + }).OrderBy(p => p.Start); + + uint height = NativeContract.Ledger.CurrentIndex(NeoSystem.StoreView); + foreach (var path in paths) + { + if (path.Start > height + 1) break; + if (path.IsCompressed) + using (FileStream fs = new(path.FileName, FileMode.Open, FileAccess.Read, FileShare.Read)) + using (ZipArchive zip = new(fs, ZipArchiveMode.Read)) + using (Stream? zs = zip.GetEntry(Path.GetFileNameWithoutExtension(path.FileName))?.Open()) + { + if (zs is not null) + { + foreach (var block in GetBlocks(zs, true)) + yield return block; + } + } + else + using (FileStream fs = new(path.FileName, FileMode.Open, FileAccess.Read, FileShare.Read)) + foreach (var block in GetBlocks(fs, true)) + yield return block; + } + } + + private bool NoWallet() + { + if (CurrentWallet != null) return false; + ConsoleHelper.Error("You have to open the wallet first."); + return true; + } + + private byte[] LoadDeploymentScript(string nefFilePath, string? manifestFilePath, JObject? data, out NefFile nef, out ContractManifest manifest) + { + if (string.IsNullOrEmpty(manifestFilePath)) + { + manifestFilePath = Path.ChangeExtension(nefFilePath, ".manifest.json"); + } + + // Read manifest + + var info = new FileInfo(manifestFilePath); + if (!info.Exists || info.Length >= Transaction.MaxTransactionSize) + { + throw new ArgumentException(nameof(manifestFilePath)); + } + + manifest = ContractManifest.Parse(File.ReadAllBytes(manifestFilePath)); + + // Read nef + + info = new FileInfo(nefFilePath); + if (!info.Exists || info.Length >= Transaction.MaxTransactionSize) + { + throw new ArgumentException(nameof(nefFilePath)); + } + + nef = File.ReadAllBytes(nefFilePath).AsSerializable(); + + ContractParameter? dataParameter = null; + if (data is not null) + try + { + dataParameter = ContractParameter.FromJson(data); + } + catch + { + throw new FormatException("invalid data"); + } + + // Basic script checks + nef.Script.IsScriptValid(manifest.Abi); + + // Build script + + using (ScriptBuilder sb = new ScriptBuilder()) + { + if (dataParameter is not null) + sb.EmitDynamicCall(NativeContract.ContractManagement.Hash, "deploy", nef.ToArray(), manifest.ToJson().ToString(), dataParameter); + else + sb.EmitDynamicCall(NativeContract.ContractManagement.Hash, "deploy", nef.ToArray(), manifest.ToJson().ToString()); + return sb.ToArray(); + } + } + + private byte[] LoadUpdateScript(UInt160 scriptHash, string nefFilePath, string manifestFilePath, JObject? data, out NefFile nef, out ContractManifest manifest) + { + if (string.IsNullOrEmpty(manifestFilePath)) + { + manifestFilePath = Path.ChangeExtension(nefFilePath, ".manifest.json"); + } + + // Read manifest + + var info = new FileInfo(manifestFilePath); + if (!info.Exists || info.Length >= Transaction.MaxTransactionSize) + { + throw new ArgumentException(nameof(manifestFilePath)); + } + + manifest = ContractManifest.Parse(File.ReadAllBytes(manifestFilePath)); + + // Read nef + + info = new FileInfo(nefFilePath); + if (!info.Exists || info.Length >= Transaction.MaxTransactionSize) + { + throw new ArgumentException(nameof(nefFilePath)); + } + + nef = File.ReadAllBytes(nefFilePath).AsSerializable(); + + ContractParameter? dataParameter = null; + if (data is not null) + try + { + dataParameter = ContractParameter.FromJson(data); + } + catch + { + throw new FormatException("invalid data"); + } + + // Basic script checks + nef.Script.IsScriptValid(manifest.Abi); + + // Build script + + using (ScriptBuilder sb = new ScriptBuilder()) + { + if (dataParameter is null) + sb.EmitDynamicCall(scriptHash, "update", nef.ToArray(), manifest.ToJson().ToString()); + else + sb.EmitDynamicCall(scriptHash, "update", nef.ToArray(), manifest.ToJson().ToString(), dataParameter); + return sb.ToArray(); + } + } + + public override bool OnStart(string[] args) + { + if (!base.OnStart(args)) return false; + return OnStartWithCommandLine(args) != 1; + } + + public override void OnStop() + { + base.OnStop(); + Stop(); + } + + public void OpenWallet(string path, string password) + { + if (!File.Exists(path)) + { + throw new FileNotFoundException(); + } + + CurrentWallet = Wallet.Open(path, password, NeoSystem.Settings) ?? throw new NotSupportedException(); + } + + public async void Start(CommandLineOptions options) + { + if (NeoSystem != null) return; + bool verifyImport = !(options.NoVerify ?? false); + + ProtocolSettings protocol = ProtocolSettings.Load("config.json"); + CustomProtocolSettings(options, protocol); + CustomApplicationSettings(options, Settings.Default); + NeoSystem = new NeoSystem(protocol, Settings.Default.Storage.Engine, string.Format(Settings.Default.Storage.Path, protocol.Network.ToString("X8"))); + NeoSystem.AddService(this); + + LocalNode = NeoSystem.LocalNode.Ask(new LocalNode.GetInstance()).Result; + + // installing plugins + var installTasks = options.Plugins?.Select(p => p).Where(p => !string.IsNullOrEmpty(p)).ToList().Select(p => InstallPluginAsync(p)); + if (installTasks is not null) + { + await Task.WhenAll(installTasks); + } + foreach (var plugin in Plugin.Plugins) + { + // Register plugins commands + + RegisterCommand(plugin, plugin.Name); + } + + using (IEnumerator blocksBeingImported = GetBlocksFromFile().GetEnumerator()) + { + while (true) + { + List blocksToImport = new List(); + for (int i = 0; i < 10; i++) + { + if (!blocksBeingImported.MoveNext()) break; + blocksToImport.Add(blocksBeingImported.Current); + } + if (blocksToImport.Count == 0) break; + await NeoSystem.Blockchain.Ask(new Blockchain.Import + { + Blocks = blocksToImport, + Verify = verifyImport + }); + if (NeoSystem is null) return; + } + } + NeoSystem.StartNode(new ChannelsConfig + { + Tcp = new IPEndPoint(IPAddress.Any, Settings.Default.P2P.Port), + MinDesiredConnections = Settings.Default.P2P.MinDesiredConnections, + MaxConnections = Settings.Default.P2P.MaxConnections, + MaxConnectionsPerAddress = Settings.Default.P2P.MaxConnectionsPerAddress + }); + + if (Settings.Default.UnlockWallet.IsActive) + { + try + { + if (Settings.Default.UnlockWallet.Path is null) + { + throw new InvalidOperationException("UnlockWallet.Path must be defined"); + } + else if (Settings.Default.UnlockWallet.Password is null) + { + throw new InvalidOperationException("UnlockWallet.Password must be defined"); + } + + OpenWallet(Settings.Default.UnlockWallet.Path, Settings.Default.UnlockWallet.Password); + } + catch (FileNotFoundException) + { + ConsoleHelper.Warning($"wallet file \"{Settings.Default.UnlockWallet.Path}\" not found."); + } + catch (System.Security.Cryptography.CryptographicException) + { + ConsoleHelper.Error($"Failed to open file \"{Settings.Default.UnlockWallet.Path}\""); + } + catch (Exception ex) + { + ConsoleHelper.Error(ex.GetBaseException().Message); + } + } + } + + public void Stop() + { + Dispose_Logger(); + Interlocked.Exchange(ref _neoSystem, null)?.Dispose(); + } + + private void WriteBlocks(uint start, uint count, string path, bool writeStart) + { + uint end = start + count - 1; + using FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.WriteThrough); + if (fs.Length > 0) + { + byte[] buffer = new byte[sizeof(uint)]; + if (writeStart) + { + fs.Seek(sizeof(uint), SeekOrigin.Begin); + fs.Read(buffer, 0, buffer.Length); + start += BitConverter.ToUInt32(buffer, 0); + fs.Seek(sizeof(uint), SeekOrigin.Begin); + } + else + { + fs.Read(buffer, 0, buffer.Length); + start = BitConverter.ToUInt32(buffer, 0); + fs.Seek(0, SeekOrigin.Begin); + } + } + else + { + if (writeStart) + { + fs.Write(BitConverter.GetBytes(start), 0, sizeof(uint)); + } + } + if (start <= end) + fs.Write(BitConverter.GetBytes(count), 0, sizeof(uint)); + fs.Seek(0, SeekOrigin.End); + Console.WriteLine("Export block from " + start + " to " + end); + + using (var percent = new ConsolePercent(start, end)) + { + for (uint i = start; i <= end; i++) + { + Block block = NativeContract.Ledger.GetBlock(NeoSystem.StoreView, i); + byte[] array = block.ToArray(); + fs.Write(BitConverter.GetBytes(array.Length), 0, sizeof(int)); + fs.Write(array, 0, array.Length); + percent.Value = i; + } + } + } + + private static void WriteLineWithoutFlicker(string message = "", int maxWidth = 80) + { + if (message.Length > 0) Console.Write(message); + var spacesToErase = maxWidth - message.Length; + if (spacesToErase < 0) spacesToErase = 0; + Console.WriteLine(new string(' ', spacesToErase)); + } + + /// + /// Make and send transaction with script, sender + /// + /// script + /// sender + /// Max fee for running the script + private void SendTransaction(byte[] script, UInt160? account = null, long gas = TestModeGas) + { + if (NoWallet()) return; + + Signer[] signers = Array.Empty(); + var snapshot = NeoSystem.StoreView; + + if (account != null) + { + signers = CurrentWallet!.GetAccounts() + .Where(p => !p.Lock && !p.WatchOnly && p.ScriptHash == account && NativeContract.GAS.BalanceOf(snapshot, p.ScriptHash).Sign > 0) + .Select(p => new Signer { Account = p.ScriptHash, Scopes = WitnessScope.CalledByEntry }) + .ToArray(); + } + + try + { + Transaction tx = CurrentWallet!.MakeTransaction(snapshot, script, account, signers, maxGas: gas); + ConsoleHelper.Info("Invoking script with: ", $"'{Convert.ToBase64String(tx.Script.Span)}'"); + + using (ApplicationEngine engine = ApplicationEngine.Run(tx.Script, snapshot, container: tx, settings: NeoSystem.Settings, gas: gas)) + { + PrintExecutionOutput(engine, true); + if (engine.State == VMState.FAULT) return; + } + + if (!ConsoleHelper.ReadUserInput("Relay tx(no|yes)").IsYes()) + { + return; + } + + SignAndSendTx(NeoSystem.StoreView, tx); + } + catch (InvalidOperationException e) + { + ConsoleHelper.Error(GetExceptionMessage(e)); + } + } + + /// + /// Process "invoke" command + /// + /// Script hash + /// Operation + /// Result + /// Transaction + /// Contract parameters + /// Show result stack if it is true + /// Max fee for running the script + /// Return true if it was successful + private bool OnInvokeWithResult(UInt160 scriptHash, string operation, out StackItem result, IVerifiable? verifiable = null, JArray? contractParameters = null, bool showStack = true, long gas = TestModeGas) + { + List parameters = new(); + + if (contractParameters != null) + { + foreach (var contractParameter in contractParameters) + { + if (contractParameter is not null) + { + parameters.Add(ContractParameter.FromJson((JObject)contractParameter)); + } + } + } + + ContractState contract = NativeContract.ContractManagement.GetContract(NeoSystem.StoreView, scriptHash); + if (contract == null) + { + ConsoleHelper.Error("Contract does not exist."); + result = StackItem.Null; + return false; + } + else + { + if (contract.Manifest.Abi.GetMethod(operation, parameters.Count) == null) + { + ConsoleHelper.Error("This method does not not exist in this contract."); + result = StackItem.Null; + return false; + } + } + + byte[] script; + + using (ScriptBuilder scriptBuilder = new ScriptBuilder()) + { + scriptBuilder.EmitDynamicCall(scriptHash, operation, parameters.ToArray()); + script = scriptBuilder.ToArray(); + ConsoleHelper.Info("Invoking script with: ", $"'{script.ToBase64String()}'"); + } + + if (verifiable is Transaction tx) + { + tx.Script = script; + } + + using ApplicationEngine engine = ApplicationEngine.Run(script, NeoSystem.StoreView, container: verifiable, settings: NeoSystem.Settings, gas: gas); + PrintExecutionOutput(engine, showStack); + result = engine.State == VMState.FAULT ? StackItem.Null : engine.ResultStack.Peek(); + return engine.State != VMState.FAULT; + } + + private void PrintExecutionOutput(ApplicationEngine engine, bool showStack = true) + { + ConsoleHelper.Info("VM State: ", engine.State.ToString()); + ConsoleHelper.Info("Gas Consumed: ", new BigDecimal((BigInteger)engine.GasConsumed, NativeContract.GAS.Decimals).ToString()); + + if (showStack) + ConsoleHelper.Info("Result Stack: ", new JArray(engine.ResultStack.Select(p => p.ToJson())).ToString()); + + if (engine.State == VMState.FAULT) + ConsoleHelper.Error(GetExceptionMessage(engine.FaultException)); + } + + static string GetExceptionMessage(Exception exception) + { + if (exception == null) return "Engine faulted."; + + if (exception.InnerException != null) + { + return GetExceptionMessage(exception.InnerException); + } + + return exception.Message; + } + + public UInt160 ResolveNeoNameServiceAddress(string domain) + { + if (Settings.Default.Contracts.NeoNameService == UInt160.Zero) + throw new Exception("Neo Name Service (NNS): is disabled on this network."); + + using var sb = new ScriptBuilder(); + sb.EmitDynamicCall(Settings.Default.Contracts.NeoNameService, "resolve", CallFlags.ReadOnly, domain, 16); + + using var appEng = ApplicationEngine.Run(sb.ToArray(), NeoSystem.StoreView, settings: NeoSystem.Settings); + if (appEng.State == VMState.HALT) + { + var data = appEng.ResultStack.Pop(); + if (data is ByteString) + { + try + { + var addressData = data.GetString(); + if (UInt160.TryParse(addressData, out var address)) + return address; + else + return addressData.ToScriptHash(NeoSystem.Settings.AddressVersion); + } + catch { } + } + else if (data is Null) + { + throw new Exception($"Neo Name Service (NNS): \"{domain}\" domain not found."); + } + throw new Exception("Neo Name Service (NNS): Record invalid address format."); + } + else + { + if (appEng.FaultException is not null) + { + throw new Exception($"Neo Name Service (NNS): \"{appEng.FaultException.Message}\"."); + } + } + throw new Exception($"Neo Name Service (NNS): \"{domain}\" domain not found."); + } + } +} diff --git a/src/Neo.CLI/Dockerfile b/src/Neo.CLI/Dockerfile new file mode 100644 index 0000000000..7b67a6812d --- /dev/null +++ b/src/Neo.CLI/Dockerfile @@ -0,0 +1,20 @@ +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:7.0 AS Build + +# Run this from the repository root folder +COPY src . +COPY NuGet.Config /Neo.CLI + +WORKDIR /Neo.CLI +RUN dotnet restore && dotnet publish -f net7.0 -c Release -o /app + +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/aspnet:7.0 AS Final +RUN apt-get update && apt-get install -y \ + screen \ + libleveldb-dev \ + sqlite3 +RUN rm -rf /var/lib/apt/lists/* + +WORKDIR /Neo.CLI +COPY --from=Build /app . + +ENTRYPOINT ["screen","-DmS","node","dotnet","neo-cli.dll","-r"] diff --git a/src/Neo.CLI/Extensions.cs b/src/Neo.CLI/Extensions.cs new file mode 100644 index 0000000000..b8331201be --- /dev/null +++ b/src/Neo.CLI/Extensions.cs @@ -0,0 +1,29 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Extensions.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.Linq; +using System.Reflection; + +namespace Neo +{ + /// + /// Extension methods + /// + internal static class Extensions + { + public static string GetVersion(this Assembly assembly) + { + CustomAttributeData? attribute = assembly.CustomAttributes.FirstOrDefault(p => p.AttributeType == typeof(AssemblyInformationalVersionAttribute)); + if (attribute == null) return assembly.GetName().Version?.ToString(3) ?? string.Empty; + return (string)attribute.ConstructorArguments[0].Value!; + } + } +} diff --git a/src/Neo.CLI/Neo.CLI.csproj b/src/Neo.CLI/Neo.CLI.csproj new file mode 100644 index 0000000000..0fa76ee2c9 --- /dev/null +++ b/src/Neo.CLI/Neo.CLI.csproj @@ -0,0 +1,40 @@ + + + + net7.0 + Neo.CLI + neo-cli + Exe + Neo.CLI + Neo + Neo.CLI + neo.ico + enable + + + + + + + + + + + + + PreserveNewest + PreserveNewest + + + + + + + + + + + + + + diff --git a/src/Neo.CLI/Program.cs b/src/Neo.CLI/Program.cs new file mode 100644 index 0000000000..b2a0f1b45c --- /dev/null +++ b/src/Neo.CLI/Program.cs @@ -0,0 +1,24 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Program.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.CLI; + +namespace Neo +{ + static class Program + { + static void Main(string[] args) + { + var mainService = new MainService(); + mainService.Run(args); + } + } +} diff --git a/src/Neo.CLI/Settings.cs b/src/Neo.CLI/Settings.cs new file mode 100644 index 0000000000..ecc38115c3 --- /dev/null +++ b/src/Neo.CLI/Settings.cs @@ -0,0 +1,186 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Settings.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.Extensions.Configuration; +using Neo.Network.P2P; +using Neo.Persistence; +using System; +using System.Reflection; +using System.Threading; + +namespace Neo +{ + public class Settings + { + public LoggerSettings Logger { get; init; } + public StorageSettings Storage { get; init; } + public P2PSettings P2P { get; init; } + public UnlockWalletSettings UnlockWallet { get; init; } + public ContractsSettings Contracts { get; init; } + public PluginsSettings Plugins { get; init; } + + static Settings? s_default; + + static bool UpdateDefault(IConfiguration configuration) + { + var settings = new Settings(configuration.GetSection("ApplicationConfiguration")); + return null == Interlocked.CompareExchange(ref s_default, settings, null); + } + + public static bool Initialize(IConfiguration configuration) + { + return UpdateDefault(configuration); + } + + public static Settings Default + { + get + { + if (s_default == null) + { + var config = new ConfigurationBuilder().AddJsonFile("config.json", optional: true).Build(); + Initialize(config); + } + return Custom ?? s_default!; + } + } + + public static Settings? Custom { get; set; } + + public Settings(IConfigurationSection section) + { + Contracts = new(section.GetSection(nameof(Contracts))); + Logger = new(section.GetSection(nameof(Logger))); + Storage = new(section.GetSection(nameof(Storage))); + P2P = new(section.GetSection(nameof(P2P))); + UnlockWallet = new(section.GetSection(nameof(UnlockWallet))); + Plugins = new(section.GetSection(nameof(Plugins))); + } + + public Settings() + { + Logger = new LoggerSettings(); + Storage = new StorageSettings(); + P2P = new P2PSettings(); + UnlockWallet = new UnlockWalletSettings(); + Contracts = new ContractsSettings(); + Plugins = new PluginsSettings(); + } + } + + public class LoggerSettings + { + public string Path { get; init; } = string.Empty; + public bool ConsoleOutput { get; init; } + public bool Active { get; init; } + + public LoggerSettings(IConfigurationSection section) + { + Path = section.GetValue(nameof(Path), "Logs")!; + ConsoleOutput = section.GetValue(nameof(ConsoleOutput), false); + Active = section.GetValue(nameof(Active), false); + } + + public LoggerSettings() { } + } + + public class StorageSettings + { + public string Engine { get; init; } = nameof(MemoryStore); + public string Path { get; init; } = string.Empty; + + public StorageSettings(IConfigurationSection section) + { + Engine = section.GetValue(nameof(Engine), nameof(MemoryStore))!; + Path = section.GetValue(nameof(Path), string.Empty)!; + } + + public StorageSettings() { } + } + + public class P2PSettings + { + public ushort Port { get; } + public int MinDesiredConnections { get; } + public int MaxConnections { get; } + public int MaxConnectionsPerAddress { get; } + + public P2PSettings(IConfigurationSection section) + { + Port = section.GetValue(nameof(Port), 10333); + MinDesiredConnections = section.GetValue(nameof(MinDesiredConnections), Peer.DefaultMinDesiredConnections); + MaxConnections = section.GetValue(nameof(MaxConnections), Peer.DefaultMaxConnections); + MaxConnectionsPerAddress = section.GetValue(nameof(MaxConnectionsPerAddress), 3); + } + + public P2PSettings() { } + } + + public class UnlockWalletSettings + { + public string? Path { get; init; } = string.Empty; + public string? Password { get; init; } = string.Empty; + public bool IsActive { get; init; } = false; + + public UnlockWalletSettings(IConfigurationSection section) + { + if (section.Exists()) + { + Path = section.GetValue(nameof(Path), string.Empty)!; + Password = section.GetValue(nameof(Password), string.Empty)!; + IsActive = section.GetValue(nameof(IsActive), false); + } + } + + public UnlockWalletSettings() { } + } + + public class ContractsSettings + { + public UInt160 NeoNameService { get; init; } = UInt160.Zero; + + public ContractsSettings(IConfigurationSection section) + { + if (section.Exists()) + { + if (UInt160.TryParse(section.GetValue(nameof(NeoNameService), string.Empty), out var hash)) + { + NeoNameService = hash; + } + else + throw new ArgumentException("Neo Name Service (NNS): NeoNameService hash is invalid. Check your config.json.", nameof(NeoNameService)); + } + } + + public ContractsSettings() { } + } + + public class PluginsSettings + { + public Uri DownloadUrl { get; init; } = new("https://api.github.com/repos/neo-project/neo-modules/releases"); + public bool Prerelease { get; init; } = false; + public Version Version { get; init; } = Assembly.GetExecutingAssembly().GetName().Version!; + + public PluginsSettings(IConfigurationSection section) + { + if (section.Exists()) + { + DownloadUrl = section.GetValue(nameof(DownloadUrl), DownloadUrl)!; +#if DEBUG + Prerelease = section.GetValue(nameof(Prerelease), Prerelease); + Version = section.GetValue(nameof(Version), Version)!; +#endif + } + } + + public PluginsSettings() { } + } +} diff --git a/src/Neo.CLI/config.fs.mainnet.json b/src/Neo.CLI/config.fs.mainnet.json new file mode 100644 index 0000000000..fad987209a --- /dev/null +++ b/src/Neo.CLI/config.fs.mainnet.json @@ -0,0 +1,58 @@ +{ + "ApplicationConfiguration": { + "Logger": { + "Path": "Logs", + "ConsoleOutput": false, + "Active": false + }, + "Storage": { + "Engine": "LevelDBStore", // Candidates [MemoryStore, LevelDBStore, RocksDBStore] + "Path": "Data_LevelDB_{0}" // {0} is a placeholder for the network id + }, + "P2P": { + "Port": 40333, + "MinDesiredConnections": 10, + "MaxConnections": 40, + "MaxConnectionsPerAddress": 3 + }, + "UnlockWallet": { + "Path": "", + "Password": "", + "IsActive": false + }, + "Contracts": { + "NeoNameService": "0x7061fbd31562664b58f422c3dee4acfd70dba8af" + }, + "Plugins": { + "DownloadUrl": "https://api.github.com/repos/neo-project/neo-modules/releases" + } + }, + "ProtocolConfiguration": { + "Network": 91414437, + "AddressVersion": 53, + "MillisecondsPerBlock": 15000, + "MaxTransactionsPerBlock": 512, + "MemoryPoolMaxTransactions": 50000, + "MaxTraceableBlocks": 2102400, + "InitialGasDistribution": 5200000000000000, + "ValidatorsCount": 7, + "StandbyCommittee": [ + "026fa34ec057d74c2fdf1a18e336d0bd597ea401a0b2ad57340d5c220d09f44086", + "039a9db2a30942b1843db673aeb0d4fd6433f74cec1d879de6343cb9fcf7628fa4", + "0366d255e7ce23ea6f7f1e4bedf5cbafe598705b47e6ec213ef13b2f0819e8ab33", + "023f9cb7bbe154d529d5c719fdc39feaa831a43ae03d2a4280575b60f52fa7bc52", + "039ba959e0ab6dc616df8b803692f1c30ba9071b76b05535eb994bf5bbc402ad5f", + "035a2a18cddafa25ad353dea5e6730a1b9fcb4b918c4a0303c4387bb9c3b816adf", + "031f4d9c66f2ec348832c48fd3a16dfaeb59e85f557ae1e07f6696d0375c64f97b" + ], + "SeedList": [ + "morph1.fs.neo.org:40333", + "morph2.fs.neo.org:40333", + "morph3.fs.neo.org:40333", + "morph4.fs.neo.org:40333", + "morph5.fs.neo.org:40333", + "morph6.fs.neo.org:40333", + "morph7.fs.neo.org:40333" + ] + } +} diff --git a/src/Neo.CLI/config.fs.testnet.json b/src/Neo.CLI/config.fs.testnet.json new file mode 100644 index 0000000000..b167f35b81 --- /dev/null +++ b/src/Neo.CLI/config.fs.testnet.json @@ -0,0 +1,58 @@ +{ + "ApplicationConfiguration": { + "Logger": { + "Path": "Logs", + "ConsoleOutput": false, + "Active": false + }, + "Storage": { + "Engine": "LevelDBStore", // Candidates [MemoryStore, LevelDBStore, RocksDBStore] + "Path": "Data_LevelDB_{0}" // {0} is a placeholder for the network id + }, + "P2P": { + "Port": 50333, + "MinDesiredConnections": 10, + "MaxConnections": 40, + "MaxConnectionsPerAddress": 3 + }, + "UnlockWallet": { + "Path": "", + "Password": "", + "IsActive": false + }, + "Contracts": { + "NeoNameService": "0xfb08ccf30ab534a871b7b092a49fe70c154ed678" + }, + "Plugins": { + "DownloadUrl": "https://api.github.com/repos/neo-project/neo-modules/releases" + } + }, + "ProtocolConfiguration": { + "Network": 91466898, + "AddressVersion": 53, + "MillisecondsPerBlock": 15000, + "MaxTransactionsPerBlock": 512, + "MemoryPoolMaxTransactions": 50000, + "MaxTraceableBlocks": 2102400, + "InitialGasDistribution": 5200000000000000, + "ValidatorsCount": 7, + "StandbyCommittee": [ + "02082828ec6efc92e5e7790da851be72d2091a961c1ac9a1772acbf181ac56b831", + "02b2bcf7e09c0237ab6ef21808e6f7546329823bc6b43488335bd357aea443fabe", + "03577029a5072ebbab12d2495b59e2cf27afb37f9640c1c1354f1bdd221e6fb82d", + "03e6ea086e4b42fa5f0535179862db7eea7e44644e5e9608d6131aa48868c12cfc", + "0379328ab4907ea7c47f61e5c9d2c78c39dc9d1c4341ca496376070a0a5e20131e", + "02f8af6440dfe0e676ae2bb6727e5cc31a6f2459e29f48e85428862b7577dbc203", + "02e19c0634c85d35937699cdeaa10595ec2e18bfe86ba0494cf6c5c6861c66b97d" + ], + "SeedList": [ + "morph01.testnet.fs.neo.org:50333", + "morph02.testnet.fs.neo.org:50333", + "morph03.testnet.fs.neo.org:50333", + "morph04.testnet.fs.neo.org:50333", + "morph05.testnet.fs.neo.org:50333", + "morph06.testnet.fs.neo.org:50333", + "morph07.testnet.fs.neo.org:50333" + ] + } +} diff --git a/src/Neo.CLI/config.json b/src/Neo.CLI/config.json new file mode 100644 index 0000000000..b4800b80ea --- /dev/null +++ b/src/Neo.CLI/config.json @@ -0,0 +1,74 @@ +{ + "ApplicationConfiguration": { + "Logger": { + "Path": "Logs", + "ConsoleOutput": false, + "Active": false + }, + "Storage": { + "Engine": "LevelDBStore", // Candidates [MemoryStore, LevelDBStore, RocksDBStore] + "Path": "Data_LevelDB_{0}" // {0} is a placeholder for the network id + }, + "P2P": { + "Port": 10333, + "MinDesiredConnections": 10, + "MaxConnections": 40, + "MaxConnectionsPerAddress": 3 + }, + "UnlockWallet": { + "Path": "", + "Password": "", + "IsActive": false + }, + "Contracts": { + "NeoNameService": "0x50ac1c37690cc2cfc594472833cf57505d5f46de" + }, + "Plugins": { + "DownloadUrl": "https://api.github.com/repos/neo-project/neo-modules/releases" + } + }, + "ProtocolConfiguration": { + "Network": 860833102, + "AddressVersion": 53, + "MillisecondsPerBlock": 15000, + "MaxTransactionsPerBlock": 512, + "MemoryPoolMaxTransactions": 50000, + "MaxTraceableBlocks": 2102400, + "Hardforks": { + "HF_Aspidochelone": 1730000, + "HF_Basilisk": 4120000 + }, + "InitialGasDistribution": 5200000000000000, + "ValidatorsCount": 7, + "StandbyCommittee": [ + "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", + "02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", + "03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", + "02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", + "024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", + "02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", + "02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", + "023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe", + "03708b860c1de5d87f5b151a12c2a99feebd2e8b315ee8e7cf8aa19692a9e18379", + "03c6aa6e12638b36e88adc1ccdceac4db9929575c3e03576c617c49cce7114a050", + "03204223f8c86b8cd5c89ef12e4f0dbb314172e9241e30c9ef2293790793537cf0", + "02a62c915cf19c7f19a50ec217e79fac2439bbaad658493de0c7d8ffa92ab0aa62", + "03409f31f0d66bdc2f70a9730b66fe186658f84a8018204db01c106edc36553cd0", + "0288342b141c30dc8ffcde0204929bb46aed5756b41ef4a56778d15ada8f0c6654", + "020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639", + "0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30", + "03d281b42002647f0113f36c7b8efb30db66078dfaaa9ab3ff76d043a98d512fde", + "02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad", + "0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d", + "03cdcea66032b82f5c30450e381e5295cae85c5e6943af716cc6b646352a6067dc", + "02cd5a5547119e24feaa7c2a0f37b8c9366216bab7054de0065c9be42084003c8a" + ], + "SeedList": [ + "seed1.neo.org:10333", + "seed2.neo.org:10333", + "seed3.neo.org:10333", + "seed4.neo.org:10333", + "seed5.neo.org:10333" + ] + } +} diff --git a/src/Neo.CLI/config.mainnet.json b/src/Neo.CLI/config.mainnet.json new file mode 100644 index 0000000000..b4800b80ea --- /dev/null +++ b/src/Neo.CLI/config.mainnet.json @@ -0,0 +1,74 @@ +{ + "ApplicationConfiguration": { + "Logger": { + "Path": "Logs", + "ConsoleOutput": false, + "Active": false + }, + "Storage": { + "Engine": "LevelDBStore", // Candidates [MemoryStore, LevelDBStore, RocksDBStore] + "Path": "Data_LevelDB_{0}" // {0} is a placeholder for the network id + }, + "P2P": { + "Port": 10333, + "MinDesiredConnections": 10, + "MaxConnections": 40, + "MaxConnectionsPerAddress": 3 + }, + "UnlockWallet": { + "Path": "", + "Password": "", + "IsActive": false + }, + "Contracts": { + "NeoNameService": "0x50ac1c37690cc2cfc594472833cf57505d5f46de" + }, + "Plugins": { + "DownloadUrl": "https://api.github.com/repos/neo-project/neo-modules/releases" + } + }, + "ProtocolConfiguration": { + "Network": 860833102, + "AddressVersion": 53, + "MillisecondsPerBlock": 15000, + "MaxTransactionsPerBlock": 512, + "MemoryPoolMaxTransactions": 50000, + "MaxTraceableBlocks": 2102400, + "Hardforks": { + "HF_Aspidochelone": 1730000, + "HF_Basilisk": 4120000 + }, + "InitialGasDistribution": 5200000000000000, + "ValidatorsCount": 7, + "StandbyCommittee": [ + "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", + "02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", + "03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", + "02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", + "024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", + "02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", + "02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", + "023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe", + "03708b860c1de5d87f5b151a12c2a99feebd2e8b315ee8e7cf8aa19692a9e18379", + "03c6aa6e12638b36e88adc1ccdceac4db9929575c3e03576c617c49cce7114a050", + "03204223f8c86b8cd5c89ef12e4f0dbb314172e9241e30c9ef2293790793537cf0", + "02a62c915cf19c7f19a50ec217e79fac2439bbaad658493de0c7d8ffa92ab0aa62", + "03409f31f0d66bdc2f70a9730b66fe186658f84a8018204db01c106edc36553cd0", + "0288342b141c30dc8ffcde0204929bb46aed5756b41ef4a56778d15ada8f0c6654", + "020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639", + "0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30", + "03d281b42002647f0113f36c7b8efb30db66078dfaaa9ab3ff76d043a98d512fde", + "02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad", + "0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d", + "03cdcea66032b82f5c30450e381e5295cae85c5e6943af716cc6b646352a6067dc", + "02cd5a5547119e24feaa7c2a0f37b8c9366216bab7054de0065c9be42084003c8a" + ], + "SeedList": [ + "seed1.neo.org:10333", + "seed2.neo.org:10333", + "seed3.neo.org:10333", + "seed4.neo.org:10333", + "seed5.neo.org:10333" + ] + } +} diff --git a/src/Neo.CLI/config.testnet.json b/src/Neo.CLI/config.testnet.json new file mode 100644 index 0000000000..19a9ca1442 --- /dev/null +++ b/src/Neo.CLI/config.testnet.json @@ -0,0 +1,74 @@ +{ + "ApplicationConfiguration": { + "Logger": { + "Path": "Logs", + "ConsoleOutput": false, + "Active": false + }, + "Storage": { + "Engine": "LevelDBStore", // Candidates [MemoryStore, LevelDBStore, RocksDBStore] + "Path": "Data_LevelDB_{0}" // {0} is a placeholder for the network id + }, + "P2P": { + "Port": 20333, + "MinDesiredConnections": 10, + "MaxConnections": 40, + "MaxConnectionsPerAddress": 3 + }, + "UnlockWallet": { + "Path": "", + "Password": "", + "IsActive": false + }, + "Contracts": { + "NeoNameService": "0x50ac1c37690cc2cfc594472833cf57505d5f46de" + }, + "Plugins": { + "DownloadUrl": "https://api.github.com/repos/neo-project/neo-modules/releases" + } + }, + "ProtocolConfiguration": { + "Network": 894710606, + "AddressVersion": 53, + "MillisecondsPerBlock": 15000, + "MaxTransactionsPerBlock": 5000, + "MemoryPoolMaxTransactions": 50000, + "MaxTraceableBlocks": 2102400, + "Hardforks": { + "HF_Aspidochelone": 210000, + "HF_Basilisk": 2680000 + }, + "InitialGasDistribution": 5200000000000000, + "ValidatorsCount": 7, + "StandbyCommittee": [ + "023e9b32ea89b94d066e649b124fd50e396ee91369e8e2a6ae1b11c170d022256d", + "03009b7540e10f2562e5fd8fac9eaec25166a58b26e412348ff5a86927bfac22a2", + "02ba2c70f5996f357a43198705859fae2cfea13e1172962800772b3d588a9d4abd", + "03408dcd416396f64783ac587ea1e1593c57d9fea880c8a6a1920e92a259477806", + "02a7834be9b32e2981d157cb5bbd3acb42cfd11ea5c3b10224d7a44e98c5910f1b", + "0214baf0ceea3a66f17e7e1e839ea25fd8bed6cd82e6bb6e68250189065f44ff01", + "030205e9cefaea5a1dfc580af20c8d5aa2468bb0148f1a5e4605fc622c80e604ba", + "025831cee3708e87d78211bec0d1bfee9f4c85ae784762f042e7f31c0d40c329b8", + "02cf9dc6e85d581480d91e88e8cbeaa0c153a046e89ded08b4cefd851e1d7325b5", + "03840415b0a0fcf066bcc3dc92d8349ebd33a6ab1402ef649bae00e5d9f5840828", + "026328aae34f149853430f526ecaa9cf9c8d78a4ea82d08bdf63dd03c4d0693be6", + "02c69a8d084ee7319cfecf5161ff257aa2d1f53e79bf6c6f164cff5d94675c38b3", + "0207da870cedb777fceff948641021714ec815110ca111ccc7a54c168e065bda70", + "035056669864feea401d8c31e447fb82dd29f342a9476cfd449584ce2a6165e4d7", + "0370c75c54445565df62cfe2e76fbec4ba00d1298867972213530cae6d418da636", + "03957af9e77282ae3263544b7b2458903624adc3f5dee303957cb6570524a5f254", + "03d84d22b8753cf225d263a3a782a4e16ca72ef323cfde04977c74f14873ab1e4c", + "02147c1b1d5728e1954958daff2f88ee2fa50a06890a8a9db3fa9e972b66ae559f", + "03c609bea5a4825908027e4ab217e7efc06e311f19ecad9d417089f14927a173d5", + "0231edee3978d46c335e851c76059166eb8878516f459e085c0dd092f0f1d51c21", + "03184b018d6b2bc093e535519732b3fd3f7551c8cffaf4621dd5a0b89482ca66c9" + ], + "SeedList": [ + "seed1t5.neo.org:20333", + "seed2t5.neo.org:20333", + "seed3t5.neo.org:20333", + "seed4t5.neo.org:20333", + "seed5t5.neo.org:20333" + ] + } +} diff --git a/src/Neo.CLI/neo.ico b/src/Neo.CLI/neo.ico new file mode 100644 index 0000000000..403aa7f376 Binary files /dev/null and b/src/Neo.CLI/neo.ico differ diff --git a/src/Neo.ConsoleService/CommandQuoteToken.cs b/src/Neo.ConsoleService/CommandQuoteToken.cs new file mode 100644 index 0000000000..865b7560d0 --- /dev/null +++ b/src/Neo.ConsoleService/CommandQuoteToken.cs @@ -0,0 +1,54 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// CommandQuoteToken.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; +using System.Diagnostics; + +namespace Neo.ConsoleService +{ + [DebuggerDisplay("Value={Value}, Value={Value}")] + internal class CommandQuoteToken : CommandToken + { + /// + /// Constructor + /// + /// Offset + /// Value + public CommandQuoteToken(int offset, char value) : base(CommandTokenType.Quote, offset) + { + if (value != '\'' && value != '"') + { + throw new ArgumentException("Not valid quote"); + } + + Value = value.ToString(); + } + + /// + /// Parse command line quotes + /// + /// Command line + /// Index + /// CommandQuoteToken + internal static CommandQuoteToken Parse(string commandLine, ref int index) + { + var c = commandLine[index]; + + if (c == '\'' || c == '"') + { + index++; + return new CommandQuoteToken(index - 1, c); + } + + throw new ArgumentException("No quote found"); + } + } +} diff --git a/src/Neo.ConsoleService/CommandSpaceToken.cs b/src/Neo.ConsoleService/CommandSpaceToken.cs new file mode 100644 index 0000000000..87080d1983 --- /dev/null +++ b/src/Neo.ConsoleService/CommandSpaceToken.cs @@ -0,0 +1,65 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// CommandSpaceToken.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; +using System.Diagnostics; + +namespace Neo.ConsoleService +{ + [DebuggerDisplay("Value={Value}, Count={Count}")] + internal class CommandSpaceToken : CommandToken + { + /// + /// Count + /// + public int Count { get; } + + /// + /// Constructor + /// + /// Offset + /// Count + public CommandSpaceToken(int offset, int count) : base(CommandTokenType.Space, offset) + { + Value = "".PadLeft(count, ' '); + Count = count; + } + + /// + /// Parse command line spaces + /// + /// Command line + /// Index + /// CommandSpaceToken + internal static CommandSpaceToken Parse(string commandLine, ref int index) + { + int offset = index; + int count = 0; + + for (int ix = index, max = commandLine.Length; ix < max; ix++) + { + if (commandLine[ix] == ' ') + { + count++; + } + else + { + break; + } + } + + if (count == 0) throw new ArgumentException("No spaces found"); + + index += count; + return new CommandSpaceToken(offset, count); + } + } +} diff --git a/src/Neo.ConsoleService/CommandStringToken.cs b/src/Neo.ConsoleService/CommandStringToken.cs new file mode 100644 index 0000000000..49a5702756 --- /dev/null +++ b/src/Neo.ConsoleService/CommandStringToken.cs @@ -0,0 +1,91 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// CommandStringToken.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; +using System.Diagnostics; + +namespace Neo.ConsoleService +{ + [DebuggerDisplay("Value={Value}, RequireQuotes={RequireQuotes}")] + internal class CommandStringToken : CommandToken + { + /// + /// Require quotes + /// + public bool RequireQuotes { get; } + + /// + /// Constructor + /// + /// Offset + /// Value + public CommandStringToken(int offset, string value) : base(CommandTokenType.String, offset) + { + Value = value; + RequireQuotes = value.IndexOfAny(new char[] { '\'', '"' }) != -1; + } + + /// + /// Parse command line spaces + /// + /// Command line + /// Index + /// Quote (could be null) + /// CommandSpaceToken + internal static CommandStringToken Parse(string commandLine, ref int index, CommandQuoteToken? quote) + { + int end; + int offset = index; + + if (quote != null) + { + var ix = index; + + do + { + end = commandLine.IndexOf(quote.Value[0], ix + 1); + + if (end == -1) + { + throw new ArgumentException("String not closed"); + } + + if (IsScaped(commandLine, end - 1)) + { + ix = end; + end = -1; + } + } + while (end < 0); + } + else + { + end = commandLine.IndexOf(' ', index + 1); + } + + if (end == -1) + { + end = commandLine.Length; + } + + var ret = new CommandStringToken(offset, commandLine.Substring(index, end - index)); + index += end - index; + return ret; + } + + private static bool IsScaped(string commandLine, int index) + { + // TODO: Scape the scape + + return (commandLine[index] == '\\'); + } + } +} diff --git a/src/Neo.ConsoleService/CommandToken.cs b/src/Neo.ConsoleService/CommandToken.cs new file mode 100644 index 0000000000..10e8336ecc --- /dev/null +++ b/src/Neo.ConsoleService/CommandToken.cs @@ -0,0 +1,229 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// CommandToken.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; +using System.Collections.Generic; +using System.Text; + +namespace Neo.ConsoleService +{ + internal abstract class CommandToken + { + /// + /// Offset + /// + public int Offset { get; } + + /// + /// Type + /// + public CommandTokenType Type { get; } + + /// + /// Value + /// + public string Value { get; protected init; } = string.Empty; + + /// + /// Constructor + /// + /// Type + /// Offset + protected CommandToken(CommandTokenType type, int offset) + { + Type = type; + Offset = offset; + } + + /// + /// Parse command line + /// + /// Command line + /// + public static IEnumerable Parse(string commandLine) + { + CommandToken? lastToken = null; + + for (int index = 0, count = commandLine.Length; index < count;) + { + switch (commandLine[index]) + { + case ' ': + { + lastToken = CommandSpaceToken.Parse(commandLine, ref index); + yield return lastToken; + break; + } + case '"': + case '\'': + { + // "'" + if (lastToken is CommandQuoteToken quote && quote.Value[0] != commandLine[index]) + { + goto default; + } + + lastToken = CommandQuoteToken.Parse(commandLine, ref index); + yield return lastToken; + break; + } + default: + { + lastToken = CommandStringToken.Parse(commandLine, ref index, + lastToken is CommandQuoteToken quote ? quote : null); + + if (lastToken is not null) + { + yield return lastToken; + } + break; + } + } + } + } + + /// + /// Create string arguments + /// + /// Tokens + /// Remove escape + /// Arguments + public static string[] ToArguments(IEnumerable tokens, bool removeEscape = true) + { + var list = new List(); + + CommandToken? lastToken = null; + + foreach (var token in tokens) + { + if (token is CommandStringToken str) + { + if (removeEscape && lastToken is CommandQuoteToken quote) + { + // Remove escape + + list.Add(str.Value.Replace("\\" + quote.Value, quote.Value)); + } + else + { + list.Add(str.Value); + } + } + + lastToken = token; + } + + return list.ToArray(); + } + + /// + /// Create a string from token list + /// + /// Tokens + /// String + public static string ToString(IEnumerable tokens) + { + var sb = new StringBuilder(); + + foreach (var token in tokens) + { + sb.Append(token.Value); + } + + return sb.ToString(); + } + + /// + /// Trim + /// + /// Args + public static void Trim(List args) + { + // Trim start + + while (args.Count > 0 && args[0].Type == CommandTokenType.Space) + { + args.RemoveAt(0); + } + + // Trim end + + while (args.Count > 0 && args[^1].Type == CommandTokenType.Space) + { + args.RemoveAt(args.Count - 1); + } + } + + /// + /// Read String + /// + /// Args + /// Consume all if not quoted + /// String + public static string? ReadString(List args, bool consumeAll) + { + Trim(args); + + var quoted = false; + + if (args.Count > 0 && args[0].Type == CommandTokenType.Quote) + { + quoted = true; + args.RemoveAt(0); + } + else + { + if (consumeAll) + { + // Return all if it's not quoted + + var ret = ToString(args); + args.Clear(); + + return ret; + } + } + + if (args.Count > 0) + { + switch (args[0]) + { + case CommandQuoteToken _: + { + if (quoted) + { + args.RemoveAt(0); + return ""; + } + + throw new ArgumentException(); + } + case CommandSpaceToken _: throw new ArgumentException(); + case CommandStringToken str: + { + args.RemoveAt(0); + + if (quoted && args.Count > 0 && args[0].Type == CommandTokenType.Quote) + { + // Remove last quote + + args.RemoveAt(0); + } + + return str.Value; + } + } + } + + return null; + } + } +} diff --git a/src/Neo.ConsoleService/CommandTokenType.cs b/src/Neo.ConsoleService/CommandTokenType.cs new file mode 100644 index 0000000000..2e522c000a --- /dev/null +++ b/src/Neo.ConsoleService/CommandTokenType.cs @@ -0,0 +1,20 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// CommandTokenType.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.ConsoleService +{ + internal enum CommandTokenType : byte + { + String, + Space, + Quote, + } +} diff --git a/src/Neo.ConsoleService/ConsoleColorSet.cs b/src/Neo.ConsoleService/ConsoleColorSet.cs new file mode 100644 index 0000000000..8e5c7f7b45 --- /dev/null +++ b/src/Neo.ConsoleService/ConsoleColorSet.cs @@ -0,0 +1,52 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ConsoleColorSet.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; + +namespace Neo.ConsoleService +{ + public class ConsoleColorSet + { + public ConsoleColor Foreground; + public ConsoleColor Background; + + /// + /// Create a new color set with the current console colors + /// + public ConsoleColorSet() : this(Console.ForegroundColor, Console.BackgroundColor) { } + + /// + /// Create a new color set + /// + /// Foreground color + public ConsoleColorSet(ConsoleColor foreground) : this(foreground, Console.BackgroundColor) { } + + /// + /// Create a new color set + /// + /// Foreground color + /// Background color + public ConsoleColorSet(ConsoleColor foreground, ConsoleColor background) + { + Foreground = foreground; + Background = background; + } + + /// + /// Apply the current set + /// + public void Apply() + { + Console.ForegroundColor = Foreground; + Console.BackgroundColor = Background; + } + } +} diff --git a/src/Neo.ConsoleService/ConsoleCommandAttribute.cs b/src/Neo.ConsoleService/ConsoleCommandAttribute.cs new file mode 100644 index 0000000000..c7831d617d --- /dev/null +++ b/src/Neo.ConsoleService/ConsoleCommandAttribute.cs @@ -0,0 +1,46 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ConsoleCommandAttribute.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; +using System.Diagnostics; +using System.Linq; + +namespace Neo.ConsoleService +{ + [DebuggerDisplay("Verbs={string.Join(' ',Verbs)}")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public class ConsoleCommandAttribute : Attribute + { + /// + /// Verbs + /// + public string[] Verbs { get; } + + /// + /// Category + /// + public string Category { get; set; } = string.Empty; + + /// + /// Description + /// + public string Description { get; set; } = string.Empty; + + /// + /// Constructor + /// + /// Verbs + public ConsoleCommandAttribute(string verbs) + { + Verbs = verbs.Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(u => u.ToLowerInvariant()).ToArray(); + } + } +} diff --git a/src/Neo.ConsoleService/ConsoleCommandMethod.cs b/src/Neo.ConsoleService/ConsoleCommandMethod.cs new file mode 100644 index 0000000000..3455d21bb4 --- /dev/null +++ b/src/Neo.ConsoleService/ConsoleCommandMethod.cs @@ -0,0 +1,121 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ConsoleCommandMethod.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.Collections.Generic; +using System.Diagnostics; +using System.Reflection; + +namespace Neo.ConsoleService +{ + [DebuggerDisplay("Key={Key}")] + internal class ConsoleCommandMethod + { + /// + /// Verbs + /// + public string[] Verbs { get; } + + /// + /// Key + /// + public string Key => string.Join(' ', Verbs); + + /// + /// Help category + /// + public string HelpCategory { get; set; } + + /// + /// Help message + /// + public string HelpMessage { get; set; } + + /// + /// Instance + /// + public object Instance { get; } + + /// + /// Method + /// + public MethodInfo Method { get; } + + /// + /// Set instance command + /// + /// Instance + /// Method + /// Attribute + public ConsoleCommandMethod(object instance, MethodInfo method, ConsoleCommandAttribute attribute) + { + Method = method; + Instance = instance; + Verbs = attribute.Verbs; + HelpCategory = attribute.Category; + HelpMessage = attribute.Description; + } + + /// + /// Is this command + /// + /// Tokens + /// Consumed Arguments + /// True if is this command + public bool IsThisCommand(CommandToken[] tokens, out int consumedArgs) + { + int checks = Verbs.Length; + bool quoted = false; + var tokenList = new List(tokens); + + while (checks > 0 && tokenList.Count > 0) + { + switch (tokenList[0]) + { + case CommandSpaceToken _: + { + tokenList.RemoveAt(0); + break; + } + case CommandQuoteToken _: + { + quoted = !quoted; + tokenList.RemoveAt(0); + break; + } + case CommandStringToken str: + { + if (Verbs[^checks] != str.Value.ToLowerInvariant()) + { + consumedArgs = 0; + return false; + } + + checks--; + tokenList.RemoveAt(0); + break; + } + } + } + + if (quoted && tokenList.Count > 0 && tokenList[0].Type == CommandTokenType.Quote) + { + tokenList.RemoveAt(0); + } + + // Trim start + + while (tokenList.Count > 0 && tokenList[0].Type == CommandTokenType.Space) tokenList.RemoveAt(0); + + consumedArgs = tokens.Length - tokenList.Count; + return checks == 0; + } + } +} diff --git a/src/Neo.ConsoleService/ConsoleHelper.cs b/src/Neo.ConsoleService/ConsoleHelper.cs new file mode 100644 index 0000000000..78f170a3af --- /dev/null +++ b/src/Neo.ConsoleService/ConsoleHelper.cs @@ -0,0 +1,163 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ConsoleHelper.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; +using System.Security; +using System.Text; + +namespace Neo.ConsoleService +{ + public static class ConsoleHelper + { + private static readonly ConsoleColorSet InfoColor = new(ConsoleColor.Cyan); + private static readonly ConsoleColorSet WarningColor = new(ConsoleColor.Yellow); + private static readonly ConsoleColorSet ErrorColor = new(ConsoleColor.Red); + + public static bool ReadingPassword { get; private set; } = false; + + /// + /// Info handles message in the format of "[tag]:[message]", + /// avoid using Info if the `tag` is too long + /// + /// The log message in pairs of (tag, message) + public static void Info(params string[] values) + { + var currentColor = new ConsoleColorSet(); + + for (int i = 0; i < values.Length; i++) + { + if (i % 2 == 0) + InfoColor.Apply(); + else + currentColor.Apply(); + Console.Write(values[i]); + } + currentColor.Apply(); + Console.WriteLine(); + } + + /// + /// Use warning if something unexpected happens + /// or the execution result is not correct. + /// Also use warning if you just want to remind + /// user of doing something. + /// + /// Warning message + public static void Warning(string msg) + { + Log("Warning", WarningColor, msg); + } + + /// + /// Use Error if the verification or input format check fails + /// or exception that breaks the execution of interactive + /// command throws. + /// + /// Error message + public static void Error(string msg) + { + Log("Error", ErrorColor, msg); + } + + private static void Log(string tag, ConsoleColorSet colorSet, string msg) + { + var currentColor = new ConsoleColorSet(); + + colorSet.Apply(); + Console.Write($"{tag}: "); + currentColor.Apply(); + Console.WriteLine(msg); + } + + public static string ReadUserInput(string prompt, bool password = false) + { + const string t = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + var sb = new StringBuilder(); + + if (!string.IsNullOrEmpty(prompt)) + { + Console.Write(prompt + ": "); + } + + if (password) ReadingPassword = true; + var prevForeground = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Yellow; + + if (Console.IsInputRedirected) + { + // neo-gui Console require it + sb.Append(Console.ReadLine()); + } + else + { + ConsoleKeyInfo key; + do + { + key = Console.ReadKey(true); + + if (t.IndexOf(key.KeyChar) != -1) + { + sb.Append(key.KeyChar); + Console.Write(password ? '*' : key.KeyChar); + } + else if (key.Key == ConsoleKey.Backspace && sb.Length > 0) + { + sb.Length--; + Console.Write("\b \b"); + } + } while (key.Key != ConsoleKey.Enter); + } + + Console.ForegroundColor = prevForeground; + if (password) ReadingPassword = false; + Console.WriteLine(); + return sb.ToString(); + } + + public static SecureString ReadSecureString(string prompt) + { + const string t = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + SecureString securePwd = new SecureString(); + ConsoleKeyInfo key; + + if (!string.IsNullOrEmpty(prompt)) + { + Console.Write(prompt + ": "); + } + + ReadingPassword = true; + Console.ForegroundColor = ConsoleColor.Yellow; + + do + { + key = Console.ReadKey(true); + if (t.IndexOf(key.KeyChar) != -1) + { + securePwd.AppendChar(key.KeyChar); + Console.Write('*'); + } + else if (key.Key == ConsoleKey.Backspace && securePwd.Length > 0) + { + securePwd.RemoveAt(securePwd.Length - 1); + Console.Write(key.KeyChar); + Console.Write(' '); + Console.Write(key.KeyChar); + } + } while (key.Key != ConsoleKey.Enter); + + Console.ForegroundColor = ConsoleColor.White; + ReadingPassword = false; + Console.WriteLine(); + securePwd.MakeReadOnly(); + return securePwd; + } + } +} diff --git a/src/Neo.ConsoleService/ConsoleServiceBase.cs b/src/Neo.ConsoleService/ConsoleServiceBase.cs new file mode 100644 index 0000000000..1da87354fb --- /dev/null +++ b/src/Neo.ConsoleService/ConsoleServiceBase.cs @@ -0,0 +1,562 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ConsoleServiceBase.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; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Runtime.Loader; +using System.ServiceProcess; +using System.Threading; +using System.Threading.Tasks; + +namespace Neo.ConsoleService +{ + public abstract class ConsoleServiceBase + { + protected virtual string? Depends => null; + protected virtual string Prompt => "service"; + + public abstract string ServiceName { get; } + + protected bool ShowPrompt { get; set; } = true; + + private bool _running; + private readonly CancellationTokenSource _shutdownTokenSource = new(); + private readonly CountdownEvent _shutdownAcknowledged = new(1); + private readonly Dictionary> _verbs = new(); + private readonly Dictionary _instances = new(); + private readonly Dictionary, bool, object>> _handlers = new(); + + private bool OnCommand(string commandLine) + { + if (string.IsNullOrEmpty(commandLine)) + { + return true; + } + + string? possibleHelp = null; + var commandArgs = CommandToken.Parse(commandLine).ToArray(); + var availableCommands = new List<(ConsoleCommandMethod Command, object?[] Arguments)>(); + + foreach (var entries in _verbs.Values) + { + foreach (var command in entries) + { + if (command.IsThisCommand(commandArgs, out var consumedArgs)) + { + var arguments = new List(); + var args = commandArgs.Skip(consumedArgs).ToList(); + + CommandSpaceToken.Trim(args); + + try + { + var parameters = command.Method.GetParameters(); + + foreach (var arg in parameters) + { + // Parse argument + + if (TryProcessValue(arg.ParameterType, args, arg == parameters.Last(), out var value)) + { + arguments.Add(value); + } + else + { + if (arg.HasDefaultValue) + { + arguments.Add(arg.DefaultValue); + } + else + { + throw new ArgumentException(arg.Name); + } + } + } + + availableCommands.Add((command, arguments.ToArray())); + } + catch (Exception ex) + { + // Skip parse errors + possibleHelp = command.Key; + ConsoleHelper.Error($"{ex.InnerException?.Message ?? ex.Message}"); + } + } + } + } + + switch (availableCommands.Count) + { + case 0: + { + if (!string.IsNullOrEmpty(possibleHelp)) + { + OnHelpCommand(possibleHelp); + return true; + } + + return false; + } + case 1: + { + var (command, arguments) = availableCommands[0]; + object? result = command.Method.Invoke(command.Instance, arguments); + if (result is Task task) task.Wait(); + return true; + } + default: + { + // Show Ambiguous call + + throw new ArgumentException("Ambiguous calls for: " + string.Join(',', availableCommands.Select(u => u.Command.Key).Distinct())); + } + } + } + + private bool TryProcessValue(Type parameterType, List args, bool canConsumeAll, out object? value) + { + if (args.Count > 0) + { + if (_handlers.TryGetValue(parameterType, out var handler)) + { + value = handler(args, canConsumeAll); + return true; + } + + if (parameterType.IsEnum) + { + var arg = CommandToken.ReadString(args, canConsumeAll); + if (arg is not null) + { + value = Enum.Parse(parameterType, arg.Trim(), true); + return true; + } + } + } + + value = null; + return false; + } + + #region Commands + + /// + /// Process "help" command + /// + [ConsoleCommand("help", Category = "Base Commands")] + protected void OnHelpCommand(string key) + { + var withHelp = new List(); + + // Try to find a plugin with this name + + if (_instances.TryGetValue(key.Trim().ToLowerInvariant(), out var instance)) + { + // Filter only the help of this plugin + + key = ""; + foreach (var commands in _verbs.Values.Select(u => u)) + { + withHelp.AddRange + ( + commands.Where(u => !string.IsNullOrEmpty(u.HelpCategory) && u.Instance == instance) + ); + } + } + else + { + // Fetch commands + + foreach (var commands in _verbs.Values.Select(u => u)) + { + withHelp.AddRange(commands.Where(u => !string.IsNullOrEmpty(u.HelpCategory))); + } + } + + // Sort and show + + withHelp.Sort((a, b) => + { + var cate = string.Compare(a.HelpCategory, b.HelpCategory, StringComparison.Ordinal); + if (cate == 0) + { + cate = string.Compare(a.Key, b.Key, StringComparison.Ordinal); + } + return cate; + }); + + if (string.IsNullOrEmpty(key) || key.Equals("help", StringComparison.InvariantCultureIgnoreCase)) + { + string? last = null; + foreach (var command in withHelp) + { + if (last != command.HelpCategory) + { + Console.WriteLine($"{command.HelpCategory}:"); + last = command.HelpCategory; + } + + Console.Write($"\t{command.Key}"); + Console.WriteLine(" " + string.Join(' ', + command.Method.GetParameters() + .Select(u => u.HasDefaultValue ? $"[{u.Name}={(u.DefaultValue == null ? "null" : u.DefaultValue.ToString())}]" : $"<{u.Name}>")) + ); + } + } + else + { + // Show help for this specific command + + string? last = null; + string? lastKey = null; + bool found = false; + + foreach (var command in withHelp.Where(u => u.Key == key)) + { + found = true; + + if (last != command.HelpMessage) + { + Console.WriteLine($"{command.HelpMessage}"); + last = command.HelpMessage; + } + + if (lastKey != command.Key) + { + Console.WriteLine("You can call this command like this:"); + lastKey = command.Key; + } + + Console.Write($"\t{command.Key}"); + Console.WriteLine(" " + string.Join(' ', + command.Method.GetParameters() + .Select(u => u.HasDefaultValue ? $"[{u.Name}={u.DefaultValue?.ToString() ?? "null"}]" : $"<{u.Name}>")) + ); + } + + if (!found) + { + throw new ArgumentException("Command not found."); + } + } + } + + /// + /// Process "clear" command + /// + [ConsoleCommand("clear", Category = "Base Commands", Description = "Clear is used in order to clean the console output.")] + protected void OnClear() + { + Console.Clear(); + } + + /// + /// Process "version" command + /// + [ConsoleCommand("version", Category = "Base Commands", Description = "Show the current version.")] + protected void OnVersion() + { + Console.WriteLine(Assembly.GetEntryAssembly()!.GetName().Version); + } + + /// + /// Process "exit" command + /// + [ConsoleCommand("exit", Category = "Base Commands", Description = "Exit the node.")] + protected void OnExit() + { + _running = false; + } + + #endregion + + public virtual bool OnStart(string[] args) + { + // Register sigterm event handler + AssemblyLoadContext.Default.Unloading += SigTermEventHandler; + // Register sigint event handler + Console.CancelKeyPress += CancelHandler; + return true; + } + + public virtual void OnStop() + { + _shutdownAcknowledged.Signal(); + } + + private void TriggerGracefulShutdown() + { + if (!_running) return; + _running = false; + _shutdownTokenSource.Cancel(); + // Wait for us to have triggered shutdown. + _shutdownAcknowledged.Wait(); + } + + private void SigTermEventHandler(AssemblyLoadContext obj) + { + TriggerGracefulShutdown(); + } + + private void CancelHandler(object? sender, ConsoleCancelEventArgs e) + { + e.Cancel = true; + TriggerGracefulShutdown(); + } + + /// + /// Constructor + /// + protected ConsoleServiceBase() + { + // Register self commands + + RegisterCommandHandler((args, canConsumeAll) => CommandToken.ReadString(args, canConsumeAll) ?? ""); + + RegisterCommandHandler((args, canConsumeAll) => + { + if (canConsumeAll) + { + var ret = CommandToken.ToString(args); + args.Clear(); + return ret.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); + } + + return (CommandToken.ReadString(args, false)?.Split(',', ' ')) ?? Array.Empty(); + }); + + RegisterCommandHandler(false, str => byte.Parse(str)); + RegisterCommandHandler(false, str => str == "1" || str == "yes" || str == "y" || bool.Parse(str)); + RegisterCommandHandler(false, str => ushort.Parse(str)); + RegisterCommandHandler(false, str => uint.Parse(str)); + RegisterCommandHandler(false, IPAddress.Parse); + } + + /// + /// Register command handler + /// + /// Return type + /// Handler + private void RegisterCommandHandler(Func, bool, object> handler) + { + _handlers[typeof(TRet)] = handler; + } + + /// + /// Register command handler + /// + /// Base type + /// Return type + /// Can consume all + /// Handler + public void RegisterCommandHandler(bool canConsumeAll, Func handler) + { + _handlers[typeof(TRet)] = (args, _) => + { + var value = (T)_handlers[typeof(T)](args, canConsumeAll); + return handler(value); + }; + } + + /// + /// Register command handler + /// + /// Base type + /// Return type + /// Handler + public void RegisterCommandHandler(Func handler) + { + _handlers[typeof(TRet)] = (args, consumeAll) => + { + var value = (T)_handlers[typeof(T)](args, consumeAll); + return handler(value); + }; + } + + /// + /// Register commands + /// + /// Instance + /// Name + public void RegisterCommand(object instance, string? name = null) + { + if (!string.IsNullOrEmpty(name)) + { + _instances.Add(name.ToLowerInvariant(), instance); + } + + foreach (var method in instance.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) + { + foreach (var attribute in method.GetCustomAttributes()) + { + // Check handlers + + if (!method.GetParameters().All(u => u.ParameterType.IsEnum || _handlers.ContainsKey(u.ParameterType))) + { + throw new ArgumentException("Handler not found for the command: " + method); + } + + // Add command + + var command = new ConsoleCommandMethod(instance, method, attribute); + + if (!_verbs.TryGetValue(command.Key, out var commands)) + { + _verbs.Add(command.Key, new List(new[] { command })); + } + else + { + commands.Add(command); + } + } + } + } + + public void Run(string[] args) + { + if (Environment.UserInteractive) + { + if (args.Length == 1 && args[0] == "/install") + { + if (Environment.OSVersion.Platform != PlatformID.Win32NT) + { + ConsoleHelper.Warning("Only support for installing services on Windows."); + return; + } + string arguments = string.Format("create {0} start= auto binPath= \"{1}\"", ServiceName, Process.GetCurrentProcess().MainModule!.FileName); + if (!string.IsNullOrEmpty(Depends)) + { + arguments += string.Format(" depend= {0}", Depends); + } + Process? process = Process.Start(new ProcessStartInfo + { + Arguments = arguments, + FileName = Path.Combine(Environment.SystemDirectory, "sc.exe"), + RedirectStandardOutput = true, + UseShellExecute = false + }); + if (process is null) + { + ConsoleHelper.Error("Error installing the service with sc.exe."); + } + else + { + process.WaitForExit(); + Console.Write(process.StandardOutput.ReadToEnd()); + } + } + else if (args.Length == 1 && args[0] == "/uninstall") + { + if (Environment.OSVersion.Platform != PlatformID.Win32NT) + { + ConsoleHelper.Warning("Only support for installing services on Windows."); + return; + } + Process? process = Process.Start(new ProcessStartInfo + { + Arguments = string.Format("delete {0}", ServiceName), + FileName = Path.Combine(Environment.SystemDirectory, "sc.exe"), + RedirectStandardOutput = true, + UseShellExecute = false + }); + if (process is null) + { + ConsoleHelper.Error("Error installing the service with sc.exe."); + } + else + { + process.WaitForExit(); + Console.Write(process.StandardOutput.ReadToEnd()); + } + } + else + { + if (OnStart(args)) RunConsole(); + OnStop(); + } + } + else + { + Debug.Assert(Environment.OSVersion.Platform == PlatformID.Win32NT); +#pragma warning disable CA1416 + ServiceBase.Run(new ServiceProxy(this)); +#pragma warning restore CA1416 + } + } + + protected string? ReadLine() + { + Task readLineTask = Task.Run(Console.ReadLine); + + try + { + readLineTask.Wait(_shutdownTokenSource.Token); + } + catch (OperationCanceledException) + { + return null; + } + + return readLineTask.Result; + } + + public virtual void RunConsole() + { + _running = true; + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + try + { + Console.Title = ServiceName; + } + catch { } + + Console.ForegroundColor = ConsoleColor.DarkGreen; + Console.SetIn(new StreamReader(Console.OpenStandardInput(), Console.InputEncoding, false, ushort.MaxValue)); + + while (_running) + { + if (ShowPrompt) + { + Console.ForegroundColor = ConsoleColor.Green; + Console.Write($"{Prompt}> "); + } + + Console.ForegroundColor = ConsoleColor.Yellow; + string? line = ReadLine()?.Trim(); + if (line == null) break; + Console.ForegroundColor = ConsoleColor.White; + + try + { + if (!OnCommand(line)) + { + ConsoleHelper.Error("Command not found"); + } + } + catch (TargetInvocationException ex) when (ex.InnerException is not null) + { + ConsoleHelper.Error(ex.InnerException.Message); + } + catch (Exception ex) + { + ConsoleHelper.Error(ex.Message); + } + } + + Console.ResetColor(); + } + } +} diff --git a/src/Neo.ConsoleService/Neo.ConsoleService.csproj b/src/Neo.ConsoleService/Neo.ConsoleService.csproj new file mode 100644 index 0000000000..0139d2a0f5 --- /dev/null +++ b/src/Neo.ConsoleService/Neo.ConsoleService.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.1;net7.0 + Neo.ConsoleService + enable + + + + + + + + diff --git a/src/Neo.ConsoleService/Properties/AssemblyInfo.cs b/src/Neo.ConsoleService/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..efa7107713 --- /dev/null +++ b/src/Neo.ConsoleService/Properties/AssemblyInfo.cs @@ -0,0 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// AssemblyInfo.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.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Neo.ConsoleService.Tests")] diff --git a/src/Neo.ConsoleService/ServiceProxy.cs b/src/Neo.ConsoleService/ServiceProxy.cs new file mode 100644 index 0000000000..97a14601c7 --- /dev/null +++ b/src/Neo.ConsoleService/ServiceProxy.cs @@ -0,0 +1,35 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ServiceProxy.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.ServiceProcess; + +namespace Neo.ConsoleService +{ + internal class ServiceProxy : ServiceBase + { + private readonly ConsoleServiceBase _service; + + public ServiceProxy(ConsoleServiceBase service) + { + this._service = service; + } + + protected override void OnStart(string[] args) + { + _service.OnStart(args); + } + + protected override void OnStop() + { + _service.OnStop(); + } + } +} diff --git a/src/Neo.Cryptography.BLS12_381/Bls12.Adder.cs b/src/Neo.Cryptography.BLS12_381/Bls12.Adder.cs new file mode 100644 index 0000000000..3981f19a1b --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/Bls12.Adder.cs @@ -0,0 +1,71 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Bls12.Adder.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.Runtime.CompilerServices; +using static Neo.Cryptography.BLS12_381.MillerLoopUtility; + +namespace Neo.Cryptography.BLS12_381; + +partial class Bls12 +{ + class Adder : IMillerLoopDriver + { + public G2Projective Curve; + public readonly G2Affine Base; + public readonly G1Affine P; + + public Adder(in G1Affine p, in G2Affine q) + { + Curve = new(q); + Base = q; + P = p; + } + + Fp12 IMillerLoopDriver.DoublingStep(in Fp12 f) + { + var coeffs = DoublingStep(ref Curve); + return Ell(in f, in coeffs, in P); + } + + Fp12 IMillerLoopDriver.AdditionStep(in Fp12 f) + { + var coeffs = AdditionStep(ref Curve, in Base); + return Ell(in f, in coeffs, in P); + } + + #region IMillerLoopDriver + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fp12 Square(in Fp12 f) => f.Square(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fp12 Conjugate(in Fp12 f) => f.Conjugate(); + + public static Fp12 One + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Fp12.One; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + Fp12 IMillerLoopDriver.Square(in Fp12 f) => Adder.Square(f); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + Fp12 IMillerLoopDriver.Conjugate(in Fp12 f) => Adder.Conjugate(f); + Fp12 IMillerLoopDriver.One + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Adder.One; + } + + #endregion + } +} diff --git a/src/Neo.Cryptography.BLS12_381/Bls12.cs b/src/Neo.Cryptography.BLS12_381/Bls12.cs new file mode 100644 index 0000000000..10022ded4d --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/Bls12.cs @@ -0,0 +1,31 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Bls12.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 static Neo.Cryptography.BLS12_381.ConstantTimeUtility; +using static Neo.Cryptography.BLS12_381.MillerLoopUtility; + +namespace Neo.Cryptography.BLS12_381; + +public static partial class Bls12 +{ + public static Gt Pairing(in G1Affine p, in G2Affine q) + { + var either_identity = p.IsIdentity | q.IsIdentity; + var p2 = ConditionalSelect(in p, in G1Affine.Generator, either_identity); + var q2 = ConditionalSelect(in q, in G2Affine.Generator, either_identity); + + var adder = new Adder(p2, q2); + + var tmp = MillerLoop(adder); + var tmp2 = new MillerLoopResult(ConditionalSelect(in tmp, in Fp12.One, either_identity)); + return tmp2.FinalExponentiation(); + } +} diff --git a/src/Neo.Cryptography.BLS12_381/ConstantTimeUtility.cs b/src/Neo.Cryptography.BLS12_381/ConstantTimeUtility.cs new file mode 100644 index 0000000000..70517edeb1 --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/ConstantTimeUtility.cs @@ -0,0 +1,42 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ConstantTimeUtility.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.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Neo.Cryptography.BLS12_381; + +public static class ConstantTimeUtility +{ + public static bool ConstantTimeEq(in T a, in T b) where T : unmanaged + { + ReadOnlySpan a_bytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in a), 1)); + ReadOnlySpan b_bytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in b), 1)); + ReadOnlySpan a_u64 = MemoryMarshal.Cast(a_bytes); + ReadOnlySpan b_u64 = MemoryMarshal.Cast(b_bytes); + ulong f = 0; + for (int i = 0; i < a_u64.Length; i++) + f |= a_u64[i] ^ b_u64[i]; + for (int i = a_u64.Length * sizeof(ulong); i < a_bytes.Length; i++) + f |= (ulong)a_bytes[i] ^ a_bytes[i]; + return f == 0; + } + + public static T ConditionalSelect(in T a, in T b, bool choice) where T : unmanaged + { + return choice ? b : a; + } + + public static void ConditionalAssign(this ref T self, in T other, bool choice) where T : unmanaged + { + self = ConditionalSelect(in self, in other, choice); + } +} diff --git a/src/Neo.Cryptography.BLS12_381/Constants.cs b/src/Neo.Cryptography.BLS12_381/Constants.cs new file mode 100644 index 0000000000..457b4d720b --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/Constants.cs @@ -0,0 +1,18 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Constants.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.Cryptography.BLS12_381; + +static class Constants +{ + public const ulong BLS_X = 0xd201_0000_0001_0000; + public const bool BLS_X_IS_NEGATIVE = true; +} diff --git a/src/Neo.Cryptography.BLS12_381/Fp.cs b/src/Neo.Cryptography.BLS12_381/Fp.cs new file mode 100644 index 0000000000..4b30b4fa5c --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/Fp.cs @@ -0,0 +1,491 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Fp.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.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; +using static Neo.Cryptography.BLS12_381.FpConstants; +using static Neo.Cryptography.BLS12_381.MathUtility; + +namespace Neo.Cryptography.BLS12_381; + +[StructLayout(LayoutKind.Explicit, Size = Size)] +public readonly struct Fp : IEquatable, INumber +{ + public const int Size = 48; + public const int SizeL = Size / sizeof(ulong); + + private static readonly Fp _zero = new(); + + public static ref readonly Fp Zero => ref _zero; + public static ref readonly Fp One => ref R; + + public bool IsZero => this == Zero; + + public static Fp FromBytes(ReadOnlySpan data) + { + if (data.Length != Size) + throw new FormatException($"The argument `{nameof(data)}` must contain {Size} bytes."); + + Span tmp = stackalloc ulong[SizeL]; + BinaryPrimitives.TryReadUInt64BigEndian(data[0..8], out tmp[5]); + BinaryPrimitives.TryReadUInt64BigEndian(data[8..16], out tmp[4]); + BinaryPrimitives.TryReadUInt64BigEndian(data[16..24], out tmp[3]); + BinaryPrimitives.TryReadUInt64BigEndian(data[24..32], out tmp[2]); + BinaryPrimitives.TryReadUInt64BigEndian(data[32..40], out tmp[1]); + BinaryPrimitives.TryReadUInt64BigEndian(data[40..48], out tmp[0]); + ReadOnlySpan span = MemoryMarshal.Cast(tmp); + + try + { + return span[0] * R2; + } + finally + { + ulong borrow; + (_, borrow) = Sbb(tmp[0], MODULUS[0], 0); + (_, borrow) = Sbb(tmp[1], MODULUS[1], borrow); + (_, borrow) = Sbb(tmp[2], MODULUS[2], borrow); + (_, borrow) = Sbb(tmp[3], MODULUS[3], borrow); + (_, borrow) = Sbb(tmp[4], MODULUS[4], borrow); + (_, borrow) = Sbb(tmp[5], MODULUS[5], borrow); + if (borrow == 0) + { + // If the element is smaller than MODULUS then the subtraction will underflow. + // Otherwise, throws. + // Why not throw before return? + // Because we want to run the method in a constant time. + throw new FormatException(); + } + } + } + + internal static Fp FromRawUnchecked(ulong[] values) + { + if (values.Length != SizeL) + throw new FormatException($"The argument `{nameof(values)}` must contain {SizeL} entries."); + + return MemoryMarshal.Cast(values)[0]; + } + + public static Fp Random(RandomNumberGenerator rng) + { + Span buffer = stackalloc byte[Size * 2]; + rng.GetBytes(buffer); + Span d = MemoryMarshal.Cast(buffer); + return d[0] * R2 + d[1] * R3; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ReadOnlySpan GetSpan() + { + return MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in this), 1)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Span GetSpanU64() + { + return MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref Unsafe.AsRef(in this), 1)); + } + + public static bool operator ==(in Fp left, in Fp right) + { + return ConstantTimeEq(in left, in right); + } + + public static bool operator !=(in Fp left, in Fp right) + { + return !(left == right); + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not Fp other) return false; + return this == other; + } + + public bool Equals(Fp other) + { + return this == other; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public byte[] ToArray() + { + byte[] result = new byte[Size]; + TryWrite(result); + return result; + } + + public bool TryWrite(Span buffer) + { + if (buffer.Length < Size) return false; + + ReadOnlySpan u64 = GetSpanU64(); + Fp tmp = MontgomeryReduce(u64[0], u64[1], u64[2], u64[3], u64[4], u64[5], 0, 0, 0, 0, 0, 0); + u64 = tmp.GetSpanU64(); + + BinaryPrimitives.WriteUInt64BigEndian(buffer[0..8], u64[5]); + BinaryPrimitives.WriteUInt64BigEndian(buffer[8..16], u64[4]); + BinaryPrimitives.WriteUInt64BigEndian(buffer[16..24], u64[3]); + BinaryPrimitives.WriteUInt64BigEndian(buffer[24..32], u64[2]); + BinaryPrimitives.WriteUInt64BigEndian(buffer[32..40], u64[1]); + BinaryPrimitives.WriteUInt64BigEndian(buffer[40..48], u64[0]); + + return true; + } + + public override string ToString() + { + var output = string.Empty; + foreach (var b in ToArray()) + output += b.ToString("x2"); + + return "0x" + output; + } + + public bool LexicographicallyLargest() + { + ReadOnlySpan s = GetSpanU64(); + Fp tmp = MontgomeryReduce(s[0], s[1], s[2], s[3], s[4], s[5], 0, 0, 0, 0, 0, 0); + ReadOnlySpan t = tmp.GetSpanU64(); + ulong borrow; + + (_, borrow) = Sbb(t[0], 0xdcff_7fff_ffff_d556, 0); + (_, borrow) = Sbb(t[1], 0x0f55_ffff_58a9_ffff, borrow); + (_, borrow) = Sbb(t[2], 0xb398_6950_7b58_7b12, borrow); + (_, borrow) = Sbb(t[3], 0xb23b_a5c2_79c2_895f, borrow); + (_, borrow) = Sbb(t[4], 0x258d_d3db_21a5_d66b, borrow); + (_, borrow) = Sbb(t[5], 0x0d00_88f5_1cbf_f34d, borrow); + + return borrow == 0; + } + + public Fp Sqrt() + { + // We use Shank's method, as p = 3 (mod 4). This means + // we only need to exponentiate by (p + 1) / 4. This only + // works for elements that are actually quadratic residue, + // so we check that we got the correct result at the end. + Fp result = this.PowVartime(P_1_4); + if (result.Square() != this) throw new ArithmeticException(); + return result; + } + + public Fp Invert() + { + if (!TryInvert(out Fp result)) + throw new DivideByZeroException(); + return result; + } + + public bool TryInvert(out Fp result) + { + // Exponentiate by p - 2 + result = this.PowVartime(P_2); + + // Why not return before Pow() if IsZero? + // Because we want to run the method in a constant time. + return !IsZero; + } + + private Fp SubtractP() + { + Fp result; + ReadOnlySpan s = GetSpanU64(); + Span r = result.GetSpanU64(); + ulong borrow; + + (r[0], borrow) = Sbb(s[0], MODULUS[0], 0); + (r[1], borrow) = Sbb(s[1], MODULUS[1], borrow); + (r[2], borrow) = Sbb(s[2], MODULUS[2], borrow); + (r[3], borrow) = Sbb(s[3], MODULUS[3], borrow); + (r[4], borrow) = Sbb(s[4], MODULUS[4], borrow); + (r[5], borrow) = Sbb(s[5], MODULUS[5], borrow); + + borrow = borrow == 0 ? ulong.MinValue : ulong.MaxValue; + r[0] = (s[0] & borrow) | (r[0] & ~borrow); + r[1] = (s[1] & borrow) | (r[1] & ~borrow); + r[2] = (s[2] & borrow) | (r[2] & ~borrow); + r[3] = (s[3] & borrow) | (r[3] & ~borrow); + r[4] = (s[4] & borrow) | (r[4] & ~borrow); + r[5] = (s[5] & borrow) | (r[5] & ~borrow); + + return result; + } + + public static Fp operator +(in Fp a, in Fp b) + { + Fp result; + ReadOnlySpan s = a.GetSpanU64(), r = b.GetSpanU64(); + Span d = result.GetSpanU64(); + + ulong carry = 0; + (d[0], carry) = Adc(s[0], r[0], carry); + (d[1], carry) = Adc(s[1], r[1], carry); + (d[2], carry) = Adc(s[2], r[2], carry); + (d[3], carry) = Adc(s[3], r[3], carry); + (d[4], carry) = Adc(s[4], r[4], carry); + (d[5], _) = Adc(s[5], r[5], carry); + + return result.SubtractP(); + } + + public static Fp operator -(in Fp a) + { + Fp result; + ReadOnlySpan self = a.GetSpanU64(); + Span d = result.GetSpanU64(); + + ulong borrow = 0; + (d[0], borrow) = Sbb(MODULUS[0], self[0], borrow); + (d[1], borrow) = Sbb(MODULUS[1], self[1], borrow); + (d[2], borrow) = Sbb(MODULUS[2], self[2], borrow); + (d[3], borrow) = Sbb(MODULUS[3], self[3], borrow); + (d[4], borrow) = Sbb(MODULUS[4], self[4], borrow); + (d[5], _) = Sbb(MODULUS[5], self[5], borrow); + + ulong mask = a.IsZero ? ulong.MinValue : ulong.MaxValue; + d[0] &= mask; + d[1] &= mask; + d[2] &= mask; + d[3] &= mask; + d[4] &= mask; + d[5] &= mask; + + return result; + } + + public static Fp operator -(in Fp a, in Fp b) + { + return -b + a; + } + + public static Fp SumOfProducts(ReadOnlySpan a, ReadOnlySpan b) + { + int length = a.Length; + if (length != b.Length) + throw new ArgumentException("The lengths of the two arrays must be the same."); + + Fp result; + ReadOnlySpan au = MemoryMarshal.Cast(a); + ReadOnlySpan bu = MemoryMarshal.Cast(b); + Span u = result.GetSpanU64(); + + for (int j = 0; j < 6; j++) + { + ulong carry; + + var (t0, t1, t2, t3, t4, t5, t6) = (u[0], u[1], u[2], u[3], u[4], u[5], 0ul); + for (int i = 0; i < length; i++) + { + (t0, carry) = Mac(t0, au[i * SizeL + j], bu[i * SizeL + 0], 0); + (t1, carry) = Mac(t1, au[i * SizeL + j], bu[i * SizeL + 1], carry); + (t2, carry) = Mac(t2, au[i * SizeL + j], bu[i * SizeL + 2], carry); + (t3, carry) = Mac(t3, au[i * SizeL + j], bu[i * SizeL + 3], carry); + (t4, carry) = Mac(t4, au[i * SizeL + j], bu[i * SizeL + 4], carry); + (t5, carry) = Mac(t5, au[i * SizeL + j], bu[i * SizeL + 5], carry); + (t6, _) = Adc(t6, 0, carry); + } + + ulong k = unchecked(t0 * INV); + (_, carry) = Mac(t0, k, MODULUS[0], 0); + (u[0], carry) = Mac(t1, k, MODULUS[1], carry); + (u[1], carry) = Mac(t2, k, MODULUS[2], carry); + (u[2], carry) = Mac(t3, k, MODULUS[3], carry); + (u[3], carry) = Mac(t4, k, MODULUS[4], carry); + (u[4], carry) = Mac(t5, k, MODULUS[5], carry); + (u[5], _) = Adc(t6, 0, carry); + } + + return result.SubtractP(); + } + + private static Fp MontgomeryReduce(ulong r0, ulong r1, ulong r2, ulong r3, ulong r4, ulong r5, ulong r6, ulong r7, ulong r8, ulong r9, ulong r10, ulong r11) + { + ulong carry, carry2; + + ulong k = unchecked(r0 * INV); + (_, carry) = Mac(r0, k, MODULUS[0], 0); + (r1, carry) = Mac(r1, k, MODULUS[1], carry); + (r2, carry) = Mac(r2, k, MODULUS[2], carry); + (r3, carry) = Mac(r3, k, MODULUS[3], carry); + (r4, carry) = Mac(r4, k, MODULUS[4], carry); + (r5, carry) = Mac(r5, k, MODULUS[5], carry); + (r6, carry2) = Adc(r6, 0, carry); + + k = unchecked(r1 * INV); + (_, carry) = Mac(r1, k, MODULUS[0], 0); + (r2, carry) = Mac(r2, k, MODULUS[1], carry); + (r3, carry) = Mac(r3, k, MODULUS[2], carry); + (r4, carry) = Mac(r4, k, MODULUS[3], carry); + (r5, carry) = Mac(r5, k, MODULUS[4], carry); + (r6, carry) = Mac(r6, k, MODULUS[5], carry); + (r7, carry2) = Adc(r7, carry2, carry); + + k = unchecked(r2 * INV); + (_, carry) = Mac(r2, k, MODULUS[0], 0); + (r3, carry) = Mac(r3, k, MODULUS[1], carry); + (r4, carry) = Mac(r4, k, MODULUS[2], carry); + (r5, carry) = Mac(r5, k, MODULUS[3], carry); + (r6, carry) = Mac(r6, k, MODULUS[4], carry); + (r7, carry) = Mac(r7, k, MODULUS[5], carry); + (r8, carry2) = Adc(r8, carry2, carry); + + k = unchecked(r3 * INV); + (_, carry) = Mac(r3, k, MODULUS[0], 0); + (r4, carry) = Mac(r4, k, MODULUS[1], carry); + (r5, carry) = Mac(r5, k, MODULUS[2], carry); + (r6, carry) = Mac(r6, k, MODULUS[3], carry); + (r7, carry) = Mac(r7, k, MODULUS[4], carry); + (r8, carry) = Mac(r8, k, MODULUS[5], carry); + (r9, carry2) = Adc(r9, carry2, carry); + + k = unchecked(r4 * INV); + (_, carry) = Mac(r4, k, MODULUS[0], 0); + (r5, carry) = Mac(r5, k, MODULUS[1], carry); + (r6, carry) = Mac(r6, k, MODULUS[2], carry); + (r7, carry) = Mac(r7, k, MODULUS[3], carry); + (r8, carry) = Mac(r8, k, MODULUS[4], carry); + (r9, carry) = Mac(r9, k, MODULUS[5], carry); + (r10, carry2) = Adc(r10, carry2, carry); + + k = unchecked(r5 * INV); + (_, carry) = Mac(r5, k, MODULUS[0], 0); + (r6, carry) = Mac(r6, k, MODULUS[1], carry); + (r7, carry) = Mac(r7, k, MODULUS[2], carry); + (r8, carry) = Mac(r8, k, MODULUS[3], carry); + (r9, carry) = Mac(r9, k, MODULUS[4], carry); + (r10, carry) = Mac(r10, k, MODULUS[5], carry); + (r11, _) = Adc(r11, carry2, carry); + + ReadOnlySpan tmp = stackalloc[] { r6, r7, r8, r9, r10, r11 }; + return MemoryMarshal.Cast(tmp)[0].SubtractP(); + } + + public static Fp operator *(in Fp a, in Fp b) + { + ReadOnlySpan s = a.GetSpanU64(), r = b.GetSpanU64(); + Span t = stackalloc ulong[SizeL * 2]; + ulong carry; + + (t[0], carry) = Mac(0, s[0], r[0], 0); + (t[1], carry) = Mac(0, s[0], r[1], carry); + (t[2], carry) = Mac(0, s[0], r[2], carry); + (t[3], carry) = Mac(0, s[0], r[3], carry); + (t[4], carry) = Mac(0, s[0], r[4], carry); + (t[5], t[6]) = Mac(0, s[0], r[5], carry); + + (t[1], carry) = Mac(t[1], s[1], r[0], 0); + (t[2], carry) = Mac(t[2], s[1], r[1], carry); + (t[3], carry) = Mac(t[3], s[1], r[2], carry); + (t[4], carry) = Mac(t[4], s[1], r[3], carry); + (t[5], carry) = Mac(t[5], s[1], r[4], carry); + (t[6], t[7]) = Mac(t[6], s[1], r[5], carry); + + (t[2], carry) = Mac(t[2], s[2], r[0], 0); + (t[3], carry) = Mac(t[3], s[2], r[1], carry); + (t[4], carry) = Mac(t[4], s[2], r[2], carry); + (t[5], carry) = Mac(t[5], s[2], r[3], carry); + (t[6], carry) = Mac(t[6], s[2], r[4], carry); + (t[7], t[8]) = Mac(t[7], s[2], r[5], carry); + (t[3], carry) = Mac(t[3], s[3], r[0], 0); + (t[4], carry) = Mac(t[4], s[3], r[1], carry); + (t[5], carry) = Mac(t[5], s[3], r[2], carry); + (t[6], carry) = Mac(t[6], s[3], r[3], carry); + (t[7], carry) = Mac(t[7], s[3], r[4], carry); + (t[8], t[9]) = Mac(t[8], s[3], r[5], carry); + (t[4], carry) = Mac(t[4], s[4], r[0], 0); + (t[5], carry) = Mac(t[5], s[4], r[1], carry); + (t[6], carry) = Mac(t[6], s[4], r[2], carry); + (t[7], carry) = Mac(t[7], s[4], r[3], carry); + (t[8], carry) = Mac(t[8], s[4], r[4], carry); + (t[9], t[10]) = Mac(t[9], s[4], r[5], carry); + (t[5], carry) = Mac(t[5], s[5], r[0], 0); + (t[6], carry) = Mac(t[6], s[5], r[1], carry); + (t[7], carry) = Mac(t[7], s[5], r[2], carry); + (t[8], carry) = Mac(t[8], s[5], r[3], carry); + (t[9], carry) = Mac(t[9], s[5], r[4], carry); + (t[10], t[11]) = Mac(t[10], s[5], r[5], carry); + + return MontgomeryReduce(t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8], t[9], t[10], t[11]); + } + + public Fp Square() + { + ReadOnlySpan self = GetSpanU64(); + ulong t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11; + ulong carry; + + (t1, carry) = Mac(0, self[0], self[1], 0); + (t2, carry) = Mac(0, self[0], self[2], carry); + (t3, carry) = Mac(0, self[0], self[3], carry); + (t4, carry) = Mac(0, self[0], self[4], carry); + (t5, t6) = Mac(0, self[0], self[5], carry); + + (t3, carry) = Mac(t3, self[1], self[2], 0); + (t4, carry) = Mac(t4, self[1], self[3], carry); + (t5, carry) = Mac(t5, self[1], self[4], carry); + (t6, t7) = Mac(t6, self[1], self[5], carry); + + (t5, carry) = Mac(t5, self[2], self[3], 0); + (t6, carry) = Mac(t6, self[2], self[4], carry); + (t7, t8) = Mac(t7, self[2], self[5], carry); + + (t7, carry) = Mac(t7, self[3], self[4], 0); + (t8, t9) = Mac(t8, self[3], self[5], carry); + + (t9, t10) = Mac(t9, self[4], self[5], 0); + + t11 = t10 >> 63; + t10 = (t10 << 1) | (t9 >> 63); + t9 = (t9 << 1) | (t8 >> 63); + t8 = (t8 << 1) | (t7 >> 63); + t7 = (t7 << 1) | (t6 >> 63); + t6 = (t6 << 1) | (t5 >> 63); + t5 = (t5 << 1) | (t4 >> 63); + t4 = (t4 << 1) | (t3 >> 63); + t3 = (t3 << 1) | (t2 >> 63); + t2 = (t2 << 1) | (t1 >> 63); + t1 <<= 1; + + (t0, carry) = Mac(0, self[0], self[0], 0); + (t1, carry) = Adc(t1, carry, 0); + (t2, carry) = Mac(t2, self[1], self[1], carry); + (t3, carry) = Adc(t3, carry, 0); + (t4, carry) = Mac(t4, self[2], self[2], carry); + (t5, carry) = Adc(t5, carry, 0); + (t6, carry) = Mac(t6, self[3], self[3], carry); + (t7, carry) = Adc(t7, carry, 0); + (t8, carry) = Mac(t8, self[4], self[4], carry); + (t9, carry) = Adc(t9, carry, 0); + (t10, carry) = Mac(t10, self[5], self[5], carry); + (t11, _) = Adc(t11, carry, 0); + + return MontgomeryReduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11); + } + + #region Instance math methods + + public Fp Negate() => -this; + public Fp Multiply(in Fp value) => this * value; + public Fp Sum(in Fp value) => this + value; + public Fp Subtract(in Fp value) => this - value; + + #endregion +} diff --git a/src/Neo.Cryptography.BLS12_381/Fp12.cs b/src/Neo.Cryptography.BLS12_381/Fp12.cs new file mode 100644 index 0000000000..2ed7ee988a --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/Fp12.cs @@ -0,0 +1,218 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Fp12.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.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Security.Cryptography; + +namespace Neo.Cryptography.BLS12_381; + +[StructLayout(LayoutKind.Explicit, Size = Size)] +public readonly struct Fp12 : IEquatable, INumber +{ + [FieldOffset(0)] + public readonly Fp6 C0; + [FieldOffset(Fp6.Size)] + public readonly Fp6 C1; + + public const int Size = Fp6.Size * 2; + + private static readonly Fp12 _zero = new(); + private static readonly Fp12 _one = new(in Fp6.One); + + public static ref readonly Fp12 Zero => ref _zero; + public static ref readonly Fp12 One => ref _one; + + public bool IsZero => C0.IsZero & C1.IsZero; + + public Fp12(in Fp f) + : this(new Fp6(in f), in Fp6.Zero) + { + } + + public Fp12(in Fp2 f) + : this(new Fp6(in f), in Fp6.Zero) + { + } + + public Fp12(in Fp6 f) + : this(in f, in Fp6.Zero) + { + } + + public Fp12(in Fp6 c0, in Fp6 c1) + { + C0 = c0; + C1 = c1; + } + + public static Fp12 FromBytes(ReadOnlySpan data) + { + if (data.Length != Size) + throw new FormatException($"The argument `{nameof(data)}` must contain {Size} bytes."); + Fp6 c0 = Fp6.FromBytes(data[Fp6.Size..]); + Fp6 c1 = Fp6.FromBytes(data[..Fp6.Size]); + return new(in c0, in c1); + } + + public static bool operator ==(in Fp12 a, in Fp12 b) + { + return a.C0 == b.C0 & a.C1 == b.C1; + } + + public static bool operator !=(in Fp12 a, in Fp12 b) + { + return !(a == b); + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not Fp12 other) return false; + return this == other; + } + + public bool Equals(Fp12 other) + { + return this == other; + } + + public override int GetHashCode() + { + return C0.GetHashCode() ^ C1.GetHashCode(); + } + + public byte[] ToArray() + { + byte[] result = new byte[Size]; + TryWrite(result); + return result; + } + + public bool TryWrite(Span buffer) + { + if (buffer.Length < Size) return false; + C0.TryWrite(buffer[Fp6.Size..Size]); + C1.TryWrite(buffer[0..Fp6.Size]); + return true; + } + + public static Fp12 Random(RandomNumberGenerator rng) + { + return new(Fp6.Random(rng), Fp6.Random(rng)); + } + + internal Fp12 MulBy_014(in Fp2 c0, in Fp2 c1, in Fp2 c4) + { + var aa = C0.MulBy_01(in c0, in c1); + var bb = C1.MulBy_1(in c4); + var o = c1 + c4; + var _c1 = C1 + C0; + _c1 = _c1.MulBy_01(in c0, in o); + _c1 = _c1 - aa - bb; + var _c0 = bb; + _c0 = _c0.MulByNonresidue(); + _c0 += aa; + + return new Fp12(in _c0, in _c1); + } + + public Fp12 Conjugate() + { + return new Fp12(in C0, -C1); + } + + public Fp12 FrobeniusMap() + { + var c0 = C0.FrobeniusMap(); + var c1 = C1.FrobeniusMap(); + + // c1 = c1 * (u + 1)^((p - 1) / 6) + c1 *= new Fp6(new Fp2( + Fp.FromRawUnchecked(new ulong[] + { + 0x0708_9552_b319_d465, + 0xc669_5f92_b50a_8313, + 0x97e8_3ccc_d117_228f, + 0xa35b_aeca_b2dc_29ee, + 0x1ce3_93ea_5daa_ce4d, + 0x08f2_220f_b0fb_66eb + }), Fp.FromRawUnchecked(new ulong[] + { + 0xb2f6_6aad_4ce5_d646, + 0x5842_a06b_fc49_7cec, + 0xcf48_95d4_2599_d394, + 0xc11b_9cba_40a8_e8d0, + 0x2e38_13cb_e5a0_de89, + 0x110e_efda_8884_7faf + }))); + + return new Fp12(in c0, in c1); + } + + public Fp12 Square() + { + var ab = C0 * C1; + var c0c1 = C0 + C1; + var c0 = C1.MulByNonresidue(); + c0 += C0; + c0 *= c0c1; + c0 -= ab; + var c1 = ab + ab; + c0 -= ab.MulByNonresidue(); + + return new Fp12(in c0, in c1); + } + + public Fp12 Invert() + { + Fp6 t = (C0.Square() - C1.Square().MulByNonresidue()).Invert(); + return new Fp12(C0 * t, C1 * -t); + } + + public static Fp12 operator -(in Fp12 a) + { + return new Fp12(-a.C0, -a.C1); + } + + public static Fp12 operator +(in Fp12 a, in Fp12 b) + { + return new Fp12(a.C0 + b.C0, a.C1 + b.C1); + } + + public static Fp12 operator -(in Fp12 a, in Fp12 b) + { + return new Fp12(a.C0 - b.C0, a.C1 - b.C1); + } + + public static Fp12 operator *(in Fp12 a, in Fp12 b) + { + var aa = a.C0 * b.C0; + var bb = a.C1 * b.C1; + var o = b.C0 + b.C1; + var c1 = a.C1 + a.C0; + c1 *= o; + c1 -= aa; + c1 -= bb; + var c0 = bb.MulByNonresidue(); + c0 += aa; + + return new Fp12(in c0, in c1); + } + + #region Instance math methods + + public Fp12 Negate() => -this; + public Fp12 Multiply(in Fp12 value) => this * value; + public Fp12 Sum(in Fp12 value) => this + value; + public Fp12 Subtract(in Fp12 value) => this - value; + + #endregion +} diff --git a/src/Neo.Cryptography.BLS12_381/Fp2.cs b/src/Neo.Cryptography.BLS12_381/Fp2.cs new file mode 100644 index 0000000000..4ca3830970 --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/Fp2.cs @@ -0,0 +1,274 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Fp2.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.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; + +namespace Neo.Cryptography.BLS12_381; + +[StructLayout(LayoutKind.Explicit, Size = Size)] +public readonly struct Fp2 : IEquatable, INumber +{ + [FieldOffset(0)] + public readonly Fp C0; + [FieldOffset(Fp.Size)] + public readonly Fp C1; + + public const int Size = Fp.Size * 2; + + private static readonly Fp2 _zero = new(); + private static readonly Fp2 _one = new(in Fp.One); + + public static ref readonly Fp2 Zero => ref _zero; + public static ref readonly Fp2 One => ref _one; + + public bool IsZero => C0.IsZero & C1.IsZero; + + public Fp2(in Fp f) + : this(in f, in Fp.Zero) + { + } + + public Fp2(in Fp c0, in Fp c1) + { + C0 = c0; + C1 = c1; + } + + public static Fp2 FromBytes(ReadOnlySpan data) + { + if (data.Length != Size) + throw new FormatException($"The argument `{nameof(data)}` must contain {Size} bytes."); + Fp c0 = Fp.FromBytes(data[Fp.Size..]); + Fp c1 = Fp.FromBytes(data[..Fp.Size]); + return new(in c0, in c1); + } + + public static Fp2 Random(RandomNumberGenerator rng) + { + return new(Fp.Random(rng), Fp.Random(rng)); + } + + public static bool operator ==(in Fp2 a, in Fp2 b) + { + return a.C0 == b.C0 & a.C1 == b.C1; + } + + public static bool operator !=(in Fp2 a, in Fp2 b) + { + return !(a == b); + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not Fp2 other) return false; + return this == other; + } + + public bool Equals(Fp2 other) + { + return this == other; + } + + public override int GetHashCode() + { + return C0.GetHashCode() ^ C1.GetHashCode(); + } + + public byte[] ToArray() + { + byte[] result = new byte[Size]; + TryWrite(result); + return result; + } + + public bool TryWrite(Span buffer) + { + if (buffer.Length < Size) return false; + C0.TryWrite(buffer[Fp.Size..Size]); + C1.TryWrite(buffer[0..Fp.Size]); + return true; + } + + public Fp2 FrobeniusMap() + { + // This is always just a conjugation. If you're curious why, here's + // an article about it: https://alicebob.cryptoland.net/the-frobenius-endomorphism-with-finite-fields/ + return Conjugate(); + } + + public Fp2 Conjugate() + { + return new(in C0, -C1); + } + + public Fp2 MulByNonresidue() + { + // Multiply a + bu by u + 1, getting + // au + a + bu^2 + bu + // and because u^2 = -1, we get + // (a - b) + (a + b)u + + return new(C0 - C1, C0 + C1); + } + + public bool LexicographicallyLargest() + { + // If this element's c1 coefficient is lexicographically largest + // then it is lexicographically largest. Otherwise, in the event + // the c1 coefficient is zero and the c0 coefficient is + // lexicographically largest, then this element is lexicographically + // largest. + + return C1.LexicographicallyLargest() | (C1.IsZero & C0.LexicographicallyLargest()); + } + + public Fp2 Square() + { + // Complex squaring: + // + // v0 = c0 * c1 + // c0' = (c0 + c1) * (c0 + \beta*c1) - v0 - \beta * v0 + // c1' = 2 * v0 + // + // In BLS12-381's F_{p^2}, our \beta is -1 so we + // can modify this formula: + // + // c0' = (c0 + c1) * (c0 - c1) + // c1' = 2 * c0 * c1 + + var a = C0 + C1; + var b = C0 - C1; + var c = C0 + C0; + + return new(a * b, c * C1); + } + + public static Fp2 operator *(in Fp2 a, in Fp2 b) + { + // F_{p^2} x F_{p^2} multiplication implemented with operand scanning (schoolbook) + // computes the result as: + // + // a·b = (a_0 b_0 + a_1 b_1 β) + (a_0 b_1 + a_1 b_0)i + // + // In BLS12-381's F_{p^2}, our β is -1, so the resulting F_{p^2} element is: + // + // c_0 = a_0 b_0 - a_1 b_1 + // c_1 = a_0 b_1 + a_1 b_0 + // + // Each of these is a "sum of products", which we can compute efficiently. + + return new( + Fp.SumOfProducts(stackalloc[] { a.C0, -a.C1 }, stackalloc[] { b.C0, b.C1 }), + Fp.SumOfProducts(stackalloc[] { a.C0, a.C1 }, stackalloc[] { b.C1, b.C0 }) + ); + } + + public static Fp2 operator +(in Fp2 a, in Fp2 b) + { + return new(a.C0 + b.C0, a.C1 + b.C1); + } + + public static Fp2 operator -(in Fp2 a, in Fp2 b) + { + return new(a.C0 - b.C0, a.C1 - b.C1); + } + + public static Fp2 operator -(in Fp2 a) + { + return new(-a.C0, -a.C1); + } + + public Fp2 Sqrt() + { + // Algorithm 9, https://eprint.iacr.org/2012/685.pdf + // with constant time modifications. + + // a1 = self^((p - 3) / 4) + var a1 = this.PowVartime(new ulong[] + { + 0xee7f_bfff_ffff_eaaa, + 0x07aa_ffff_ac54_ffff, + 0xd9cc_34a8_3dac_3d89, + 0xd91d_d2e1_3ce1_44af, + 0x92c6_e9ed_90d2_eb35, + 0x0680_447a_8e5f_f9a6 + }); + + // alpha = a1^2 * self = self^((p - 3) / 2 + 1) = self^((p - 1) / 2) + var alpha = a1.Square() * this; + + // x0 = self^((p + 1) / 4) + var x0 = a1 * this; + + // (1 + alpha)^((q - 1) // 2) * x0 + var sqrt = (alpha + One).PowVartime(new ulong[] { + 0xdcff_7fff_ffff_d555, + 0x0f55_ffff_58a9_ffff, + 0xb398_6950_7b58_7b12, + 0xb23b_a5c2_79c2_895f, + 0x258d_d3db_21a5_d66b, + 0x0d00_88f5_1cbf_f34d, + }) * x0; + + // In the event that alpha = -1, the element is order p - 1 and so + // we're just trying to get the square of an element of the subfield + // Fp. This is given by x0 * u, since u = sqrt(-1). Since the element + // x0 = a + bu has b = 0, the solution is therefore au. + sqrt = ConditionalSelect(in sqrt, new(-x0.C1, in x0.C0), alpha == -One); + + sqrt = ConditionalSelect(in sqrt, in Zero, IsZero); + + // Only return the result if it's really the square root (and so + // self is actually quadratic nonresidue) + if (sqrt.Square() != this) throw new ArithmeticException(); + return sqrt; + } + + public Fp2 Invert() + { + if (!TryInvert(out Fp2 result)) + throw new DivideByZeroException(); + return result; + } + + public bool TryInvert(out Fp2 result) + { + // We wish to find the multiplicative inverse of a nonzero + // element a + bu in Fp2. We leverage an identity + // + // (a + bu)(a - bu) = a^2 + b^2 + // + // which holds because u^2 = -1. This can be rewritten as + // + // (a + bu)(a - bu)/(a^2 + b^2) = 1 + // + // because a^2 + b^2 = 0 has no nonzero solutions for (a, b). + // This gives that (a - bu)/(a^2 + b^2) is the inverse + // of (a + bu). Importantly, this can be computing using + // only a single inversion in Fp. + + bool s = (C0.Square() + C1.Square()).TryInvert(out Fp t); + result = new Fp2(C0 * t, C1 * -t); + return s; + } + + #region Instance math methods + + public Fp2 Negate() => -this; + public Fp2 Multiply(in Fp2 value) => this * value; + public Fp2 Sum(in Fp2 value) => this + value; + public Fp2 Subtract(in Fp2 value) => this - value; + + #endregion +} diff --git a/src/Neo.Cryptography.BLS12_381/Fp6.cs b/src/Neo.Cryptography.BLS12_381/Fp6.cs new file mode 100644 index 0000000000..540bfc8a36 --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/Fp6.cs @@ -0,0 +1,308 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Fp6.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.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Security.Cryptography; + +namespace Neo.Cryptography.BLS12_381; + +[StructLayout(LayoutKind.Explicit, Size = Size)] +public readonly struct Fp6 : IEquatable, INumber +{ + [FieldOffset(0)] + public readonly Fp2 C0; + [FieldOffset(Fp2.Size)] + public readonly Fp2 C1; + [FieldOffset(Fp2.Size * 2)] + public readonly Fp2 C2; + + public const int Size = Fp2.Size * 3; + + private static readonly Fp6 _zero = new(); + private static readonly Fp6 _one = new(in Fp2.One); + + public static ref readonly Fp6 Zero => ref _zero; + public static ref readonly Fp6 One => ref _one; + + public bool IsZero => C0.IsZero & C1.IsZero & C2.IsZero; + + public Fp6(in Fp f) + : this(new Fp2(in f), in Fp2.Zero, in Fp2.Zero) + { + } + + public Fp6(in Fp2 f) + : this(in f, in Fp2.Zero, in Fp2.Zero) + { + } + + public Fp6(in Fp2 c0, in Fp2 c1, in Fp2 c2) + { + C0 = c0; + C1 = c1; + C2 = c2; + } + + public static Fp6 FromBytes(ReadOnlySpan data) + { + if (data.Length != Size) + throw new FormatException($"The argument `{nameof(data)}` must contain {Size} bytes."); + Fp2 c0 = Fp2.FromBytes(data[(Fp2.Size * 2)..]); + Fp2 c1 = Fp2.FromBytes(data[Fp2.Size..(Fp2.Size * 2)]); + Fp2 c2 = Fp2.FromBytes(data[..Fp2.Size]); + return new(in c0, in c1, in c2); + } + + public static bool operator ==(in Fp6 a, in Fp6 b) + { + return a.C0 == b.C0 & a.C1 == b.C1 & a.C2 == b.C2; + } + + public static bool operator !=(in Fp6 a, in Fp6 b) + { + return !(a == b); + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not Fp6 other) return false; + return this == other; + } + + public bool Equals(Fp6 other) + { + return this == other; + } + + public override int GetHashCode() + { + return C0.GetHashCode() ^ C1.GetHashCode() ^ C2.GetHashCode(); + } + + public byte[] ToArray() + { + byte[] result = new byte[Size]; + TryWrite(result); + return result; + } + + public bool TryWrite(Span buffer) + { + if (buffer.Length < Size) return false; + C0.TryWrite(buffer[(Fp2.Size * 2)..Size]); + C1.TryWrite(buffer[Fp2.Size..(Fp2.Size * 2)]); + C2.TryWrite(buffer[0..Fp2.Size]); + return true; + } + + public static Fp6 Random(RandomNumberGenerator rng) + { + return new(Fp2.Random(rng), Fp2.Random(rng), Fp2.Random(rng)); + } + + internal Fp6 MulBy_1(in Fp2 c1) + { + var b_b = C1 * c1; + + var t1 = (C1 + C2) * c1 - b_b; + t1 = t1.MulByNonresidue(); + + var t2 = (C0 + C1) * c1 - b_b; + + return new Fp6(in t1, in t2, in b_b); + } + + internal Fp6 MulBy_01(in Fp2 c0, in Fp2 c1) + { + var a_a = C0 * c0; + var b_b = C1 * c1; + + var t1 = (C1 + C2) * c1 - b_b; + t1 = t1.MulByNonresidue() + a_a; + + var t2 = (c0 + c1) * (C0 + C1) - a_a - b_b; + + var t3 = (C0 + C2) * c0 - a_a + b_b; + + return new Fp6(in t1, in t2, in t3); + } + + public Fp6 MulByNonresidue() + { + // Given a + bv + cv^2, this produces + // av + bv^2 + cv^3 + // but because v^3 = u + 1, we have + // c(u + 1) + av + v^2 + + return new Fp6(C2.MulByNonresidue(), in C0, in C1); + } + + public Fp6 FrobeniusMap() + { + var c0 = C0.FrobeniusMap(); + var c1 = C1.FrobeniusMap(); + var c2 = C2.FrobeniusMap(); + + // c1 = c1 * (u + 1)^((p - 1) / 3) + c1 *= new Fp2(in Fp.Zero, Fp.FromRawUnchecked(new ulong[] + { + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741 + })); + + // c2 = c2 * (u + 1)^((2p - 2) / 3) + c2 *= new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x890d_c9e4_8675_45c3, + 0x2af3_2253_3285_a5d5, + 0x5088_0866_309b_7e2c, + 0xa20d_1b8c_7e88_1024, + 0x14e4_f04f_e2db_9068, + 0x14e5_6d3f_1564_853a + }), in Fp.Zero); + + return new Fp6(c0, c1, c2); + } + + public Fp6 Square() + { + var s0 = C0.Square(); + var ab = C0 * C1; + var s1 = ab + ab; + var s2 = (C0 - C1 + C2).Square(); + var bc = C1 * C2; + var s3 = bc + bc; + var s4 = C2.Square(); + + return new Fp6( + s3.MulByNonresidue() + s0, + s4.MulByNonresidue() + s1, + s1 + s2 + s3 - s0 - s4 + ); + } + + public Fp6 Invert() + { + var c0 = (C1 * C2).MulByNonresidue(); + c0 = C0.Square() - c0; + + var c1 = C2.Square().MulByNonresidue(); + c1 -= C0 * C1; + + var c2 = C1.Square(); + c2 -= C0 * C2; + + var t = (C1 * c2 + C2 * c1).MulByNonresidue(); + t += C0 * c0; + + t = t.Invert(); + return new Fp6(t * c0, t * c1, t * c2); + } + + public static Fp6 operator -(in Fp6 a) + { + return new Fp6(-a.C0, -a.C1, -a.C2); + } + + public static Fp6 operator +(in Fp6 a, in Fp6 b) + { + return new Fp6(a.C0 + b.C0, a.C1 + b.C1, a.C2 + b.C2); + } + + public static Fp6 operator -(in Fp6 a, in Fp6 b) + { + return new Fp6(a.C0 - b.C0, a.C1 - b.C1, a.C2 - b.C2); + } + + public static Fp6 operator *(in Fp6 a, in Fp6 b) + { + // The intuition for this algorithm is that we can look at F_p^6 as a direct + // extension of F_p^2, and express the overall operations down to the base field + // F_p instead of only over F_p^2. This enables us to interleave multiplications + // and reductions, ensuring that we don't require double-width intermediate + // representations (with around twice as many limbs as F_p elements). + + // We want to express the multiplication c = a x b, where a = (a_0, a_1, a_2) is + // an element of F_p^6, and a_i = (a_i,0, a_i,1) is an element of F_p^2. The fully + // expanded multiplication is given by (2022-376 §5): + // + // c_0,0 = a_0,0 b_0,0 - a_0,1 b_0,1 + a_1,0 b_2,0 - a_1,1 b_2,1 + a_2,0 b_1,0 - a_2,1 b_1,1 + // - a_1,0 b_2,1 - a_1,1 b_2,0 - a_2,0 b_1,1 - a_2,1 b_1,0. + // = a_0,0 b_0,0 - a_0,1 b_0,1 + a_1,0 (b_2,0 - b_2,1) - a_1,1 (b_2,0 + b_2,1) + // + a_2,0 (b_1,0 - b_1,1) - a_2,1 (b_1,0 + b_1,1). + // + // c_0,1 = a_0,0 b_0,1 + a_0,1 b_0,0 + a_1,0 b_2,1 + a_1,1 b_2,0 + a_2,0 b_1,1 + a_2,1 b_1,0 + // + a_1,0 b_2,0 - a_1,1 b_2,1 + a_2,0 b_1,0 - a_2,1 b_1,1. + // = a_0,0 b_0,1 + a_0,1 b_0,0 + a_1,0(b_2,0 + b_2,1) + a_1,1(b_2,0 - b_2,1) + // + a_2,0(b_1,0 + b_1,1) + a_2,1(b_1,0 - b_1,1). + // + // c_1,0 = a_0,0 b_1,0 - a_0,1 b_1,1 + a_1,0 b_0,0 - a_1,1 b_0,1 + a_2,0 b_2,0 - a_2,1 b_2,1 + // - a_2,0 b_2,1 - a_2,1 b_2,0. + // = a_0,0 b_1,0 - a_0,1 b_1,1 + a_1,0 b_0,0 - a_1,1 b_0,1 + a_2,0(b_2,0 - b_2,1) + // - a_2,1(b_2,0 + b_2,1). + // + // c_1,1 = a_0,0 b_1,1 + a_0,1 b_1,0 + a_1,0 b_0,1 + a_1,1 b_0,0 + a_2,0 b_2,1 + a_2,1 b_2,0 + // + a_2,0 b_2,0 - a_2,1 b_2,1 + // = a_0,0 b_1,1 + a_0,1 b_1,0 + a_1,0 b_0,1 + a_1,1 b_0,0 + a_2,0(b_2,0 + b_2,1) + // + a_2,1(b_2,0 - b_2,1). + // + // c_2,0 = a_0,0 b_2,0 - a_0,1 b_2,1 + a_1,0 b_1,0 - a_1,1 b_1,1 + a_2,0 b_0,0 - a_2,1 b_0,1. + // c_2,1 = a_0,0 b_2,1 + a_0,1 b_2,0 + a_1,0 b_1,1 + a_1,1 b_1,0 + a_2,0 b_0,1 + a_2,1 b_0,0. + // + // Each of these is a "sum of products", which we can compute efficiently. + + var b10_p_b11 = b.C1.C0 + b.C1.C1; + var b10_m_b11 = b.C1.C0 - b.C1.C1; + var b20_p_b21 = b.C2.C0 + b.C2.C1; + var b20_m_b21 = b.C2.C0 - b.C2.C1; + + return new Fp6(new Fp2( + Fp.SumOfProducts( + stackalloc[] { a.C0.C0, -a.C0.C1, a.C1.C0, -a.C1.C1, a.C2.C0, -a.C2.C1 }, + stackalloc[] { b.C0.C0, b.C0.C1, b20_m_b21, b20_p_b21, b10_m_b11, b10_p_b11 } + ), + Fp.SumOfProducts( + stackalloc[] { a.C0.C0, a.C0.C1, a.C1.C0, a.C1.C1, a.C2.C0, a.C2.C1 }, + stackalloc[] { b.C0.C1, b.C0.C0, b20_p_b21, b20_m_b21, b10_p_b11, b10_m_b11 } + )), new Fp2( + Fp.SumOfProducts( + stackalloc[] { a.C0.C0, -a.C0.C1, a.C1.C0, -a.C1.C1, a.C2.C0, -a.C2.C1 }, + stackalloc[] { b.C1.C0, b.C1.C1, b.C0.C0, b.C0.C1, b20_m_b21, b20_p_b21 } + ), + Fp.SumOfProducts( + stackalloc[] { a.C0.C0, a.C0.C1, a.C1.C0, a.C1.C1, a.C2.C0, a.C2.C1 }, + stackalloc[] { b.C1.C1, b.C1.C0, b.C0.C1, b.C0.C0, b20_p_b21, b20_m_b21 } + )), new Fp2( + Fp.SumOfProducts( + stackalloc[] { a.C0.C0, -a.C0.C1, a.C1.C0, -a.C1.C1, a.C2.C0, -a.C2.C1 }, + stackalloc[] { b.C2.C0, b.C2.C1, b.C1.C0, b.C1.C1, b.C0.C0, b.C0.C1 } + ), + Fp.SumOfProducts( + stackalloc[] { a.C0.C0, a.C0.C1, a.C1.C0, a.C1.C1, a.C2.C0, a.C2.C1 }, + stackalloc[] { b.C2.C1, b.C2.C0, b.C1.C1, b.C1.C0, b.C0.C1, b.C0.C0 } + )) + ); + } + + #region Instance math methods + + public Fp6 Negate() => -this; + public Fp6 Multiply(in Fp6 value) => this * value; + public Fp6 Sum(in Fp6 value) => this + value; + public Fp6 Subtract(in Fp6 value) => this - value; + + #endregion +} diff --git a/src/Neo.Cryptography.BLS12_381/FpConstants.cs b/src/Neo.Cryptography.BLS12_381/FpConstants.cs new file mode 100644 index 0000000000..dd9f7ece9f --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/FpConstants.cs @@ -0,0 +1,84 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// FpConstants.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.Cryptography.BLS12_381; + +static class FpConstants +{ + // p = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 + public static readonly ulong[] MODULUS = + { + 0xb9fe_ffff_ffff_aaab, + 0x1eab_fffe_b153_ffff, + 0x6730_d2a0_f6b0_f624, + 0x6477_4b84_f385_12bf, + 0x4b1b_a7b6_434b_acd7, + 0x1a01_11ea_397f_e69a + }; + + // p - 2 + public static readonly ulong[] P_2 = + { + 0xb9fe_ffff_ffff_aaa9, + 0x1eab_fffe_b153_ffff, + 0x6730_d2a0_f6b0_f624, + 0x6477_4b84_f385_12bf, + 0x4b1b_a7b6_434b_acd7, + 0x1a01_11ea_397f_e69a + }; + + // (p + 1) / 4 + public static readonly ulong[] P_1_4 = + { + 0xee7f_bfff_ffff_eaab, + 0x07aa_ffff_ac54_ffff, + 0xd9cc_34a8_3dac_3d89, + 0xd91d_d2e1_3ce1_44af, + 0x92c6_e9ed_90d2_eb35, + 0x0680_447a_8e5f_f9a6 + }; + + // INV = -(p^{-1} mod 2^64) mod 2^64 + public const ulong INV = 0x89f3_fffc_fffc_fffd; + + // R = 2^384 mod p + public static readonly Fp R = Fp.FromRawUnchecked(new ulong[] + { + 0x7609_0000_0002_fffd, + 0xebf4_000b_c40c_0002, + 0x5f48_9857_53c7_58ba, + 0x77ce_5853_7052_5745, + 0x5c07_1a97_a256_ec6d, + 0x15f6_5ec3_fa80_e493 + }); + + // R2 = 2^(384*2) mod p + public static readonly Fp R2 = Fp.FromRawUnchecked(new ulong[] + { + 0xf4df_1f34_1c34_1746, + 0x0a76_e6a6_09d1_04f1, + 0x8de5_476c_4c95_b6d5, + 0x67eb_88a9_939d_83c0, + 0x9a79_3e85_b519_952d, + 0x1198_8fe5_92ca_e3aa + }); + + // R3 = 2^(384*3) mod p + public static readonly Fp R3 = Fp.FromRawUnchecked(new ulong[] + { + 0xed48_ac6b_d94c_a1e0, + 0x315f_831e_03a7_adf8, + 0x9a53_352a_615e_29dd, + 0x34c0_4e5e_921e_1761, + 0x2512_d435_6572_4728, + 0x0aa6_3460_9175_5d4d + }); +} diff --git a/src/Neo.Cryptography.BLS12_381/G1Affine.cs b/src/Neo.Cryptography.BLS12_381/G1Affine.cs new file mode 100644 index 0000000000..cbed66bc8f --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/G1Affine.cs @@ -0,0 +1,181 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// G1Affine.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.CodeAnalysis; +using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; +using static Neo.Cryptography.BLS12_381.G1Constants; + +namespace Neo.Cryptography.BLS12_381; + +public readonly struct G1Affine : IEquatable +{ + public readonly Fp X; + public readonly Fp Y; + public readonly bool Infinity; + + public static readonly G1Affine Identity = new(in Fp.Zero, in Fp.One, true); + public static readonly G1Affine Generator = new(in GeneratorX, in GeneratorY, false); + + public bool IsIdentity => Infinity; + public bool IsTorsionFree => -new G1Projective(this).MulByX().MulByX() == new G1Projective(Endomorphism()); + public bool IsOnCurve => ((Y.Square() - (X.Square() * X)) == B) | Infinity; + + public G1Affine(in Fp x, in Fp y) + : this(in x, in y, false) + { + } + + private G1Affine(in Fp x, in Fp y, bool infinity) + { + X = x; + Y = y; + Infinity = infinity; + } + + public G1Affine(in G1Projective p) + { + bool s = p.Z.TryInvert(out Fp zinv); + + zinv = ConditionalSelect(in Fp.Zero, in zinv, s); + Fp x = p.X * zinv; + Fp y = p.Y * zinv; + + G1Affine tmp = new(in x, in y, false); + this = ConditionalSelect(in tmp, in Identity, !s); + } + + public static G1Affine FromUncompressed(ReadOnlySpan data) + { + return FromBytes(data, false, true); + } + + public static G1Affine FromCompressed(ReadOnlySpan data) + { + return FromBytes(data, true, true); + } + + private static G1Affine FromBytes(ReadOnlySpan data, bool compressed, bool check) + { + bool compression_flag_set = (data[0] & 0x80) != 0; + bool infinity_flag_set = (data[0] & 0x40) != 0; + bool sort_flag_set = (data[0] & 0x20) != 0; + byte[] tmp = data[0..48].ToArray(); + tmp[0] &= 0b0001_1111; + Fp x = Fp.FromBytes(tmp); + if (compressed) + { + Fp y = ((x.Square() * x) + B).Sqrt(); + y = ConditionalSelect(in y, -y, y.LexicographicallyLargest() ^ sort_flag_set); + G1Affine result = new(in x, in y, infinity_flag_set); + result = ConditionalSelect(in result, in Identity, infinity_flag_set); + if (check) + { + bool _checked = (!infinity_flag_set | (infinity_flag_set & !sort_flag_set & x.IsZero)) + & compression_flag_set; + _checked &= result.IsTorsionFree; + if (!_checked) throw new FormatException(); + } + return result; + } + else + { + Fp y = Fp.FromBytes(data[48..96]); + G1Affine result = ConditionalSelect(new(in x, in y, infinity_flag_set), in Identity, infinity_flag_set); + if (check) + { + bool _checked = (!infinity_flag_set | (infinity_flag_set & x.IsZero & y.IsZero)) + & !compression_flag_set + & !sort_flag_set; + _checked &= result.IsOnCurve & result.IsTorsionFree; + if (!_checked) throw new FormatException(); + } + return result; + } + } + + public static bool operator ==(in G1Affine a, in G1Affine b) + { + return (a.Infinity & b.Infinity) | (!a.Infinity & !b.Infinity & a.X == b.X & a.Y == b.Y); + } + + public static bool operator !=(in G1Affine a, in G1Affine b) + { + return !(a == b); + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not G1Affine other) return false; + return this == other; + } + + public bool Equals(G1Affine other) + { + return this == other; + } + + public override int GetHashCode() + { + if (Infinity) return Infinity.GetHashCode(); + return X.GetHashCode() ^ Y.GetHashCode(); + } + + public static G1Affine operator -(in G1Affine p) + { + return new G1Affine(in p.X, ConditionalSelect(-p.Y, in Fp.One, p.Infinity), p.Infinity); + } + + public byte[] ToCompressed() + { + byte[] res = ConditionalSelect(in X, in Fp.Zero, Infinity).ToArray(); + + // This point is in compressed form, so we set the most significant bit. + res[0] |= 0x80; + + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= ConditionalSelect((byte)0, (byte)0x40, Infinity); + + // Is the y-coordinate the lexicographically largest of the two associated with the + // x-coordinate? If so, set the third-most significant bit so long as this is not + // the point at infinity. + res[0] |= ConditionalSelect((byte)0, (byte)0x20, !Infinity & Y.LexicographicallyLargest()); + + return res; + } + + public byte[] ToUncompressed() + { + byte[] res = new byte[96]; + + ConditionalSelect(in X, in Fp.Zero, Infinity).TryWrite(res.AsSpan(0..48)); + ConditionalSelect(in Y, in Fp.Zero, Infinity).TryWrite(res.AsSpan(48..96)); + + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= ConditionalSelect((byte)0, (byte)0x40, Infinity); + + return res; + } + + public G1Projective ToCurve() + { + return new(this); + } + + private G1Affine Endomorphism() + { + return new(X * BETA, in Y, Infinity); + } + + public static G1Projective operator *(in G1Affine a, in Scalar b) + { + return new G1Projective(in a) * b.ToArray(); + } +} diff --git a/src/Neo.Cryptography.BLS12_381/G1Constants.cs b/src/Neo.Cryptography.BLS12_381/G1Constants.cs new file mode 100644 index 0000000000..1c07bb0900 --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/G1Constants.cs @@ -0,0 +1,55 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// G1Constants.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.Cryptography.BLS12_381; + +static class G1Constants +{ + public static readonly Fp GeneratorX = Fp.FromRawUnchecked(new ulong[] + { + 0x5cb3_8790_fd53_0c16, + 0x7817_fc67_9976_fff5, + 0x154f_95c7_143b_a1c1, + 0xf0ae_6acd_f3d0_e747, + 0xedce_6ecc_21db_f440, + 0x1201_7741_9e0b_fb75 + }); + + public static readonly Fp GeneratorY = Fp.FromRawUnchecked(new ulong[] + { + 0xbaac_93d5_0ce7_2271, + 0x8c22_631a_7918_fd8e, + 0xdd59_5f13_5707_25ce, + 0x51ac_5829_5040_5194, + 0x0e1c_8c3f_ad00_59c0, + 0x0bbc_3efc_5008_a26a + }); + + public static readonly Fp B = Fp.FromRawUnchecked(new ulong[] + { + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e + }); + + public static readonly Fp BETA = Fp.FromRawUnchecked(new ulong[] + { + 0x30f1_361b_798a_64e8, + 0xf3b8_ddab_7ece_5a2a, + 0x16a8_ca3a_c615_77f7, + 0xc26a_2ff8_74fd_029b, + 0x3636_b766_6070_1c6e, + 0x051b_a4ab_241b_6160 + }); +} diff --git a/src/Neo.Cryptography.BLS12_381/G1Projective.cs b/src/Neo.Cryptography.BLS12_381/G1Projective.cs new file mode 100644 index 0000000000..95600af135 --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/G1Projective.cs @@ -0,0 +1,294 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// G1Projective.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.CodeAnalysis; +using System.Runtime.InteropServices; +using static Neo.Cryptography.BLS12_381.Constants; +using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; +using static Neo.Cryptography.BLS12_381.G1Constants; + +namespace Neo.Cryptography.BLS12_381; + +[StructLayout(LayoutKind.Explicit, Size = Fp.Size * 3)] +public readonly struct G1Projective : IEquatable +{ + [FieldOffset(0)] + public readonly Fp X; + [FieldOffset(Fp.Size)] + public readonly Fp Y; + [FieldOffset(Fp.Size * 2)] + public readonly Fp Z; + + public static readonly G1Projective Identity = new(in Fp.Zero, in Fp.One, in Fp.Zero); + public static readonly G1Projective Generator = new(in GeneratorX, in GeneratorY, in Fp.One); + + public bool IsIdentity => Z.IsZero; + public bool IsOnCurve => ((Y.Square() * Z) == (X.Square() * X + Z.Square() * Z * B)) | Z.IsZero; + + public G1Projective(in Fp x, in Fp y, in Fp z) + { + X = x; + Y = y; + Z = z; + } + + public G1Projective(in G1Affine p) + : this(in p.X, in p.Y, ConditionalSelect(in Fp.One, in Fp.Zero, p.Infinity)) + { + } + + public static bool operator ==(in G1Projective a, in G1Projective b) + { + // Is (xz, yz, z) equal to (x'z', y'z', z') when converted to affine? + + Fp x1 = a.X * b.Z; + Fp x2 = b.X * a.Z; + + Fp y1 = a.Y * b.Z; + Fp y2 = b.Y * a.Z; + + bool self_is_zero = a.Z.IsZero; + bool other_is_zero = b.Z.IsZero; + + // Both point at infinity. Or neither point at infinity, coordinates are the same. + return (self_is_zero & other_is_zero) | ((!self_is_zero) & (!other_is_zero) & x1 == x2 & y1 == y2); + } + + public static bool operator !=(in G1Projective a, in G1Projective b) + { + return !(a == b); + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not G1Projective other) return false; + return this == other; + } + + public bool Equals(G1Projective other) + { + return this == other; + } + + public override int GetHashCode() + { + return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); + } + + public static G1Projective operator -(in G1Projective p) + { + return new G1Projective(in p.X, -p.Y, in p.Z); + } + + private static Fp MulBy3B(in Fp a) + { + Fp b = a + a; + b += b; + return b + b + b; + } + + public G1Projective Double() + { + Fp t0 = Y.Square(); + Fp z3 = t0 + t0; + z3 += z3; + z3 += z3; + Fp t1 = Y * Z; + Fp t2 = Z.Square(); + t2 = MulBy3B(in t2); + Fp x3 = t2 * z3; + Fp y3 = t0 + t2; + z3 = t1 * z3; + t1 = t2 + t2; + t2 = t1 + t2; + t0 -= t2; + y3 = t0 * y3; + y3 = x3 + y3; + t1 = X * Y; + x3 = t0 * t1; + x3 += x3; + + G1Projective tmp = new(in x3, in y3, in z3); + return ConditionalSelect(in tmp, in Identity, IsIdentity); + } + + public static G1Projective operator +(in G1Projective a, in G1Projective b) + { + Fp t0 = a.X * b.X; + Fp t1 = a.Y * b.Y; + Fp t2 = a.Z * b.Z; + Fp t3 = a.X + a.Y; + Fp t4 = b.X + b.Y; + t3 *= t4; + t4 = t0 + t1; + t3 -= t4; + t4 = a.Y + a.Z; + Fp x3 = b.Y + b.Z; + t4 *= x3; + x3 = t1 + t2; + t4 -= x3; + x3 = a.X + a.Z; + Fp y3 = b.X + b.Z; + x3 *= y3; + y3 = t0 + t2; + y3 = x3 - y3; + x3 = t0 + t0; + t0 = x3 + t0; + t2 = MulBy3B(in t2); + Fp z3 = t1 + t2; + t1 -= t2; + y3 = MulBy3B(in y3); + x3 = t4 * y3; + t2 = t3 * t1; + x3 = t2 - x3; + y3 *= t0; + t1 *= z3; + y3 = t1 + y3; + t0 *= t3; + z3 *= t4; + z3 += t0; + + return new G1Projective(in x3, in y3, in z3); + } + + public static G1Projective operator +(in G1Projective a, in G1Affine b) + { + Fp t0 = a.X * b.X; + Fp t1 = a.Y * b.Y; + Fp t3 = b.X + b.Y; + Fp t4 = a.X + a.Y; + t3 *= t4; + t4 = t0 + t1; + t3 -= t4; + t4 = b.Y * a.Z; + t4 += a.Y; + Fp y3 = b.X * a.Z; + y3 += a.X; + Fp x3 = t0 + t0; + t0 = x3 + t0; + Fp t2 = MulBy3B(in a.Z); + Fp z3 = t1 + t2; + t1 -= t2; + y3 = MulBy3B(in y3); + x3 = t4 * y3; + t2 = t3 * t1; + x3 = t2 - x3; + y3 *= t0; + t1 *= z3; + y3 = t1 + y3; + t0 *= t3; + z3 *= t4; + z3 += t0; + + G1Projective tmp = new(in x3, in y3, in z3); + return ConditionalSelect(in tmp, in a, b.IsIdentity); + } + + public static G1Projective operator +(in G1Affine a, in G1Projective b) + { + return b + a; + } + + public static G1Projective operator -(in G1Projective a, in G1Projective b) + { + return a + -b; + } + + public static G1Projective operator -(in G1Projective a, in G1Affine b) + { + return a + -b; + } + + public static G1Projective operator -(in G1Affine a, in G1Projective b) + { + return -b + a; + } + + public static G1Projective operator *(in G1Projective a, byte[] b) + { + int length = b.Length; + if (length != 32) + throw new ArgumentException($"The argument {nameof(b)} must be 32 bytes."); + + G1Projective acc = Identity; + + foreach (bool bit in b + .SelectMany(p => Enumerable.Range(0, 8).Select(q => ((p >> q) & 1) == 1)) + .Reverse() + .Skip(1)) + { + acc = acc.Double(); + acc = ConditionalSelect(in acc, acc + a, bit); + } + + return acc; + } + + public static G1Projective operator *(in G1Projective a, in Scalar b) + { + return a * b.ToArray(); + } + + internal G1Projective MulByX() + { + G1Projective xself = Identity; + + ulong x = BLS_X >> 1; + G1Projective tmp = this; + while (x > 0) + { + tmp = tmp.Double(); + + if (x % 2 == 1) + { + xself += tmp; + } + x >>= 1; + } + + if (BLS_X_IS_NEGATIVE) + { + xself = -xself; + } + return xself; + } + + public G1Projective ClearCofactor() + { + return this - MulByX(); + } + + public static void BatchNormalize(ReadOnlySpan p, Span q) + { + int length = p.Length; + if (length != q.Length) + throw new ArgumentException($"{nameof(p)} and {nameof(q)} must have the same length."); + + Span x = stackalloc Fp[length]; + Fp acc = Fp.One; + for (int i = 0; i < length; i++) + { + x[i] = acc; + acc = ConditionalSelect(acc * p[i].Z, in acc, p[i].IsIdentity); + } + + acc = acc.Invert(); + + for (int i = length - 1; i >= 0; i--) + { + bool skip = p[i].IsIdentity; + Fp tmp = x[i] * acc; + acc = ConditionalSelect(acc * p[i].Z, in acc, skip); + G1Affine qi = new(p[i].X * tmp, p[i].Y * tmp); + q[i] = ConditionalSelect(in qi, in G1Affine.Identity, skip); + } + } +} diff --git a/src/Neo.Cryptography.BLS12_381/G2Affine.cs b/src/Neo.Cryptography.BLS12_381/G2Affine.cs new file mode 100644 index 0000000000..97256d02c7 --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/G2Affine.cs @@ -0,0 +1,225 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// G2Affine.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.CodeAnalysis; +using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; +using static Neo.Cryptography.BLS12_381.G2Constants; + +namespace Neo.Cryptography.BLS12_381; + +public readonly struct G2Affine : IEquatable +{ + public readonly Fp2 X; + public readonly Fp2 Y; + public readonly bool Infinity; + + public static readonly G2Affine Identity = new(in Fp2.Zero, in Fp2.One, true); + public static readonly G2Affine Generator = new(in GeneratorX, in GeneratorY, false); + + public bool IsIdentity => Infinity; + public bool IsTorsionFree + { + get + { + // Algorithm from Section 4 of https://eprint.iacr.org/2021/1130 + // Updated proof of correctness in https://eprint.iacr.org/2022/352 + // + // Check that psi(P) == [x] P + var p = new G2Projective(this); + return p.Psi() == p.MulByX(); + } + } + public bool IsOnCurve => ((Y.Square() - X.Square() * X) == B) | Infinity; // y^2 - x^3 ?= 4(u + 1) + + public G2Affine(in Fp2 x, in Fp2 y) + : this(in x, in y, false) + { + } + + private G2Affine(in Fp2 x, in Fp2 y, bool infinity) + { + X = x; + Y = y; + Infinity = infinity; + } + + public G2Affine(in G2Projective p) + { + bool s = p.Z.TryInvert(out Fp2 zinv); + + zinv = ConditionalSelect(in Fp2.Zero, in zinv, s); + Fp2 x = p.X * zinv; + Fp2 y = p.Y * zinv; + + G2Affine tmp = new(in x, in y, false); + this = ConditionalSelect(in tmp, in Identity, !s); + } + + public static bool operator ==(in G2Affine a, in G2Affine b) + { + // The only cases in which two points are equal are + // 1. infinity is set on both + // 2. infinity is not set on both, and their coordinates are equal + + return (a.Infinity & b.Infinity) | (!a.Infinity & !b.Infinity & a.X == b.X & a.Y == b.Y); + } + + public static bool operator !=(in G2Affine a, in G2Affine b) + { + return !(a == b); + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not G2Affine other) return false; + return this == other; + } + + public bool Equals(G2Affine other) + { + return this == other; + } + + public override int GetHashCode() + { + if (Infinity) return Infinity.GetHashCode(); + return X.GetHashCode() ^ Y.GetHashCode(); + } + + public static G2Affine operator -(in G2Affine a) + { + return new G2Affine( + in a.X, + ConditionalSelect(-a.Y, in Fp2.One, a.Infinity), + a.Infinity + ); + } + + public static G2Projective operator *(in G2Affine a, in Scalar b) + { + return new G2Projective(a) * b.ToArray(); + } + + public byte[] ToCompressed() + { + // Strictly speaking, self.x is zero already when self.infinity is true, but + // to guard against implementation mistakes we do not assume this. + var x = ConditionalSelect(in X, in Fp2.Zero, Infinity); + + var res = new byte[96]; + + x.C1.TryWrite(res.AsSpan(0..48)); + x.C0.TryWrite(res.AsSpan(48..96)); + + // This point is in compressed form, so we set the most significant bit. + res[0] |= 0x80; + + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= ConditionalSelect((byte)0, (byte)0x40, Infinity); + + // Is the y-coordinate the lexicographically largest of the two associated with the + // x-coordinate? If so, set the third-most significant bit so long as this is not + // the point at infinity. + res[0] |= ConditionalSelect((byte)0, (byte)0x20, !Infinity & Y.LexicographicallyLargest()); + + return res; + } + + public byte[] ToUncompressed() + { + var res = new byte[192]; + + var x = ConditionalSelect(in X, in Fp2.Zero, Infinity); + var y = ConditionalSelect(in Y, in Fp2.Zero, Infinity); + + x.C1.TryWrite(res.AsSpan(0..48)); + x.C0.TryWrite(res.AsSpan(48..96)); + y.C1.TryWrite(res.AsSpan(96..144)); + y.C0.TryWrite(res.AsSpan(144..192)); + + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= ConditionalSelect((byte)0, (byte)0x40, Infinity); + + return res; + } + + public static G2Affine FromUncompressed(ReadOnlySpan bytes) + { + return FromBytes(bytes, false, true); + } + + public static G2Affine FromCompressed(ReadOnlySpan bytes) + { + return FromBytes(bytes, true, true); + } + + private static G2Affine FromBytes(ReadOnlySpan bytes, bool compressed, bool check) + { + // Obtain the three flags from the start of the byte sequence + bool compression_flag_set = (bytes[0] & 0x80) != 0; + bool infinity_flag_set = (bytes[0] & 0x40) != 0; + bool sort_flag_set = (bytes[0] & 0x20) != 0; + + // Attempt to obtain the x-coordinate + var tmp = bytes[0..48].ToArray(); + tmp[0] &= 0b0001_1111; + var xc1 = Fp.FromBytes(tmp); + var xc0 = Fp.FromBytes(bytes[48..96]); + var x = new Fp2(in xc0, in xc1); + + if (compressed) + { + // Recover a y-coordinate given x by y = sqrt(x^3 + 4) + var y = ((x.Square() * x) + B).Sqrt(); + y = ConditionalSelect(in y, -y, y.LexicographicallyLargest() ^ sort_flag_set); + G2Affine result = new(in x, in y, infinity_flag_set); + result = ConditionalSelect(in result, in Identity, infinity_flag_set); + if (check) + { + bool _checked = (!infinity_flag_set | (infinity_flag_set & !sort_flag_set & x.IsZero)) + & compression_flag_set; + _checked &= result.IsTorsionFree; + if (!_checked) throw new FormatException(); + } + return result; + } + else + { + // Attempt to obtain the y-coordinate + var yc1 = Fp.FromBytes(bytes[96..144]); + var yc0 = Fp.FromBytes(bytes[144..192]); + var y = new Fp2(in yc0, in yc1); + + // Create a point representing this value + var p = ConditionalSelect(new G2Affine(in x, in y, infinity_flag_set), in Identity, infinity_flag_set); + + if (check) + { + bool _checked = + // If the infinity flag is set, the x and y coordinates should have been zero. + ((!infinity_flag_set) | (infinity_flag_set & x.IsZero & y.IsZero)) & + // The compression flag should not have been set, as this is an uncompressed element + (!compression_flag_set) & + // The sort flag should not have been set, as this is an uncompressed element + (!sort_flag_set); + _checked &= p.IsOnCurve & p.IsTorsionFree; + if (!_checked) throw new FormatException(); + } + + return p; + } + } + + public G2Projective ToCurve() + { + return new(this); + } +} diff --git a/src/Neo.Cryptography.BLS12_381/G2Constants.cs b/src/Neo.Cryptography.BLS12_381/G2Constants.cs new file mode 100644 index 0000000000..045ad43a93 --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/G2Constants.cs @@ -0,0 +1,112 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// G2Constants.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.Cryptography.BLS12_381; + +static class G2Constants +{ + public static readonly Fp2 GeneratorX = new(Fp.FromRawUnchecked(new ulong[] + { + 0xf5f2_8fa2_0294_0a10, + 0xb3f5_fb26_87b4_961a, + 0xa1a8_93b5_3e2a_e580, + 0x9894_999d_1a3c_aee9, + 0x6f67_b763_1863_366b, + 0x0581_9192_4350_bcd7 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xa5a9_c075_9e23_f606, + 0xaaa0_c59d_bccd_60c3, + 0x3bb1_7e18_e286_7806, + 0x1b1a_b6cc_8541_b367, + 0xc2b6_ed0e_f215_8547, + 0x1192_2a09_7360_edf3 + })); + + public static readonly Fp2 GeneratorY = new(Fp.FromRawUnchecked(new ulong[] + { + 0x4c73_0af8_6049_4c4a, + 0x597c_fa1f_5e36_9c5a, + 0xe7e6_856c_aa0a_635a, + 0xbbef_b5e9_6e0d_495f, + 0x07d3_a975_f0ef_25a2, + 0x0083_fd8e_7e80_dae5 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xadc0_fc92_df64_b05d, + 0x18aa_270a_2b14_61dc, + 0x86ad_ac6a_3be4_eba0, + 0x7949_5c4e_c93d_a33a, + 0xe717_5850_a43c_caed, + 0x0b2b_c2a1_63de_1bf2 + })); + + public static readonly Fp2 B = new(Fp.FromRawUnchecked(new ulong[] + { + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e + }), Fp.FromRawUnchecked(new ulong[] + { + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e + })); + + public static readonly Fp2 B3 = B + B + B; + + // 1 / ((u+1) ^ ((q-1)/3)) + public static readonly Fp2 PsiCoeffX = new(in Fp.Zero, Fp.FromRawUnchecked(new ulong[] + { + 0x890dc9e4867545c3, + 0x2af322533285a5d5, + 0x50880866309b7e2c, + 0xa20d1b8c7e881024, + 0x14e4f04fe2db9068, + 0x14e56d3f1564853a + })); + + // 1 / ((u+1) ^ (p-1)/2) + public static readonly Fp2 PsiCoeffY = new(Fp.FromRawUnchecked(new ulong[] + { + 0x3e2f585da55c9ad1, + 0x4294213d86c18183, + 0x382844c88b623732, + 0x92ad2afd19103e18, + 0x1d794e4fac7cf0b9, + 0x0bd592fc7d825ec8 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x7bcfa7a25aa30fda, + 0xdc17dec12a927e7c, + 0x2f088dd86b4ebef1, + 0xd1ca2087da74d4a7, + 0x2da2596696cebc1d, + 0x0e2b7eedbbfd87d2 + })); + + // 1 / 2 ^ ((q-1)/3) + public static readonly Fp2 Psi2CoeffX = new(Fp.FromRawUnchecked(new ulong[] + { + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x03f97d6e83d050d2, + 0x18f0206554638741 + }), in Fp.Zero); +} diff --git a/src/Neo.Cryptography.BLS12_381/G2Prepared.Adder.cs b/src/Neo.Cryptography.BLS12_381/G2Prepared.Adder.cs new file mode 100644 index 0000000000..c28b8889cb --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/G2Prepared.Adder.cs @@ -0,0 +1,74 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// G2Prepared.Adder.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.Runtime.CompilerServices; +using static Neo.Cryptography.BLS12_381.MillerLoopUtility; + +namespace Neo.Cryptography.BLS12_381; + +partial class G2Prepared +{ + class Adder : IMillerLoopDriver + { + public G2Projective Curve; + public readonly G2Affine Base; + public readonly List<(Fp2, Fp2, Fp2)> Coeffs; + + public Adder(in G2Affine q) + { + Curve = new G2Projective(in q); + Base = q; + Coeffs = new(68); + } + + object? IMillerLoopDriver.DoublingStep(in object? f) + { + var coeffs = DoublingStep(ref Curve); + Coeffs.Add(coeffs); + return null; + } + + object? IMillerLoopDriver.AdditionStep(in object? f) + { + var coeffs = AdditionStep(ref Curve, in Base); + Coeffs.Add(coeffs); + return null; + } + + #region IMillerLoopDriver + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static object? Square(in object? f) => null; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static object? Conjugate(in object? f) => null; + + public static object? One + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + object? IMillerLoopDriver.Square(in object? f) => null; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + object? IMillerLoopDriver.Conjugate(in object? f) => null; + + object? IMillerLoopDriver.One + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => null; + } + + #endregion + } +} diff --git a/src/Neo.Cryptography.BLS12_381/G2Prepared.cs b/src/Neo.Cryptography.BLS12_381/G2Prepared.cs new file mode 100644 index 0000000000..38e8bdc902 --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/G2Prepared.cs @@ -0,0 +1,31 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// G2Prepared.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 static Neo.Cryptography.BLS12_381.ConstantTimeUtility; +using static Neo.Cryptography.BLS12_381.MillerLoopUtility; + +namespace Neo.Cryptography.BLS12_381; + +partial class G2Prepared +{ + public readonly bool Infinity; + public readonly List<(Fp2, Fp2, Fp2)> Coeffs; + + public G2Prepared(in G2Affine q) + { + Infinity = q.IsIdentity; + var q2 = ConditionalSelect(in q, in G2Affine.Generator, Infinity); + var adder = new Adder(q2); + MillerLoop(adder); + Coeffs = adder.Coeffs; + if (Coeffs.Count != 68) throw new InvalidOperationException(); + } +} diff --git a/src/Neo.Cryptography.BLS12_381/G2Projective.cs b/src/Neo.Cryptography.BLS12_381/G2Projective.cs new file mode 100644 index 0000000000..08e0fd4593 --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/G2Projective.cs @@ -0,0 +1,377 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// G2Projective.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.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using static Neo.Cryptography.BLS12_381.Constants; +using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; +using static Neo.Cryptography.BLS12_381.G2Constants; + +namespace Neo.Cryptography.BLS12_381; + +[StructLayout(LayoutKind.Explicit, Size = Fp2.Size * 3)] +public readonly struct G2Projective : IEquatable +{ + [FieldOffset(0)] + public readonly Fp2 X; + [FieldOffset(Fp2.Size)] + public readonly Fp2 Y; + [FieldOffset(Fp2.Size * 2)] + public readonly Fp2 Z; + + public static readonly G2Projective Identity = new(in Fp2.Zero, in Fp2.One, in Fp2.Zero); + public static readonly G2Projective Generator = new(in GeneratorX, in GeneratorY, in Fp2.One); + + public bool IsIdentity => Z.IsZero; + public bool IsOnCurve => ((Y.Square() * Z) == (X.Square() * X + Z.Square() * Z * B)) | Z.IsZero; // Y^2 Z = X^3 + b Z^3 + + public G2Projective(in Fp2 x, in Fp2 y, in Fp2 z) + { + X = x; + Y = y; + Z = z; + } + + public G2Projective(in G2Affine p) + { + X = p.X; + Y = p.Y; + Z = ConditionalSelect(in Fp2.One, in Fp2.Zero, p.Infinity); + } + + public static bool operator ==(in G2Projective a, in G2Projective b) + { + // Is (xz, yz, z) equal to (x'z', y'z', z') when converted to affine? + + var x1 = a.X * b.Z; + var x2 = b.X * a.Z; + + var y1 = a.Y * b.Z; + var y2 = b.Y * a.Z; + + var self_is_zero = a.Z.IsZero; + var other_is_zero = b.Z.IsZero; + + return (self_is_zero & other_is_zero) // Both point at infinity + | ((!self_is_zero) & (!other_is_zero) & x1 == x2 & y1 == y2); + // Neither point at infinity, coordinates are the same + } + + public static bool operator !=(in G2Projective a, in G2Projective b) + { + return !(a == b); + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not G2Projective other) return false; + return this == other; + } + + public bool Equals(G2Projective other) + { + return this == other; + } + + public override int GetHashCode() + { + return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); + } + + public static G2Projective operator -(in G2Projective a) + { + return new(in a.X, -a.Y, in a.Z); + } + + public static G2Projective operator +(in G2Projective a, in G2Projective b) + { + // Algorithm 7, https://eprint.iacr.org/2015/1060.pdf + + var t0 = a.X * b.X; + var t1 = a.Y * b.Y; + var t2 = a.Z * b.Z; + var t3 = a.X + a.Y; + var t4 = b.X + b.Y; + t3 *= t4; + t4 = t0 + t1; + t3 -= t4; + t4 = a.Y + a.Z; + var x3 = b.Y + b.Z; + t4 *= x3; + x3 = t1 + t2; + t4 -= x3; + x3 = a.X + a.Z; + var y3 = b.X + b.Z; + x3 *= y3; + y3 = t0 + t2; + y3 = x3 - y3; + x3 = t0 + t0; + t0 = x3 + t0; + t2 = MulBy3B(t2); + var z3 = t1 + t2; + t1 -= t2; + y3 = MulBy3B(y3); + x3 = t4 * y3; + t2 = t3 * t1; + x3 = t2 - x3; + y3 *= t0; + t1 *= z3; + y3 = t1 + y3; + t0 *= t3; + z3 *= t4; + z3 += t0; + + return new G2Projective(in x3, in y3, in z3); + } + + public static G2Projective operator +(in G2Affine a, in G2Projective b) + { + return b + a; + } + + public static G2Projective operator +(in G2Projective a, in G2Affine b) + { + // Algorithm 8, https://eprint.iacr.org/2015/1060.pdf + + var t0 = a.X * b.X; + var t1 = a.Y * b.Y; + var t3 = b.X + b.Y; + var t4 = a.X + a.Y; + t3 *= t4; + t4 = t0 + t1; + t3 -= t4; + t4 = b.Y * a.Z; + t4 += a.Y; + var y3 = b.X * a.Z; + y3 += a.X; + var x3 = t0 + t0; + t0 = x3 + t0; + var t2 = MulBy3B(a.Z); + var z3 = t1 + t2; + t1 -= t2; + y3 = MulBy3B(y3); + x3 = t4 * y3; + t2 = t3 * t1; + x3 = t2 - x3; + y3 *= t0; + t1 *= z3; + y3 = t1 + y3; + t0 *= t3; + z3 *= t4; + z3 += t0; + + var tmp = new G2Projective(in x3, in y3, in z3); + + return ConditionalSelect(in tmp, in a, b.IsIdentity); + } + + public static G2Projective operator -(in G2Projective a, in G2Projective b) + { + return a + -b; + } + + public static G2Projective operator -(in G2Affine a, in G2Projective b) + { + return a + -b; + } + + public static G2Projective operator -(in G2Projective a, in G2Affine b) + { + return a + -b; + } + + public static G2Projective operator *(in G2Projective a, in Scalar b) + { + return a * b.ToArray(); + } + + public static G2Projective operator *(in G2Projective a, byte[] b) + { + var acc = Identity; + + // This is a simple double-and-add implementation of point + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading bit because it's always unset for Fq + // elements. + foreach (bool bit in b + .SelectMany(p => Enumerable.Range(0, 8).Select(q => ((p >> q) & 1) == 1)) + .Reverse() + .Skip(1)) + { + acc = acc.Double(); + acc = ConditionalSelect(in acc, acc + a, bit); + } + + return acc; + } + + private static Fp2 MulBy3B(Fp2 x) + { + return x * B3; + } + + public G2Projective Double() + { + // Algorithm 9, https://eprint.iacr.org/2015/1060.pdf + + var t0 = Y.Square(); + var z3 = t0 + t0; + z3 += z3; + z3 += z3; + var t1 = Y * Z; + var t2 = Z.Square(); + t2 = MulBy3B(t2); + var x3 = t2 * z3; + var y3 = t0 + t2; + z3 = t1 * z3; + t1 = t2 + t2; + t2 = t1 + t2; + t0 -= t2; + y3 = t0 * y3; + y3 = x3 + y3; + t1 = X * Y; + x3 = t0 * t1; + x3 += x3; + + var tmp = new G2Projective(in x3, in y3, in z3); + + return ConditionalSelect(in tmp, in Identity, IsIdentity); + } + + internal G2Projective Psi() + { + return new G2Projective( + // x = frobenius(x)/((u+1)^((p-1)/3)) + X.FrobeniusMap() * PsiCoeffX, + // y = frobenius(y)/(u+1)^((p-1)/2) + Y.FrobeniusMap() * PsiCoeffY, + // z = frobenius(z) + Z.FrobeniusMap() + ); + } + + internal G2Projective Psi2() + { + return new G2Projective( + // x = frobenius^2(x)/2^((p-1)/3); note that q^2 is the order of the field. + X * Psi2CoeffX, + // y = -frobenius^2(y); note that q^2 is the order of the field. + -Y, + // z = z + in Z + ); + } + + internal G2Projective MulByX() + { + var xself = Identity; + // NOTE: in BLS12-381 we can just skip the first bit. + var x = BLS_X >> 1; + var acc = this; + while (x != 0) + { + acc = acc.Double(); + if (x % 2 == 1) + { + xself += acc; + } + x >>= 1; + } + // finally, flip the sign + if (BLS_X_IS_NEGATIVE) + { + xself = -xself; + } + return xself; + } + + public G2Projective ClearCofactor() + { + var t1 = MulByX(); // [x] P + var t2 = Psi(); // psi(P) + + return Double().Psi2() // psi^2(2P) + + (t1 + t2).MulByX() // psi^2(2P) + [x^2] P + [x] psi(P) + - t1 // psi^2(2P) + [x^2 - x] P + [x] psi(P) + - t2 // psi^2(2P) + [x^2 - x] P + [x - 1] psi(P) + - this; // psi^2(2P) + [x^2 - x - 1] P + [x - 1] psi(P) + } + + public static void BatchNormalize(ReadOnlySpan p, Span q) + { + int length = p.Length; + if (length != q.Length) + throw new ArgumentException($"{nameof(p)} and {nameof(q)} must have the same length."); + + Span x = stackalloc Fp2[length]; + Fp2 acc = Fp2.One; + for (int i = 0; i < length; i++) + { + // We use the `x` field of `G2Affine` to store the product + // of previous z-coordinates seen. + x[i] = acc; + + // We will end up skipping all identities in p + acc = ConditionalSelect(acc * p[i].Z, in acc, p[i].IsIdentity); + } + + // This is the inverse, as all z-coordinates are nonzero and the ones + // that are not are skipped. + acc = acc.Invert(); + + for (int i = length - 1; i >= 0; i--) + { + bool skip = p[i].IsIdentity; + + // Compute tmp = 1/z + var tmp = x[i] * acc; + + // Cancel out z-coordinate in denominator of `acc` + acc = ConditionalSelect(acc * p[i].Z, in acc, skip); + + // Set the coordinates to the correct value + G2Affine qi = new(p[i].X * tmp, p[i].Y * tmp); + + q[i] = ConditionalSelect(in qi, in G2Affine.Identity, skip); + } + } + + public static G2Projective Random(RandomNumberGenerator rng) + { + Span buffer = stackalloc byte[sizeof(uint)]; + while (true) + { + var x = Fp2.Random(rng); + rng.GetBytes(buffer); + var flip_sign = BinaryPrimitives.ReadUInt32LittleEndian(buffer) % 2 != 0; + + // Obtain the corresponding y-coordinate given x as y = sqrt(x^3 + 4) + var y = ((x.Square() * x) + B).Sqrt(); + + G2Affine p; + try + { + p = new G2Affine(in x, flip_sign ? -y : y); + } + catch + { + continue; + } + + var result = p.ToCurve().ClearCofactor(); + if (!result.IsIdentity) return result; + } + } +} diff --git a/src/Neo.Cryptography.BLS12_381/GlobalSuppressions.cs b/src/Neo.Cryptography.BLS12_381/GlobalSuppressions.cs new file mode 100644 index 0000000000..97ff6376ff --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/GlobalSuppressions.cs @@ -0,0 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// GlobalSuppressions.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.CodeAnalysis; + +[assembly: SuppressMessage("Usage", "CA2219")] diff --git a/src/Neo.Cryptography.BLS12_381/Gt.cs b/src/Neo.Cryptography.BLS12_381/Gt.cs new file mode 100644 index 0000000000..e6c33b18d5 --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/Gt.cs @@ -0,0 +1,135 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Gt.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.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; +using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; +using static Neo.Cryptography.BLS12_381.GtConstants; + +namespace Neo.Cryptography.BLS12_381; + +public readonly struct Gt : IEquatable +{ + public readonly Fp12 Value; + + public static readonly Gt Identity = new(in Fp12.One); + public static readonly Gt Generator = new(in GeneratorValue); + + public bool IsIdentity => this == Identity; + + public Gt(in Fp12 f) + { + Value = f; + } + + public static Gt FromBytes(ReadOnlySpan data) + { + return new(Fp12.FromBytes(data)); + } + + public static bool operator ==(in Gt a, in Gt b) + { + return a.Value == b.Value; + } + + public static bool operator !=(in Gt a, in Gt b) + { + return !(a == b); + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not Gt other) return false; + return this == other; + } + + public bool Equals(Gt other) + { + return this == other; + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + public byte[] ToArray() + { + return Value.ToArray(); + } + + public bool TryWrite(Span buffer) + { + return Value.TryWrite(buffer); + } + + public static Gt Random(RandomNumberGenerator rng) + { + while (true) + { + var inner = Fp12.Random(rng); + + // Not all elements of Fp12 are elements of the prime-order multiplicative + // subgroup. We run the random element through final_exponentiation to obtain + // a valid element, which requires that it is non-zero. + if (!inner.IsZero) + { + ref MillerLoopResult result = ref Unsafe.As(ref inner); + return result.FinalExponentiation(); + } + } + } + + public Gt Double() + { + return new(Value.Square()); + } + + public static Gt operator -(in Gt a) + { + // The element is unitary, so we just conjugate. + return new(a.Value.Conjugate()); + } + + public static Gt operator +(in Gt a, in Gt b) + { + return new(a.Value * b.Value); + } + + public static Gt operator -(in Gt a, in Gt b) + { + return a + -b; + } + + public static Gt operator *(in Gt a, in Scalar b) + { + var acc = Identity; + + // This is a simple double-and-add implementation of group element + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading bit because it's always unset for Fq + // elements. + foreach (bool bit in b + .ToArray() + .SelectMany(p => Enumerable.Range(0, 8).Select(q => ((p >> q) & 1) == 1)) + .Reverse() + .Skip(1)) + { + acc = acc.Double(); + acc = ConditionalSelect(in acc, acc + a, bit); + } + + return acc; + } +} diff --git a/src/Neo.Cryptography.BLS12_381/GtConstants.cs b/src/Neo.Cryptography.BLS12_381/GtConstants.cs new file mode 100644 index 0000000000..ca76083fe8 --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/GtConstants.cs @@ -0,0 +1,115 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// GtConstants.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.Cryptography.BLS12_381; + +static class GtConstants +{ + // pairing(&G1Affine.generator(), &G2Affine.generator()) + public static readonly Fp12 GeneratorValue = new(new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x1972_e433_a01f_85c5, + 0x97d3_2b76_fd77_2538, + 0xc8ce_546f_c96b_cdf9, + 0xcef6_3e73_66d4_0614, + 0xa611_3427_8184_3780, + 0x13f3_448a_3fc6_d825 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xd263_31b0_2e9d_6995, + 0x9d68_a482_f779_7e7d, + 0x9c9b_2924_8d39_ea92, + 0xf480_1ca2_e131_07aa, + 0xa16c_0732_bdbc_b066, + 0x083c_a4af_ba36_0478 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x59e2_61db_0916_b641, + 0x2716_b6f4_b23e_960d, + 0xc8e5_5b10_a0bd_9c45, + 0x0bdb_0bd9_9c4d_eda8, + 0x8cf8_9ebf_57fd_aac5, + 0x12d6_b792_9e77_7a5e + }), Fp.FromRawUnchecked(new ulong[] + { + 0x5fc8_5188_b0e1_5f35, + 0x34a0_6e3a_8f09_6365, + 0xdb31_26a6_e02a_d62c, + 0xfc6f_5aa9_7d9a_990b, + 0xa12f_55f5_eb89_c210, + 0x1723_703a_926f_8889 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x9358_8f29_7182_8778, + 0x43f6_5b86_11ab_7585, + 0x3183_aaf5_ec27_9fdf, + 0xfa73_d7e1_8ac9_9df6, + 0x64e1_76a6_a64c_99b0, + 0x179f_a78c_5838_8f1f + }), Fp.FromRawUnchecked(new ulong[] + { + 0x672a_0a11_ca2a_ef12, + 0x0d11_b9b5_2aa3_f16b, + 0xa444_12d0_699d_056e, + 0xc01d_0177_221a_5ba5, + 0x66e0_cede_6c73_5529, + 0x05f5_a71e_9fdd_c339 + }))), new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xd30a_88a1_b062_c679, + 0x5ac5_6a5d_35fc_8304, + 0xd0c8_34a6_a81f_290d, + 0xcd54_30c2_da37_07c7, + 0xf0c2_7ff7_8050_0af0, + 0x0924_5da6_e2d7_2eae + }), + Fp.FromRawUnchecked(new ulong[] + { + 0x9f2e_0676_791b_5156, + 0xe2d1_c823_4918_fe13, + 0x4c9e_459f_3c56_1bf4, + 0xa3e8_5e53_b9d3_e3c1, + 0x820a_121e_21a7_0020, + 0x15af_6183_41c5_9acc + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x7c95_658c_2499_3ab1, + 0x73eb_3872_1ca8_86b9, + 0x5256_d749_4774_34bc, + 0x8ba4_1902_ea50_4a8b, + 0x04a3_d3f8_0c86_ce6d, + 0x18a6_4a87_fb68_6eaa + }), Fp.FromRawUnchecked(new ulong[] + { + 0xbb83_e71b_b920_cf26, + 0x2a52_77ac_92a7_3945, + 0xfc0e_e59f_94f0_46a0, + 0x7158_cdf3_7860_58f7, + 0x7cc1_061b_82f9_45f6, + 0x03f8_47aa_9fdb_e567 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x8078_dba5_6134_e657, + 0x1cd7_ec9a_4399_8a6e, + 0xb1aa_599a_1a99_3766, + 0xc9a0_f62f_0842_ee44, + 0x8e15_9be3_b605_dffa, + 0x0c86_ba0d_4af1_3fc2 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xe80f_f2a0_6a52_ffb1, + 0x7694_ca48_721a_906c, + 0x7583_183e_03b0_8514, + 0xf567_afdd_40ce_e4e2, + 0x9a6d_96d2_e526_a5fc, + 0x197e_9f49_861f_2242 + })))); +} diff --git a/src/Neo.Cryptography.BLS12_381/IMillerLoopDriver.cs b/src/Neo.Cryptography.BLS12_381/IMillerLoopDriver.cs new file mode 100644 index 0000000000..0d0d3ac9df --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/IMillerLoopDriver.cs @@ -0,0 +1,21 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// IMillerLoopDriver.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.Cryptography.BLS12_381; + +interface IMillerLoopDriver +{ + public T DoublingStep(in T f); + public T AdditionStep(in T f); + public T Square(in T f); + public T Conjugate(in T f); + public T One { get; } +} diff --git a/src/Neo.Cryptography.BLS12_381/INumber.cs b/src/Neo.Cryptography.BLS12_381/INumber.cs new file mode 100644 index 0000000000..f328a77c2e --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/INumber.cs @@ -0,0 +1,70 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// INumber.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.Runtime.CompilerServices; + +namespace Neo.Cryptography.BLS12_381; + +interface INumber where T : unmanaged, INumber +{ + //static abstract int Size { get; } + //static abstract ref readonly T Zero { get; } + //static abstract ref readonly T One { get; } + + //static abstract T operator -(in T x); + //static abstract T operator +(in T x, in T y); + //static abstract T operator -(in T x, in T y); + //static abstract T operator *(in T x, in T y); + + T Negate(); + T Sum(in T value); + T Subtract(in T value); + T Multiply(in T value); + + abstract T Square(); +} + +static class NumberExtensions +{ + private static T PowVartime(T one, T self, ulong[] by) where T : unmanaged, INumber + { + // Although this is labeled "vartime", it is only + // variable time with respect to the exponent. + var res = one; + for (int j = by.Length - 1; j >= 0; j--) + { + for (int i = 63; i >= 0; i--) + { + res = res.Square(); + if (((by[j] >> i) & 1) == 1) + { + res = res.Multiply(self); + } + } + } + return res; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fp PowVartime(this Fp self, ulong[] by) => PowVartime(Fp.One, self, by); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fp2 PowVartime(this Fp2 self, ulong[] by) => PowVartime(Fp2.One, self, by); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fp6 PowVartime(this Fp6 self, ulong[] by) => PowVartime(Fp6.One, self, by); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fp12 PowVartime(this Fp12 self, ulong[] by) => PowVartime(Fp12.One, self, by); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Scalar PowVartime(this Scalar self, ulong[] by) => PowVartime(Scalar.One, self, by); +} diff --git a/src/Neo.Cryptography.BLS12_381/MathUtility.cs b/src/Neo.Cryptography.BLS12_381/MathUtility.cs new file mode 100644 index 0000000000..3721b482cc --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/MathUtility.cs @@ -0,0 +1,67 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MathUtility.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.Cryptography.BLS12_381; + +static class MathUtility +{ + public static (ulong result, ulong carry) Adc(ulong a, ulong b, ulong carry) + { + ulong result = unchecked(a + b + carry); + carry = ((a & b) | ((a | b) & (~result))) >> 63; + return (result, carry); + } + + public static (ulong result, ulong borrow) Sbb(ulong a, ulong b, ulong borrow) + { + ulong result = unchecked(a - b - borrow); + borrow = (((~a) & b) | (~(a ^ b)) & result) >> 63; + return (result, borrow); + } + + public static (ulong low, ulong high) Mac(ulong z, ulong x, ulong y, ulong carry) + { + ulong high = BigMul(x, y, out ulong low); + (low, carry) = Adc(low, carry, 0); + (high, _) = Adc(high, 0, carry); + (low, carry) = Adc(low, z, 0); + (high, _) = Adc(high, 0, carry); + return (low, high); + } + + /// Produces the full product of two unsigned 64-bit numbers. + /// The first number to multiply. + /// The second number to multiply. + /// The low 64-bit of the product of the specified numbers. + /// The high 64-bit of the product of the specified numbers. + public static ulong BigMul(ulong a, ulong b, out ulong low) + { + // Adaptation of algorithm for multiplication + // of 32-bit unsigned integers described + // in Hacker's Delight by Henry S. Warren, Jr. (ISBN 0-201-91465-4), Chapter 8 + // Basically, it's an optimized version of FOIL method applied to + // low and high dwords of each operand + + // Use 32-bit uints to optimize the fallback for 32-bit platforms. + uint al = (uint)a; + uint ah = (uint)(a >> 32); + uint bl = (uint)b; + uint bh = (uint)(b >> 32); + + ulong mull = ((ulong)al) * bl; + ulong t = ((ulong)ah) * bl + (mull >> 32); + ulong tl = ((ulong)al) * bh + (uint)t; + + low = tl << 32 | (uint)mull; + + return ((ulong)ah) * bh + (t >> 32) + (tl >> 32); + } +} diff --git a/src/Neo.Cryptography.BLS12_381/MillerLoopResult.cs b/src/Neo.Cryptography.BLS12_381/MillerLoopResult.cs new file mode 100644 index 0000000000..7f9bdfbb60 --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/MillerLoopResult.cs @@ -0,0 +1,143 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MillerLoopResult.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.Runtime.InteropServices; +using static Neo.Cryptography.BLS12_381.Constants; + +namespace Neo.Cryptography.BLS12_381; + +[StructLayout(LayoutKind.Explicit, Size = Fp12.Size)] +readonly struct MillerLoopResult +{ + [FieldOffset(0)] + private readonly Fp12 v; + + public MillerLoopResult(in Fp12 v) + { + this.v = v; + } + + public Gt FinalExponentiation() + { + static (Fp2, Fp2) Fp4Square(in Fp2 a, in Fp2 b) + { + var t0 = a.Square(); + var t1 = b.Square(); + var t2 = t1.MulByNonresidue(); + var c0 = t2 + t0; + t2 = a + b; + t2 = t2.Square(); + t2 -= t0; + var c1 = t2 - t1; + + return (c0, c1); + } + + static Fp12 CyclotomicSquare(in Fp12 f) + { + var z0 = f.C0.C0; + var z4 = f.C0.C1; + var z3 = f.C0.C2; + var z2 = f.C1.C0; + var z1 = f.C1.C1; + var z5 = f.C1.C2; + + var (t0, t1) = Fp4Square(in z0, in z1); + + // For A + z0 = t0 - z0; + z0 = z0 + z0 + t0; + + z1 = t1 + z1; + z1 = z1 + z1 + t1; + + (t0, t1) = Fp4Square(in z2, in z3); + var (t2, t3) = Fp4Square(in z4, in z5); + + // For C + z4 = t0 - z4; + z4 = z4 + z4 + t0; + + z5 = t1 + z5; + z5 = z5 + z5 + t1; + + // For B + t0 = t3.MulByNonresidue(); + z2 = t0 + z2; + z2 = z2 + z2 + t0; + + z3 = t2 - z3; + z3 = z3 + z3 + t2; + + return new Fp12(new Fp6(in z0, in z4, in z3), new Fp6(in z2, in z1, in z5)); + } + + static Fp12 CycolotomicExp(in Fp12 f) + { + var x = BLS_X; + var tmp = Fp12.One; + var found_one = false; + foreach (bool i in Enumerable.Range(0, 64).Select(b => ((x >> b) & 1) == 1).Reverse()) + { + if (found_one) + tmp = CyclotomicSquare(tmp); + else + found_one = i; + + if (i) + tmp *= f; + } + + return tmp.Conjugate(); + } + + var f = v; + var t0 = f + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap(); + var t1 = f.Invert(); + var t2 = t0 * t1; + t1 = t2; + t2 = t2.FrobeniusMap().FrobeniusMap(); + t2 *= t1; + t1 = CyclotomicSquare(t2).Conjugate(); + var t3 = CycolotomicExp(t2); + var t4 = CyclotomicSquare(t3); + var t5 = t1 * t3; + t1 = CycolotomicExp(t5); + t0 = CycolotomicExp(t1); + var t6 = CycolotomicExp(t0); + t6 *= t4; + t4 = CycolotomicExp(t6); + t5 = t5.Conjugate(); + t4 *= t5 * t2; + t5 = t2.Conjugate(); + t1 *= t2; + t1 = t1.FrobeniusMap().FrobeniusMap().FrobeniusMap(); + t6 *= t5; + t6 = t6.FrobeniusMap(); + t3 *= t0; + t3 = t3.FrobeniusMap().FrobeniusMap(); + t3 *= t1; + t3 *= t6; + f = t3 * t4; + return new Gt(f); + } + + public static MillerLoopResult operator +(in MillerLoopResult a, in MillerLoopResult b) + { + return new(a.v * b.v); + } +} diff --git a/src/Neo.Cryptography.BLS12_381/MillerLoopUtility.cs b/src/Neo.Cryptography.BLS12_381/MillerLoopUtility.cs new file mode 100644 index 0000000000..03187b7944 --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/MillerLoopUtility.cs @@ -0,0 +1,120 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MillerLoopUtility.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 static Neo.Cryptography.BLS12_381.Constants; + +namespace Neo.Cryptography.BLS12_381; + +static class MillerLoopUtility +{ + public static T MillerLoop(D driver) where D : IMillerLoopDriver + { + var f = driver.One; + + var found_one = false; + foreach (var i in Enumerable.Range(0, 64).Reverse().Select(b => ((BLS_X >> 1 >> b) & 1) == 1)) + { + if (!found_one) + { + found_one = i; + continue; + } + + f = driver.DoublingStep(f); + + if (i) + f = driver.AdditionStep(f); + + f = driver.Square(f); + } + + f = driver.DoublingStep(f); + + if (BLS_X_IS_NEGATIVE) + f = driver.Conjugate(f); + + return f; + } + + public static Fp12 Ell(in Fp12 f, in (Fp2 X, Fp2 Y, Fp2 Z) coeffs, in G1Affine p) + { + var c0 = new Fp2(coeffs.X.C0 * p.Y, coeffs.X.C1 * p.Y); + var c1 = new Fp2(coeffs.Y.C0 * p.X, coeffs.Y.C1 * p.X); + return f.MulBy_014(in coeffs.Z, in c1, in c0); + } + + public static (Fp2, Fp2, Fp2) DoublingStep(ref G2Projective r) + { + // Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf + var tmp0 = r.X.Square(); + var tmp1 = r.Y.Square(); + var tmp2 = tmp1.Square(); + var tmp3 = (tmp1 + r.X).Square() - tmp0 - tmp2; + tmp3 += tmp3; + var tmp4 = tmp0 + tmp0 + tmp0; + var tmp6 = r.X + tmp4; + var tmp5 = tmp4.Square(); + var zsquared = r.Z.Square(); + var x = tmp5 - tmp3 - tmp3; + var z = (r.Z + r.Y).Square() - tmp1 - zsquared; + var y = (tmp3 - x) * tmp4; + tmp2 += tmp2; + tmp2 += tmp2; + tmp2 += tmp2; + y -= tmp2; + r = new(in x, in y, in z); + tmp3 = tmp4 * zsquared; + tmp3 += tmp3; + tmp3 = -tmp3; + tmp6 = tmp6.Square() - tmp0 - tmp5; + tmp1 += tmp1; + tmp1 += tmp1; + tmp6 -= tmp1; + tmp0 = r.Z * zsquared; + tmp0 += tmp0; + + return (tmp0, tmp3, tmp6); + } + + public static (Fp2, Fp2, Fp2) AdditionStep(ref G2Projective r, in G2Affine q) + { + // Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf + var zsquared = r.Z.Square(); + var ysquared = q.Y.Square(); + var t0 = zsquared * q.X; + var t1 = ((q.Y + r.Z).Square() - ysquared - zsquared) * zsquared; + var t2 = t0 - r.X; + var t3 = t2.Square(); + var t4 = t3 + t3; + t4 += t4; + var t5 = t4 * t2; + var t6 = t1 - r.Y - r.Y; + var t9 = t6 * q.X; + var t7 = t4 * r.X; + var x = t6.Square() - t5 - t7 - t7; + var z = (r.Z + t2).Square() - zsquared - t3; + var t10 = q.Y + z; + var t8 = (t7 - x) * t6; + t0 = r.Y * t5; + t0 += t0; + var y = t8 - t0; + r = new(in x, in y, in z); + t10 = t10.Square() - ysquared; + var ztsquared = r.Z.Square(); + t10 -= ztsquared; + t9 = t9 + t9 - t10; + t10 = r.Z + r.Z; + t6 = -t6; + t1 = t6 + t6; + + return (t10, t1, t9); + } +} diff --git a/src/Neo.Cryptography.BLS12_381/Neo.Cryptography.BLS12_381.csproj b/src/Neo.Cryptography.BLS12_381/Neo.Cryptography.BLS12_381.csproj new file mode 100644 index 0000000000..d5861b2e45 --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/Neo.Cryptography.BLS12_381.csproj @@ -0,0 +1,15 @@ + + + + 0.3.0 + netstandard2.1;net7.0 + enable + enable + 11.0 + + + + + + + diff --git a/src/Neo.Cryptography.BLS12_381/Properties/AssemblyInfo.cs b/src/Neo.Cryptography.BLS12_381/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..998a53f179 --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/Properties/AssemblyInfo.cs @@ -0,0 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// AssemblyInfo.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.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Neo.Cryptography.BLS12_381.Tests")] diff --git a/src/Neo.Cryptography.BLS12_381/Scalar.cs b/src/Neo.Cryptography.BLS12_381/Scalar.cs new file mode 100644 index 0000000000..ef40e9b817 --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/Scalar.cs @@ -0,0 +1,513 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Scalar.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.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; +using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; +using static Neo.Cryptography.BLS12_381.MathUtility; +using static Neo.Cryptography.BLS12_381.ScalarConstants; + +namespace Neo.Cryptography.BLS12_381; + +[StructLayout(LayoutKind.Explicit, Size = Size)] +public readonly struct Scalar : IEquatable, INumber +{ + public const int Size = 32; + public const int SizeL = Size / sizeof(ulong); + public static readonly Scalar Default = new(); + + public static ref readonly Scalar Zero => ref Default; + public static ref readonly Scalar One => ref R; + + public bool IsZero => this == Zero; + + internal Scalar(ulong[] values) + { + if (values.Length != SizeL) + throw new FormatException($"The argument `{nameof(values)}` must contain {SizeL} entries."); + + // This internal method is only used by the constants classes. + // The data must be in the correct format. + // So, there is no need to do any additional checks. + this = Unsafe.As(ref MemoryMarshal.GetReference(MemoryMarshal.Cast(values))); + } + + public Scalar(ulong value) + { + Span data = stackalloc ulong[SizeL]; + data[0] = value; + this = FromRaw(data); + } + + public Scalar(RandomNumberGenerator rng) + { + Span buffer = stackalloc byte[Size * 2]; + rng.GetBytes(buffer); + this = FromBytesWide(buffer); + } + + public static Scalar FromBytes(ReadOnlySpan data) + { + if (data.Length != Size) + throw new FormatException($"The argument `{nameof(data)}` must contain {Size} bytes."); + + ref readonly Scalar ref_ = ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + + try + { + return ref_ * R2; + } + finally + { + ReadOnlySpan u64 = MemoryMarshal.Cast(data); + ulong borrow = 0; + (_, borrow) = Sbb(u64[0], MODULUS_LIMBS_64[0], borrow); + (_, borrow) = Sbb(u64[1], MODULUS_LIMBS_64[1], borrow); + (_, borrow) = Sbb(u64[2], MODULUS_LIMBS_64[2], borrow); + (_, borrow) = Sbb(u64[3], MODULUS_LIMBS_64[3], borrow); + if (borrow == 0) + { + // If the element is smaller than MODULUS then the subtraction will underflow. + // Otherwise, throws. + // Why not throw before return? + // Because we want to run the method in a constant time. + throw new FormatException(); + } + } + } + + public static Scalar FromBytesWide(ReadOnlySpan data) + { + if (data.Length != Size * 2) + throw new FormatException($"The argument `{nameof(data)}` must contain {Size * 2} bytes."); + + ReadOnlySpan d = MemoryMarshal.Cast(data); + return d[0] * R2 + d[1] * R3; + } + + public static Scalar FromRaw(ReadOnlySpan data) + { + if (data.Length != SizeL) + throw new FormatException($"The argument `{nameof(data)}` must contain {SizeL} entries."); + + ReadOnlySpan span = MemoryMarshal.Cast(data); + return span[0] * R2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ReadOnlySpan GetSpan() + { + return MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in this), 1)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Span GetSpanU64() + { + return MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref Unsafe.AsRef(in this), 1)); + } + + public override string ToString() + { + byte[] bytes = ToArray(); + StringBuilder sb = new(); + sb.Append("0x"); + for (int i = bytes.Length - 1; i >= 0; i--) + sb.AppendFormat("{0:x2}", bytes[i]); + return sb.ToString(); + } + + public static bool operator ==(in Scalar a, in Scalar b) + { + return ConstantTimeEq(in a, in b); + } + + public static bool operator !=(in Scalar a, in Scalar b) + { + return !(a == b); + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not Scalar other) return false; + return this == other; + } + + public bool Equals(Scalar other) + { + return this == other; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public Scalar Double() + { + return this + this; + } + + public byte[] ToArray() + { + ReadOnlySpan self = GetSpanU64(); + + // Turn into canonical form by computing + // (a.R) / R = a + Scalar result = MontgomeryReduce(self[0], self[1], self[2], self[3], 0, 0, 0, 0); + return result.GetSpan().ToArray(); + } + + public Scalar Square() + { + ReadOnlySpan self = GetSpanU64(); + ulong r0, r1, r2, r3, r4, r5, r6, r7; + ulong carry; + + (r1, carry) = Mac(0, self[0], self[1], 0); + (r2, carry) = Mac(0, self[0], self[2], carry); + (r3, r4) = Mac(0, self[0], self[3], carry); + + (r3, carry) = Mac(r3, self[1], self[2], 0); + (r4, r5) = Mac(r4, self[1], self[3], carry); + + (r5, r6) = Mac(r5, self[2], self[3], 0); + + r7 = r6 >> 63; + r6 = (r6 << 1) | (r5 >> 63); + r5 = (r5 << 1) | (r4 >> 63); + r4 = (r4 << 1) | (r3 >> 63); + r3 = (r3 << 1) | (r2 >> 63); + r2 = (r2 << 1) | (r1 >> 63); + r1 <<= 1; + + (r0, carry) = Mac(0, self[0], self[0], 0); + (r1, carry) = Adc(r1, carry, 0); + (r2, carry) = Mac(r2, self[1], self[1], carry); + (r3, carry) = Adc(r3, carry, 0); + (r4, carry) = Mac(r4, self[2], self[2], carry); + (r5, carry) = Adc(r5, carry, 0); + (r6, carry) = Mac(r6, self[3], self[3], carry); + (r7, _) = Adc(r7, carry, 0); + + return MontgomeryReduce(r0, r1, r2, r3, r4, r5, r6, r7); + } + + public Scalar Sqrt() + { + // Tonelli-Shank's algorithm for q mod 16 = 1 + // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) + + // w = self^((t - 1) // 2) + // = self^6104339283789297388802252303364915521546564123189034618274734669823 + var w = this.PowVartime(new ulong[] + { + 0x7fff_2dff_7fff_ffff, + 0x04d0_ec02_a9de_d201, + 0x94ce_bea4_199c_ec04, + 0x0000_0000_39f6_d3a9 + }); + + var v = S; + var x = this * w; + var b = x * w; + + // Initialize z as the 2^S root of unity. + var z = ROOT_OF_UNITY; + + for (uint max_v = S; max_v >= 1; max_v--) + { + uint k = 1; + var tmp = b.Square(); + var j_less_than_v = true; + + for (uint j = 2; j < max_v; j++) + { + var tmp_is_one = tmp == One; + var squared = ConditionalSelect(in tmp, in z, tmp_is_one).Square(); + tmp = ConditionalSelect(in squared, in tmp, tmp_is_one); + var new_z = ConditionalSelect(in z, in squared, tmp_is_one); + j_less_than_v &= j != v; + k = ConditionalSelect(j, k, tmp_is_one); + z = ConditionalSelect(in z, in new_z, j_less_than_v); + } + + var result = x * z; + x = ConditionalSelect(in result, in x, b == One); + z = z.Square(); + b *= z; + v = k; + } + + if (x * x != this) throw new ArithmeticException(); + return x; + } + + public Scalar Pow(ulong[] by) + { + if (by.Length != SizeL) + throw new ArgumentException($"The length of the parameter `{nameof(by)}` must be {SizeL}."); + + var res = One; + for (int j = by.Length - 1; j >= 0; j--) + { + for (int i = 63; i >= 0; i--) + { + res = res.Square(); + var tmp = res; + tmp *= this; + res.ConditionalAssign(in tmp, ((by[j] >> i) & 1) == 1); + } + } + return res; + } + + public Scalar Invert() + { + static void SquareAssignMulti(ref Scalar n, int num_times) + { + for (int i = 0; i < num_times; i++) + { + n = n.Square(); + } + } + + var t0 = Square(); + var t1 = t0 * this; + var t16 = t0.Square(); + var t6 = t16.Square(); + var t5 = t6 * t0; + t0 = t6 * t16; + var t12 = t5 * t16; + var t2 = t6.Square(); + var t7 = t5 * t6; + var t15 = t0 * t5; + var t17 = t12.Square(); + t1 *= t17; + var t3 = t7 * t2; + var t8 = t1 * t17; + var t4 = t8 * t2; + var t9 = t8 * t7; + t7 = t4 * t5; + var t11 = t4 * t17; + t5 = t9 * t17; + var t14 = t7 * t15; + var t13 = t11 * t12; + t12 = t11 * t17; + t15 *= t12; + t16 *= t15; + t3 *= t16; + t17 *= t3; + t0 *= t17; + t6 *= t0; + t2 *= t6; + SquareAssignMulti(ref t0, 8); + t0 *= t17; + SquareAssignMulti(ref t0, 9); + t0 *= t16; + SquareAssignMulti(ref t0, 9); + t0 *= t15; + SquareAssignMulti(ref t0, 9); + t0 *= t15; + SquareAssignMulti(ref t0, 7); + t0 *= t14; + SquareAssignMulti(ref t0, 7); + t0 *= t13; + SquareAssignMulti(ref t0, 10); + t0 *= t12; + SquareAssignMulti(ref t0, 9); + t0 *= t11; + SquareAssignMulti(ref t0, 8); + t0 *= t8; + SquareAssignMulti(ref t0, 8); + t0 *= this; + SquareAssignMulti(ref t0, 14); + t0 *= t9; + SquareAssignMulti(ref t0, 10); + t0 *= t8; + SquareAssignMulti(ref t0, 15); + t0 *= t7; + SquareAssignMulti(ref t0, 10); + t0 *= t6; + SquareAssignMulti(ref t0, 8); + t0 *= t5; + SquareAssignMulti(ref t0, 16); + t0 *= t3; + SquareAssignMulti(ref t0, 8); + t0 *= t2; + SquareAssignMulti(ref t0, 7); + t0 *= t4; + SquareAssignMulti(ref t0, 9); + t0 *= t2; + SquareAssignMulti(ref t0, 8); + t0 *= t3; + SquareAssignMulti(ref t0, 8); + t0 *= t2; + SquareAssignMulti(ref t0, 8); + t0 *= t2; + SquareAssignMulti(ref t0, 8); + t0 *= t2; + SquareAssignMulti(ref t0, 8); + t0 *= t3; + SquareAssignMulti(ref t0, 8); + t0 *= t2; + SquareAssignMulti(ref t0, 8); + t0 *= t2; + SquareAssignMulti(ref t0, 5); + t0 *= t1; + SquareAssignMulti(ref t0, 5); + t0 *= t1; + + if (this == Zero) throw new DivideByZeroException(); + return t0; + } + + private static Scalar MontgomeryReduce(ulong r0, ulong r1, ulong r2, ulong r3, ulong r4, ulong r5, ulong r6, ulong r7) + { + // The Montgomery reduction here is based on Algorithm 14.32 in + // Handbook of Applied Cryptography + // . + + ulong carry, carry2; + + var k = unchecked(r0 * INV); + (_, carry) = Mac(r0, k, MODULUS_LIMBS_64[0], 0); + (r1, carry) = Mac(r1, k, MODULUS_LIMBS_64[1], carry); + (r2, carry) = Mac(r2, k, MODULUS_LIMBS_64[2], carry); + (r3, carry) = Mac(r3, k, MODULUS_LIMBS_64[3], carry); + (r4, carry2) = Adc(r4, 0, carry); + + k = unchecked(r1 * INV); + (_, carry) = Mac(r1, k, MODULUS_LIMBS_64[0], 0); + (r2, carry) = Mac(r2, k, MODULUS_LIMBS_64[1], carry); + (r3, carry) = Mac(r3, k, MODULUS_LIMBS_64[2], carry); + (r4, carry) = Mac(r4, k, MODULUS_LIMBS_64[3], carry); + (r5, carry2) = Adc(r5, carry2, carry); + + k = unchecked(r2 * INV); + (_, carry) = Mac(r2, k, MODULUS_LIMBS_64[0], 0); + (r3, carry) = Mac(r3, k, MODULUS_LIMBS_64[1], carry); + (r4, carry) = Mac(r4, k, MODULUS_LIMBS_64[2], carry); + (r5, carry) = Mac(r5, k, MODULUS_LIMBS_64[3], carry); + (r6, carry2) = Adc(r6, carry2, carry); + + k = unchecked(r3 * INV); + (_, carry) = Mac(r3, k, MODULUS_LIMBS_64[0], 0); + (r4, carry) = Mac(r4, k, MODULUS_LIMBS_64[1], carry); + (r5, carry) = Mac(r5, k, MODULUS_LIMBS_64[2], carry); + (r6, carry) = Mac(r6, k, MODULUS_LIMBS_64[3], carry); + (r7, _) = Adc(r7, carry2, carry); + + // Result may be within MODULUS of the correct value + ReadOnlySpan tmp = stackalloc[] { r4, r5, r6, r7 }; + return MemoryMarshal.Cast(tmp)[0] - MODULUS; + } + + public static Scalar operator *(in Scalar a, in Scalar b) + { + ReadOnlySpan self = a.GetSpanU64(), rhs = b.GetSpanU64(); + ulong r0, r1, r2, r3, r4, r5, r6, r7; + ulong carry; + + (r0, carry) = Mac(0, self[0], rhs[0], 0); + (r1, carry) = Mac(0, self[0], rhs[1], carry); + (r2, carry) = Mac(0, self[0], rhs[2], carry); + (r3, r4) = Mac(0, self[0], rhs[3], carry); + + (r1, carry) = Mac(r1, self[1], rhs[0], 0); + (r2, carry) = Mac(r2, self[1], rhs[1], carry); + (r3, carry) = Mac(r3, self[1], rhs[2], carry); + (r4, r5) = Mac(r4, self[1], rhs[3], carry); + + (r2, carry) = Mac(r2, self[2], rhs[0], 0); + (r3, carry) = Mac(r3, self[2], rhs[1], carry); + (r4, carry) = Mac(r4, self[2], rhs[2], carry); + (r5, r6) = Mac(r5, self[2], rhs[3], carry); + + (r3, carry) = Mac(r3, self[3], rhs[0], 0); + (r4, carry) = Mac(r4, self[3], rhs[1], carry); + (r5, carry) = Mac(r5, self[3], rhs[2], carry); + (r6, r7) = Mac(r6, self[3], rhs[3], carry); + + return MontgomeryReduce(r0, r1, r2, r3, r4, r5, r6, r7); + } + + public static Scalar operator -(in Scalar a, in Scalar b) + { + ReadOnlySpan self = a.GetSpanU64(), rhs = b.GetSpanU64(); + ulong d0, d1, d2, d3; + ulong carry, borrow; + + (d0, borrow) = Sbb(self[0], rhs[0], 0); + (d1, borrow) = Sbb(self[1], rhs[1], borrow); + (d2, borrow) = Sbb(self[2], rhs[2], borrow); + (d3, borrow) = Sbb(self[3], rhs[3], borrow); + + borrow = borrow == 0 ? ulong.MinValue : ulong.MaxValue; + (d0, carry) = Adc(d0, MODULUS_LIMBS_64[0] & borrow, 0); + (d1, carry) = Adc(d1, MODULUS_LIMBS_64[1] & borrow, carry); + (d2, carry) = Adc(d2, MODULUS_LIMBS_64[2] & borrow, carry); + (d3, _) = Adc(d3, MODULUS_LIMBS_64[3] & borrow, carry); + + ReadOnlySpan tmp = stackalloc[] { d0, d1, d2, d3 }; + return MemoryMarshal.Cast(tmp)[0]; + } + + public static Scalar operator +(in Scalar a, in Scalar b) + { + ReadOnlySpan self = a.GetSpanU64(), rhs = b.GetSpanU64(); + ulong d0, d1, d2, d3; + ulong carry; + + (d0, carry) = Adc(self[0], rhs[0], 0); + (d1, carry) = Adc(self[1], rhs[1], carry); + (d2, carry) = Adc(self[2], rhs[2], carry); + (d3, _) = Adc(self[3], rhs[3], carry); + + // Attempt to subtract the modulus, to ensure the value + // is smaller than the modulus. + ReadOnlySpan tmp = stackalloc[] { d0, d1, d2, d3 }; + return MemoryMarshal.Cast(tmp)[0] - MODULUS; + } + + public static Scalar operator -(in Scalar a) + { + ReadOnlySpan self = a.GetSpanU64(); + ulong d0, d1, d2, d3; + ulong borrow; + + // Subtract `self` from `MODULUS` to negate. Ignore the final + // borrow because it cannot underflow; self is guaranteed to + // be in the field. + (d0, borrow) = Sbb(MODULUS_LIMBS_64[0], self[0], 0); + (d1, borrow) = Sbb(MODULUS_LIMBS_64[1], self[1], borrow); + (d2, borrow) = Sbb(MODULUS_LIMBS_64[2], self[2], borrow); + (d3, _) = Sbb(MODULUS_LIMBS_64[3], self[3], borrow); + + // `tmp` could be `MODULUS` if `self` was zero. Create a mask that is + // zero if `self` was zero, and `u64::max_value()` if self was nonzero. + ulong mask = a.IsZero ? ulong.MinValue : ulong.MaxValue; + + ReadOnlySpan tmp = stackalloc[] { d0 & mask, d1 & mask, d2 & mask, d3 & mask }; + return MemoryMarshal.Cast(tmp)[0]; + } + + #region Instance math methods + + public Scalar Negate() => -this; + public Scalar Multiply(in Scalar value) => this * value; + public Scalar Sum(in Scalar value) => this + value; + public Scalar Subtract(in Scalar value) => this - value; + + #endregion +} diff --git a/src/Neo.Cryptography.BLS12_381/ScalarConstants.cs b/src/Neo.Cryptography.BLS12_381/ScalarConstants.cs new file mode 100644 index 0000000000..56bab5b067 --- /dev/null +++ b/src/Neo.Cryptography.BLS12_381/ScalarConstants.cs @@ -0,0 +1,96 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ScalarConstants.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.Cryptography.BLS12_381; + +static class ScalarConstants +{ + // The modulus as u32 limbs. + public static readonly uint[] MODULUS_LIMBS_32 = + { + 0x0000_0001, + 0xffff_ffff, + 0xfffe_5bfe, + 0x53bd_a402, + 0x09a1_d805, + 0x3339_d808, + 0x299d_7d48, + 0x73ed_a753 + }; + + // The modulus as u64 limbs. + public static readonly ulong[] MODULUS_LIMBS_64 = + { + 0xffff_ffff_0000_0001, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48 + }; + + // q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 + public static readonly Scalar MODULUS = new(MODULUS_LIMBS_64); + + // The number of bits needed to represent the modulus. + public const uint MODULUS_BITS = 255; + + // GENERATOR = 7 (multiplicative generator of r-1 order, that is also quadratic nonresidue) + public static readonly Scalar GENERATOR = new(new ulong[] + { + 0x0000_000e_ffff_fff1, + 0x17e3_63d3_0018_9c0f, + 0xff9c_5787_6f84_57b0, + 0x3513_3220_8fc5_a8c4 + }); + + // INV = -(q^{-1} mod 2^64) mod 2^64 + public const ulong INV = 0xffff_fffe_ffff_ffff; + + // R = 2^256 mod q + public static readonly Scalar R = new(new ulong[] + { + 0x0000_0001_ffff_fffe, + 0x5884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff5, + 0x1824_b159_acc5_056f + }); + + // R^2 = 2^512 mod q + public static readonly Scalar R2 = new(new ulong[] + { + 0xc999_e990_f3f2_9c6d, + 0x2b6c_edcb_8792_5c23, + 0x05d3_1496_7254_398f, + 0x0748_d9d9_9f59_ff11 + }); + + // R^3 = 2^768 mod q + public static readonly Scalar R3 = new(new ulong[] + { + 0xc62c_1807_439b_73af, + 0x1b3e_0d18_8cf0_6990, + 0x73d1_3c71_c7b5_f418, + 0x6e2a_5bb9_c8db_33e9 + }); + + // 2^S * t = MODULUS - 1 with t odd + public const uint S = 32; + + // GENERATOR^t where t * 2^s + 1 = q with t odd. + // In other words, this is a 2^s root of unity. + // `GENERATOR = 7 mod q` is a generator of the q - 1 order multiplicative subgroup. + public static readonly Scalar ROOT_OF_UNITY = new(new ulong[] + { + 0xb9b5_8d8c_5f0e_466a, + 0x5b1b_4c80_1819_d7ec, + 0x0af5_3ae3_52a3_1e64, + 0x5bf3_adda_19e9_b27b + }); +} diff --git a/src/neo/LogLevel.cs b/src/Neo.Extensions/LogLevel.cs similarity index 73% rename from src/neo/LogLevel.cs rename to src/Neo.Extensions/LogLevel.cs index b8892a65d9..5e3acca37c 100644 --- a/src/neo/LogLevel.cs +++ b/src/Neo.Extensions/LogLevel.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// LogLevel.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. diff --git a/src/Neo.Extensions/Neo.Extensions.csproj b/src/Neo.Extensions/Neo.Extensions.csproj new file mode 100644 index 0000000000..7552a56311 --- /dev/null +++ b/src/Neo.Extensions/Neo.Extensions.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.1;net7.0 + enable + NEO;Blockchain;Extensions + + + + + + + + + + + + + diff --git a/src/neo/Utility.cs b/src/Neo.Extensions/Utility.cs similarity index 74% rename from src/neo/Utility.cs rename to src/Neo.Extensions/Utility.cs index 377decb1fb..fca164a4f2 100644 --- a/src/neo/Utility.cs +++ b/src/Neo.Extensions/Utility.cs @@ -1,20 +1,22 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Utility.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 Akka.Actor; using Akka.Event; -using Neo.Plugins; using System.Text; namespace Neo { + public delegate void LogEventHandler(string source, LogLevel level, object message); + /// /// A utility class that provides common functions. /// @@ -29,6 +31,8 @@ public Logger() } } + public static event LogEventHandler? Logging; + /// /// A strict UTF8 encoding used in NEO system. /// @@ -49,8 +53,7 @@ static Utility() /// The message of the log. public static void Log(string source, LogLevel level, object message) { - foreach (ILogPlugin plugin in Plugin.Loggers) - plugin.Log(source, level, message); + Logging?.Invoke(source, level, message); } } } diff --git a/src/Neo.GUI/GUI/BulkPayDialog.Designer.cs b/src/Neo.GUI/GUI/BulkPayDialog.Designer.cs new file mode 100644 index 0000000000..41ad15e57a --- /dev/null +++ b/src/Neo.GUI/GUI/BulkPayDialog.Designer.cs @@ -0,0 +1,129 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class BulkPayDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(BulkPayDialog)); + this.textBox3 = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.comboBox1 = new System.Windows.Forms.ComboBox(); + this.label3 = new System.Windows.Forms.Label(); + this.button1 = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // textBox3 + // + resources.ApplyResources(this.textBox3, "textBox3"); + this.textBox3.Name = "textBox3"; + this.textBox3.ReadOnly = true; + // + // label4 + // + resources.ApplyResources(this.label4, "label4"); + this.label4.Name = "label4"; + // + // comboBox1 + // + resources.ApplyResources(this.comboBox1, "comboBox1"); + this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBox1.FormattingEnabled = true; + this.comboBox1.Name = "comboBox1"; + this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged); + // + // label3 + // + resources.ApplyResources(this.label3, "label3"); + this.label3.Name = "label3"; + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.DialogResult = System.Windows.Forms.DialogResult.OK; + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.textBox1); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // textBox1 + // + this.textBox1.AcceptsReturn = true; + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.Name = "textBox1"; + this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged); + // + // BulkPayDialog + // + resources.ApplyResources(this, "$this"); + this.AcceptButton = this.button1; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.textBox3); + this.Controls.Add(this.label4); + this.Controls.Add(this.comboBox1); + this.Controls.Add(this.label3); + this.Controls.Add(this.button1); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "BulkPayDialog"; + this.ShowInTaskbar = false; + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox textBox3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.ComboBox comboBox1; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TextBox textBox1; + } +} diff --git a/src/Neo.GUI/GUI/BulkPayDialog.cs b/src/Neo.GUI/GUI/BulkPayDialog.cs new file mode 100644 index 0000000000..b67f9ae61c --- /dev/null +++ b/src/Neo.GUI/GUI/BulkPayDialog.cs @@ -0,0 +1,81 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// BulkPayDialog.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.Wallets; +using System; +using System.Linq; +using System.Windows.Forms; +using static Neo.Program; + +namespace Neo.GUI +{ + internal partial class BulkPayDialog : Form + { + public BulkPayDialog(AssetDescriptor asset = null) + { + InitializeComponent(); + if (asset == null) + { + foreach (UInt160 assetId in NEP5Watched) + { + try + { + comboBox1.Items.Add(new AssetDescriptor(Service.NeoSystem.StoreView, Service.NeoSystem.Settings, assetId)); + } + catch (ArgumentException) + { + continue; + } + } + } + else + { + comboBox1.Items.Add(asset); + comboBox1.SelectedIndex = 0; + comboBox1.Enabled = false; + } + } + + public TxOutListBoxItem[] GetOutputs() + { + AssetDescriptor asset = (AssetDescriptor)comboBox1.SelectedItem; + return textBox1.Lines.Where(p => !string.IsNullOrWhiteSpace(p)).Select(p => + { + string[] line = p.Split(new[] { ' ', '\t', ',' }, StringSplitOptions.RemoveEmptyEntries); + return new TxOutListBoxItem + { + AssetName = asset.AssetName, + AssetId = asset.AssetId, + Value = BigDecimal.Parse(line[1], asset.Decimals), + ScriptHash = line[0].ToScriptHash(Service.NeoSystem.Settings.AddressVersion) + }; + }).Where(p => p.Value.Value != 0).ToArray(); + } + + private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) + { + if (comboBox1.SelectedItem is AssetDescriptor asset) + { + textBox3.Text = Service.CurrentWallet.GetAvailable(Service.NeoSystem.StoreView, asset.AssetId).ToString(); + } + else + { + textBox3.Text = ""; + } + textBox1_TextChanged(this, EventArgs.Empty); + } + + private void textBox1_TextChanged(object sender, EventArgs e) + { + button1.Enabled = comboBox1.SelectedIndex >= 0 && textBox1.TextLength > 0; + } + } +} diff --git a/src/Neo.GUI/GUI/BulkPayDialog.es-ES.resx b/src/Neo.GUI/GUI/BulkPayDialog.es-ES.resx new file mode 100644 index 0000000000..3aa43a7bae --- /dev/null +++ b/src/Neo.GUI/GUI/BulkPayDialog.es-ES.resx @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 24, 54 + + + 44, 17 + + + Saldo: + + + 22, 17 + + + 46, 17 + + + Activo: + + + Aceptar + + + Pagar a + + + Pago + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/BulkPayDialog.resx b/src/Neo.GUI/GUI/BulkPayDialog.resx new file mode 100644 index 0000000000..0a6c0c3d28 --- /dev/null +++ b/src/Neo.GUI/GUI/BulkPayDialog.resx @@ -0,0 +1,354 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Left, Right + + + + 74, 51 + + + 468, 23 + + + + 12 + + + textBox3 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + True + + + NoControl + + + 12, 54 + + + 56, 17 + + + 11 + + + Balance: + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + Top, Left, Right + + + 74, 14 + + + 468, 25 + + + 10 + + + comboBox1 + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + True + + + NoControl + + + 26, 17 + + + 42, 17 + + + 9 + + + Asset: + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 4 + + + Bottom, Right + + + False + + + NoControl + + + 467, 325 + + + 75, 23 + + + 17 + + + OK + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 5 + + + Top, Bottom, Left, Right + + + Fill + + + Consolas, 9pt + + + 3, 19 + + + True + + + Vertical + + + 524, 217 + + + 0 + + + False + + + textBox1 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + 12, 80 + + + 530, 239 + + + 18 + + + Pay to + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 7, 17 + + + 554, 360 + + + 微软雅黑, 9pt + + + 3, 4, 3, 4 + + + CenterScreen + + + Payment + + + BulkPayDialog + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/BulkPayDialog.zh-Hans.resx b/src/Neo.GUI/GUI/BulkPayDialog.zh-Hans.resx new file mode 100644 index 0000000000..e429e3bf5e --- /dev/null +++ b/src/Neo.GUI/GUI/BulkPayDialog.zh-Hans.resx @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 62, 51 + + + 480, 23 + + + 44, 17 + + + 余额: + + + 62, 14 + + + 480, 25 + + + 12, 17 + + + 44, 17 + + + 资产: + + + 确定 + + + 账户和金额 + + + 支付 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ChangePasswordDialog.Designer.cs b/src/Neo.GUI/GUI/ChangePasswordDialog.Designer.cs new file mode 100644 index 0000000000..0d9c6ea80c --- /dev/null +++ b/src/Neo.GUI/GUI/ChangePasswordDialog.Designer.cs @@ -0,0 +1,137 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class ChangePasswordDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ChangePasswordDialog)); + this.label1 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.textBox3 = new System.Windows.Forms.TextBox(); + this.button1 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // textBox1 + // + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.Name = "textBox1"; + this.textBox1.UseSystemPasswordChar = true; + this.textBox1.TextChanged += new System.EventHandler(this.textBox_TextChanged); + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // textBox2 + // + resources.ApplyResources(this.textBox2, "textBox2"); + this.textBox2.Name = "textBox2"; + this.textBox2.UseSystemPasswordChar = true; + this.textBox2.TextChanged += new System.EventHandler(this.textBox_TextChanged); + // + // label3 + // + resources.ApplyResources(this.label3, "label3"); + this.label3.Name = "label3"; + // + // textBox3 + // + resources.ApplyResources(this.textBox3, "textBox3"); + this.textBox3.Name = "textBox3"; + this.textBox3.UseSystemPasswordChar = true; + this.textBox3.TextChanged += new System.EventHandler(this.textBox_TextChanged); + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.DialogResult = System.Windows.Forms.DialogResult.OK; + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + // + // button2 + // + resources.ApplyResources(this.button2, "button2"); + this.button2.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.button2.Name = "button2"; + this.button2.UseVisualStyleBackColor = true; + // + // ChangePasswordDialog + // + this.AcceptButton = this.button1; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.button2; + this.Controls.Add(this.button2); + this.Controls.Add(this.button1); + this.Controls.Add(this.textBox3); + this.Controls.Add(this.label3); + this.Controls.Add(this.textBox2); + this.Controls.Add(this.label2); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ChangePasswordDialog"; + this.ShowInTaskbar = false; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox textBox3; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Button button2; + } +} diff --git a/src/Neo.GUI/GUI/ChangePasswordDialog.cs b/src/Neo.GUI/GUI/ChangePasswordDialog.cs new file mode 100644 index 0000000000..f6ea079e41 --- /dev/null +++ b/src/Neo.GUI/GUI/ChangePasswordDialog.cs @@ -0,0 +1,54 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ChangePasswordDialog.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; +using System.Windows.Forms; + +namespace Neo.GUI +{ + internal partial class ChangePasswordDialog : Form + { + public string OldPassword + { + get + { + return textBox1.Text; + } + set + { + textBox1.Text = value; + } + } + + public string NewPassword + { + get + { + return textBox2.Text; + } + set + { + textBox2.Text = value; + textBox3.Text = value; + } + } + + public ChangePasswordDialog() + { + InitializeComponent(); + } + + private void textBox_TextChanged(object sender, EventArgs e) + { + button1.Enabled = textBox1.TextLength > 0 && textBox2.TextLength > 0 && textBox3.Text == textBox2.Text; + } + } +} diff --git a/src/Neo.GUI/GUI/ChangePasswordDialog.es-ES.resx b/src/Neo.GUI/GUI/ChangePasswordDialog.es-ES.resx new file mode 100644 index 0000000000..27c58de2d0 --- /dev/null +++ b/src/Neo.GUI/GUI/ChangePasswordDialog.es-ES.resx @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 55, 15 + + + 115, 17 + + + Contraseña actual: + + + 177, 12 + + + 275, 23 + + + 55, 44 + + + 116, 17 + + + Nueva contraseña: + + + 177, 41 + + + 275, 23 + + + 159, 17 + + + Repetir nueva contraseña: + + + 177, 70 + + + 275, 23 + + + 296, 107 + + + Aceptar + + + 377, 107 + + + Cancelar + + + 464, 142 + + + Cambiar contraseña + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ChangePasswordDialog.resx b/src/Neo.GUI/GUI/ChangePasswordDialog.resx new file mode 100644 index 0000000000..89c4851043 --- /dev/null +++ b/src/Neo.GUI/GUI/ChangePasswordDialog.resx @@ -0,0 +1,369 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + True + + + + 41, 15 + + + 92, 17 + + + 0 + + + Old Password: + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 7 + + + + Top, Left, Right + + + 139, 12 + + + 234, 23 + + + 1 + + + textBox1 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 6 + + + True + + + NoControl + + + 36, 44 + + + 97, 17 + + + 2 + + + New Password: + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 5 + + + Top, Left, Right + + + 139, 41 + + + 234, 23 + + + 3 + + + textBox2 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 4 + + + True + + + NoControl + + + 12, 73 + + + 121, 17 + + + 4 + + + Re-Enter Password: + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + Top, Left, Right + + + 139, 70 + + + 234, 23 + + + 5 + + + textBox3 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + Bottom, Right + + + False + + + 217, 107 + + + 75, 23 + + + 6 + + + OK + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Bottom, Right + + + NoControl + + + 298, 107 + + + 75, 23 + + + 7 + + + Cancel + + + button2 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 7, 17 + + + 385, 142 + + + Microsoft YaHei UI, 9pt + + + 2, 2, 2, 2 + + + CenterScreen + + + Change Password + + + ChangePasswordDialog + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ChangePasswordDialog.zh-Hans.resx b/src/Neo.GUI/GUI/ChangePasswordDialog.zh-Hans.resx new file mode 100644 index 0000000000..9ec5cb724c --- /dev/null +++ b/src/Neo.GUI/GUI/ChangePasswordDialog.zh-Hans.resx @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 24, 15 + + + 47, 17 + + + 旧密码: + + + 77, 12 + + + 296, 23 + + + 24, 44 + + + 47, 17 + + + 新密码: + + + 77, 41 + + + 296, 23 + + + 59, 17 + + + 重复密码: + + + 77, 70 + + + 296, 23 + + + 确定 + + + 取消 + + + 修改密码 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ConsoleForm.Designer.cs b/src/Neo.GUI/GUI/ConsoleForm.Designer.cs new file mode 100644 index 0000000000..e3fd639db0 --- /dev/null +++ b/src/Neo.GUI/GUI/ConsoleForm.Designer.cs @@ -0,0 +1,91 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class ConsoleForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.textBox1 = new System.Windows.Forms.TextBox(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // textBox1 + // + this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBox1.Location = new System.Drawing.Point(12, 12); + this.textBox1.Font = new System.Drawing.Font("Consolas", 11.0f); + this.textBox1.MaxLength = 1048576; + this.textBox1.Multiline = true; + this.textBox1.Name = "textBox1"; + this.textBox1.ReadOnly = true; + this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.textBox1.Size = new System.Drawing.Size(609, 367); + this.textBox1.TabIndex = 1; + // + // textBox2 + // + this.textBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBox2.Location = new System.Drawing.Point(12, 385); + this.textBox2.Font = new System.Drawing.Font("Consolas", 11.0f); + this.textBox2.Name = "textBox2"; + this.textBox2.Size = new System.Drawing.Size(609, 21); + this.textBox2.TabIndex = 0; + this.textBox2.KeyDown += new System.Windows.Forms.KeyEventHandler(this.textBox2_KeyDown); + // + // ConsoleForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(633, 418); + this.Controls.Add(this.textBox2); + this.Controls.Add(this.textBox1); + this.Name = "ConsoleForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Console"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.TextBox textBox2; + } +} diff --git a/src/Neo.GUI/GUI/ConsoleForm.cs b/src/Neo.GUI/GUI/ConsoleForm.cs new file mode 100644 index 0000000000..ae1543980e --- /dev/null +++ b/src/Neo.GUI/GUI/ConsoleForm.cs @@ -0,0 +1,69 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ConsoleForm.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.ConsoleService; +using System; +using System.IO; +using System.Threading; +using System.Windows.Forms; + +namespace Neo.GUI +{ + internal partial class ConsoleForm : Form + { + private Thread thread; + private readonly QueueReader queue = new QueueReader(); + + public ConsoleForm() + { + InitializeComponent(); + } + + protected override void OnHandleCreated(EventArgs e) + { + base.OnHandleCreated(e); + Console.SetOut(new TextBoxWriter(textBox1)); + Console.SetIn(queue); + thread = new Thread(Program.Service.RunConsole); + thread.Start(); + } + + protected override void OnFormClosing(FormClosingEventArgs e) + { + queue.Enqueue($"exit{Environment.NewLine}"); + thread.Join(); + Console.SetIn(new StreamReader(Console.OpenStandardInput())); + Console.SetOut(new StreamWriter(Console.OpenStandardOutput())); + base.OnFormClosing(e); + } + + private void textBox2_KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Enter) + { + e.SuppressKeyPress = true; + string line = $"{textBox2.Text}{Environment.NewLine}"; + textBox1.AppendText(ConsoleHelper.ReadingPassword ? "***" : line); + switch (textBox2.Text.ToLower()) + { + case "clear": + textBox1.Clear(); + break; + case "exit": + Close(); + return; + } + queue.Enqueue(line); + textBox2.Clear(); + } + } + } +} diff --git a/src/Neo.GUI/GUI/ConsoleForm.resx b/src/Neo.GUI/GUI/ConsoleForm.resx new file mode 100644 index 0000000000..1af7de150c --- /dev/null +++ b/src/Neo.GUI/GUI/ConsoleForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/CreateMultiSigContractDialog.Designer.cs b/src/Neo.GUI/GUI/CreateMultiSigContractDialog.Designer.cs new file mode 100644 index 0000000000..e91c25c9b7 --- /dev/null +++ b/src/Neo.GUI/GUI/CreateMultiSigContractDialog.Designer.cs @@ -0,0 +1,153 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class CreateMultiSigContractDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CreateMultiSigContractDialog)); + this.button5 = new System.Windows.Forms.Button(); + this.button4 = new System.Windows.Forms.Button(); + this.textBox5 = new System.Windows.Forms.TextBox(); + this.label7 = new System.Windows.Forms.Label(); + this.listBox1 = new System.Windows.Forms.ListBox(); + this.numericUpDown2 = new System.Windows.Forms.NumericUpDown(); + this.label6 = new System.Windows.Forms.Label(); + this.button6 = new System.Windows.Forms.Button(); + this.button1 = new System.Windows.Forms.Button(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDown2)).BeginInit(); + this.SuspendLayout(); + // + // button5 + // + resources.ApplyResources(this.button5, "button5"); + this.button5.Name = "button5"; + this.button5.UseVisualStyleBackColor = true; + this.button5.Click += new System.EventHandler(this.button5_Click); + // + // button4 + // + resources.ApplyResources(this.button4, "button4"); + this.button4.Name = "button4"; + this.button4.UseVisualStyleBackColor = true; + this.button4.Click += new System.EventHandler(this.button4_Click); + // + // textBox5 + // + resources.ApplyResources(this.textBox5, "textBox5"); + this.textBox5.Name = "textBox5"; + this.textBox5.TextChanged += new System.EventHandler(this.textBox5_TextChanged); + // + // label7 + // + resources.ApplyResources(this.label7, "label7"); + this.label7.Name = "label7"; + // + // listBox1 + // + resources.ApplyResources(this.listBox1, "listBox1"); + this.listBox1.FormattingEnabled = true; + this.listBox1.Name = "listBox1"; + this.listBox1.SelectedIndexChanged += new System.EventHandler(this.listBox1_SelectedIndexChanged); + // + // numericUpDown2 + // + resources.ApplyResources(this.numericUpDown2, "numericUpDown2"); + this.numericUpDown2.Maximum = new decimal(new int[] { + 0, + 0, + 0, + 0}); + this.numericUpDown2.Name = "numericUpDown2"; + this.numericUpDown2.ValueChanged += new System.EventHandler(this.numericUpDown2_ValueChanged); + // + // label6 + // + resources.ApplyResources(this.label6, "label6"); + this.label6.Name = "label6"; + // + // button6 + // + resources.ApplyResources(this.button6, "button6"); + this.button6.DialogResult = System.Windows.Forms.DialogResult.OK; + this.button6.Name = "button6"; + this.button6.UseVisualStyleBackColor = true; + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + // + // CreateMultiSigContractDialog + // + this.AcceptButton = this.button6; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.button1; + this.Controls.Add(this.button1); + this.Controls.Add(this.button6); + this.Controls.Add(this.button5); + this.Controls.Add(this.button4); + this.Controls.Add(this.textBox5); + this.Controls.Add(this.label7); + this.Controls.Add(this.listBox1); + this.Controls.Add(this.numericUpDown2); + this.Controls.Add(this.label6); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "CreateMultiSigContractDialog"; + this.ShowInTaskbar = false; + ((System.ComponentModel.ISupportInitialize)(this.numericUpDown2)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button button5; + private System.Windows.Forms.Button button4; + private System.Windows.Forms.TextBox textBox5; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.ListBox listBox1; + private System.Windows.Forms.NumericUpDown numericUpDown2; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Button button6; + private System.Windows.Forms.Button button1; + } +} diff --git a/src/Neo.GUI/GUI/CreateMultiSigContractDialog.cs b/src/Neo.GUI/GUI/CreateMultiSigContractDialog.cs new file mode 100644 index 0000000000..5dd10f858f --- /dev/null +++ b/src/Neo.GUI/GUI/CreateMultiSigContractDialog.cs @@ -0,0 +1,72 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// CreateMultiSigContractDialog.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.Cryptography.ECC; +using Neo.SmartContract; +using Neo.Wallets; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using static Neo.Program; + +namespace Neo.GUI +{ + internal partial class CreateMultiSigContractDialog : Form + { + private ECPoint[] publicKeys; + + public CreateMultiSigContractDialog() + { + InitializeComponent(); + } + + public Contract GetContract() + { + publicKeys = listBox1.Items.OfType().Select(p => ECPoint.DecodePoint(p.HexToBytes(), ECCurve.Secp256r1)).ToArray(); + return Contract.CreateMultiSigContract((int)numericUpDown2.Value, publicKeys); + } + + public KeyPair GetKey() + { + HashSet hashSet = new HashSet(publicKeys); + return Service.CurrentWallet.GetAccounts().FirstOrDefault(p => p.HasKey && hashSet.Contains(p.GetKey().PublicKey))?.GetKey(); + } + + private void numericUpDown2_ValueChanged(object sender, EventArgs e) + { + button6.Enabled = numericUpDown2.Value > 0; + } + + private void listBox1_SelectedIndexChanged(object sender, EventArgs e) + { + button5.Enabled = listBox1.SelectedIndices.Count > 0; + } + + private void textBox5_TextChanged(object sender, EventArgs e) + { + button4.Enabled = textBox5.TextLength > 0; + } + + private void button4_Click(object sender, EventArgs e) + { + listBox1.Items.Add(textBox5.Text); + textBox5.Clear(); + numericUpDown2.Maximum = listBox1.Items.Count; + } + + private void button5_Click(object sender, EventArgs e) + { + listBox1.Items.RemoveAt(listBox1.SelectedIndex); + numericUpDown2.Maximum = listBox1.Items.Count; + } + } +} diff --git a/src/Neo.GUI/GUI/CreateMultiSigContractDialog.es-ES.resx b/src/Neo.GUI/GUI/CreateMultiSigContractDialog.es-ES.resx new file mode 100644 index 0000000000..c5eefc3279 --- /dev/null +++ b/src/Neo.GUI/GUI/CreateMultiSigContractDialog.es-ES.resx @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 573, 246 + + + 542, 246 + + + 168, 246 + + + 368, 23 + + + 147, 17 + + + Lista de claves públicas: + + + 168, 41 + + + 430, 199 + + + 168, 12 + + + 442, 275 + + + Confirmar + + + 523, 275 + + + Cancelar + + + + NoControl + + + 29, 14 + + + 133, 17 + + + Nº mínimo de firmas: + + + 610, 310 + + + Contrato con múltiples firmas + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/CreateMultiSigContractDialog.resx b/src/Neo.GUI/GUI/CreateMultiSigContractDialog.resx new file mode 100644 index 0000000000..fcd5dfc317 --- /dev/null +++ b/src/Neo.GUI/GUI/CreateMultiSigContractDialog.resx @@ -0,0 +1,399 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + False + + + + 425, 199 + + + 551, 310 + + + 114, 12 + + + button5 + + + $this + + + + 3, 4, 3, 4 + + + False + + + 514, 246 + + + True + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 12 + + + cancel + + + 5 + + + 14 + + + 12, 14 + + + button6 + + + textBox5 + + + 7, 17 + + + True + + + Min. Sig. Num.: + + + $this + + + Bottom, Right + + + 3 + + + 13 + + + Bottom, Right + + + 464, 275 + + + 114, 41 + + + 75, 23 + + + 93, 17 + + + confirm + + + $this + + + $this + + + numericUpDown2 + + + 483, 246 + + + 2 + + + System.Windows.Forms.ListBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 7 + + + 114, 246 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 15 + + + CenterScreen + + + False + + + 363, 23 + + + label7 + + + Bottom, Right + + + 25, 23 + + + 197, 23 + + + $this + + + 1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 383, 275 + + + Bottom, Right + + + listBox1 + + + 7 + + + 6 + + + $this + + + 4 + + + 微软雅黑, 9pt + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Bottom, Left, Right + + + button1 + + + False + + + 15, 41 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 11 + + + $this + + + 0 + + + 8 + + + CreateMultiSigContractDialog + + + $this + + + 25, 23 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + - + + + $this + + + button4 + + + 8 + + + True + + + 96, 17 + + + Multi-Signature + + + 10 + + + 75, 23 + + + 17 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 9 + + + label6 + + + Top, Bottom, Left, Right + + + Public Key List: + + + True + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/CreateMultiSigContractDialog.zh-Hans.resx b/src/Neo.GUI/GUI/CreateMultiSigContractDialog.zh-Hans.resx new file mode 100644 index 0000000000..acd731d2a1 --- /dev/null +++ b/src/Neo.GUI/GUI/CreateMultiSigContractDialog.zh-Hans.resx @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 491, 263 + + + 460, 263 + + + 99, 263 + + + 355, 23 + + + 34, 41 + + + 59, 17 + + + 公钥列表: + + + 99, 41 + + + 417, 216 + + + 99, 12 + + + 120, 23 + + + 83, 17 + + + 最小签名数量: + + + 360, 292 + + + 确定 + + + 441, 292 + + + 取消 + + + 528, 327 + + + 多方签名 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/CreateWalletDialog.cs b/src/Neo.GUI/GUI/CreateWalletDialog.cs new file mode 100644 index 0000000000..770cd07cac --- /dev/null +++ b/src/Neo.GUI/GUI/CreateWalletDialog.cs @@ -0,0 +1,72 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// CreateWalletDialog.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; +using System.Windows.Forms; + +namespace Neo.GUI +{ + internal partial class CreateWalletDialog : Form + { + public CreateWalletDialog() + { + InitializeComponent(); + } + + public string Password + { + get + { + return textBox2.Text; + } + set + { + textBox2.Text = value; + textBox3.Text = value; + } + } + + public string WalletPath + { + get + { + return textBox1.Text; + } + set + { + textBox1.Text = value; + } + } + + private void textBox_TextChanged(object sender, EventArgs e) + { + if (textBox1.TextLength == 0 || textBox2.TextLength == 0 || textBox3.TextLength == 0) + { + button2.Enabled = false; + return; + } + if (textBox2.Text != textBox3.Text) + { + button2.Enabled = false; + return; + } + button2.Enabled = true; + } + + private void button1_Click(object sender, EventArgs e) + { + if (saveFileDialog1.ShowDialog() == DialogResult.OK) + { + textBox1.Text = saveFileDialog1.FileName; + } + } + } +} diff --git a/src/Neo.GUI/GUI/CreateWalletDialog.designer.cs b/src/Neo.GUI/GUI/CreateWalletDialog.designer.cs new file mode 100644 index 0000000000..c4f41b6573 --- /dev/null +++ b/src/Neo.GUI/GUI/CreateWalletDialog.designer.cs @@ -0,0 +1,143 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class CreateWalletDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CreateWalletDialog)); + this.label1 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.button1 = new System.Windows.Forms.Button(); + this.label2 = new System.Windows.Forms.Label(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.textBox3 = new System.Windows.Forms.TextBox(); + this.button2 = new System.Windows.Forms.Button(); + this.saveFileDialog1 = new System.Windows.Forms.SaveFileDialog(); + this.SuspendLayout(); + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // textBox1 + // + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.Name = "textBox1"; + this.textBox1.ReadOnly = true; + this.textBox1.TextChanged += new System.EventHandler(this.textBox_TextChanged); + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // textBox2 + // + resources.ApplyResources(this.textBox2, "textBox2"); + this.textBox2.Name = "textBox2"; + this.textBox2.UseSystemPasswordChar = true; + this.textBox2.TextChanged += new System.EventHandler(this.textBox_TextChanged); + // + // label3 + // + resources.ApplyResources(this.label3, "label3"); + this.label3.Name = "label3"; + // + // textBox3 + // + resources.ApplyResources(this.textBox3, "textBox3"); + this.textBox3.Name = "textBox3"; + this.textBox3.UseSystemPasswordChar = true; + this.textBox3.TextChanged += new System.EventHandler(this.textBox_TextChanged); + // + // button2 + // + resources.ApplyResources(this.button2, "button2"); + this.button2.DialogResult = System.Windows.Forms.DialogResult.OK; + this.button2.Name = "button2"; + this.button2.UseVisualStyleBackColor = true; + // + // saveFileDialog1 + // + this.saveFileDialog1.DefaultExt = "json"; + resources.ApplyResources(this.saveFileDialog1, "saveFileDialog1"); + // + // CreateWalletDialog + // + this.AcceptButton = this.button2; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.button2); + this.Controls.Add(this.textBox3); + this.Controls.Add(this.label3); + this.Controls.Add(this.textBox2); + this.Controls.Add(this.label2); + this.Controls.Add(this.button1); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "CreateWalletDialog"; + this.ShowInTaskbar = false; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox textBox3; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.SaveFileDialog saveFileDialog1; + } +} diff --git a/src/Neo.GUI/GUI/CreateWalletDialog.es-ES.resx b/src/Neo.GUI/GUI/CreateWalletDialog.es-ES.resx new file mode 100644 index 0000000000..09d7d9f325 --- /dev/null +++ b/src/Neo.GUI/GUI/CreateWalletDialog.es-ES.resx @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 9, 16 + + + 137, 17 + + + Fichero de monedero: + + + 152, 13 + + + 293, 23 + + + Buscar... + + + 69, 51 + + + 77, 17 + + + Contraseña: + + + 152, 48 + + + 25, 84 + + + 121, 17 + + + Repetir contraseña: + + + 152, 81 + + + Confirmar + + + Nuevo monedero + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/CreateWalletDialog.resx b/src/Neo.GUI/GUI/CreateWalletDialog.resx new file mode 100644 index 0000000000..bfe06e458d --- /dev/null +++ b/src/Neo.GUI/GUI/CreateWalletDialog.resx @@ -0,0 +1,363 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + confirm + + + Wallet File: + + + Wallet File|*.json + + + $this + + + 5 + + + + 150, 23 + + + browse + + + label1 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + 87, 17 + + + $this + + + 7, 17 + + + 0 + + + label3 + + + + True + + + + Top, Right + + + 105, 48 + + + CenterScreen + + + $this + + + 12, 84 + + + 7 + + + $this + + + 75, 23 + + + 0 + + + 5 + + + 70, 17 + + + 340, 23 + + + 6 + + + 105, 13 + + + 9 + + + 6 + + + saveFileDialog1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 451, 12 + + + 4 + + + 67, 17 + + + $this + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + textBox1 + + + 2 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 微软雅黑, 9pt + + + Re-Password: + + + 8 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + button1 + + + True + + + 451, 86 + + + True + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + label2 + + + 1 + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 1 + + + Top, Left, Right + + + Bottom, Right + + + 29, 16 + + + New Wallet + + + Password: + + + 7 + + + System.Windows.Forms.SaveFileDialog, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 32, 51 + + + button2 + + + textBox3 + + + $this + + + 150, 23 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 105, 81 + + + 538, 121 + + + 75, 23 + + + CreateWalletDialog + + + textBox2 + + + 2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + True + + + 17, 17 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/CreateWalletDialog.zh-Hans.resx b/src/Neo.GUI/GUI/CreateWalletDialog.zh-Hans.resx new file mode 100644 index 0000000000..ae934ad543 --- /dev/null +++ b/src/Neo.GUI/GUI/CreateWalletDialog.zh-Hans.resx @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 12, 15 + + + 83, 17 + + + 钱包文件位置: + + + 101, 12 + + + 289, 23 + + + 396, 12 + + + 浏览 + + + 60, 44 + + + 35, 17 + + + 密码: + + + 101, 41 + + + 36, 73 + + + 59, 17 + + + 重复密码: + + + 101, 70 + + + 396, 70 + + + 确定 + + + 钱包文件|*.json + + + 483, 105 + + + 新建钱包 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/DeployContractDialog.Designer.cs b/src/Neo.GUI/GUI/DeployContractDialog.Designer.cs new file mode 100644 index 0000000000..629b5e96dd --- /dev/null +++ b/src/Neo.GUI/GUI/DeployContractDialog.Designer.cs @@ -0,0 +1,308 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class DeployContractDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DeployContractDialog)); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.textBox5 = new System.Windows.Forms.TextBox(); + this.label5 = new System.Windows.Forms.Label(); + this.textBox4 = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.textBox3 = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.textBox7 = new System.Windows.Forms.TextBox(); + this.label7 = new System.Windows.Forms.Label(); + this.textBox6 = new System.Windows.Forms.TextBox(); + this.label6 = new System.Windows.Forms.Label(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.textBox9 = new System.Windows.Forms.TextBox(); + this.button1 = new System.Windows.Forms.Button(); + this.checkBox1 = new System.Windows.Forms.CheckBox(); + this.textBox8 = new System.Windows.Forms.TextBox(); + this.button2 = new System.Windows.Forms.Button(); + this.button3 = new System.Windows.Forms.Button(); + this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog(); + this.checkBox2 = new System.Windows.Forms.CheckBox(); + this.checkBox3 = new System.Windows.Forms.CheckBox(); + this.label8 = new System.Windows.Forms.Label(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.groupBox3.SuspendLayout(); + this.SuspendLayout(); + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.textBox5); + this.groupBox1.Controls.Add(this.label5); + this.groupBox1.Controls.Add(this.textBox4); + this.groupBox1.Controls.Add(this.label4); + this.groupBox1.Controls.Add(this.textBox3); + this.groupBox1.Controls.Add(this.label3); + this.groupBox1.Controls.Add(this.textBox2); + this.groupBox1.Controls.Add(this.label2); + this.groupBox1.Controls.Add(this.textBox1); + this.groupBox1.Controls.Add(this.label1); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // textBox5 + // + resources.ApplyResources(this.textBox5, "textBox5"); + this.textBox5.AcceptsReturn = true; + this.textBox5.AcceptsTab = true; + this.textBox5.Name = "textBox5"; + this.textBox5.TextChanged += new System.EventHandler(this.textBox_TextChanged); + // + // label5 + // + resources.ApplyResources(this.label5, "label5"); + this.label5.Name = "label5"; + // + // textBox4 + // + resources.ApplyResources(this.textBox4, "textBox4"); + this.textBox4.Name = "textBox4"; + this.textBox4.TextChanged += new System.EventHandler(this.textBox_TextChanged); + // + // label4 + // + resources.ApplyResources(this.label4, "label4"); + this.label4.Name = "label4"; + // + // textBox3 + // + resources.ApplyResources(this.textBox3, "textBox3"); + this.textBox3.Name = "textBox3"; + this.textBox3.TextChanged += new System.EventHandler(this.textBox_TextChanged); + // + // label3 + // + resources.ApplyResources(this.label3, "label3"); + this.label3.Name = "label3"; + // + // textBox2 + // + resources.ApplyResources(this.textBox2, "textBox2"); + this.textBox2.Name = "textBox2"; + this.textBox2.TextChanged += new System.EventHandler(this.textBox_TextChanged); + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // textBox1 + // + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.Name = "textBox1"; + this.textBox1.TextChanged += new System.EventHandler(this.textBox_TextChanged); + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // groupBox2 + // + resources.ApplyResources(this.groupBox2, "groupBox2"); + this.groupBox2.Controls.Add(this.textBox7); + this.groupBox2.Controls.Add(this.label7); + this.groupBox2.Controls.Add(this.textBox6); + this.groupBox2.Controls.Add(this.label6); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.TabStop = false; + // + // textBox7 + // + resources.ApplyResources(this.textBox7, "textBox7"); + this.textBox7.Name = "textBox7"; + // + // label7 + // + resources.ApplyResources(this.label7, "label7"); + this.label7.Name = "label7"; + // + // textBox6 + // + resources.ApplyResources(this.textBox6, "textBox6"); + this.textBox6.Name = "textBox6"; + // + // label6 + // + resources.ApplyResources(this.label6, "label6"); + this.label6.Name = "label6"; + // + // groupBox3 + // + resources.ApplyResources(this.groupBox3, "groupBox3"); + this.groupBox3.Controls.Add(this.label8); + this.groupBox3.Controls.Add(this.checkBox2); + this.groupBox3.Controls.Add(this.checkBox3); + this.groupBox3.Controls.Add(this.textBox9); + this.groupBox3.Controls.Add(this.button1); + this.groupBox3.Controls.Add(this.checkBox1); + this.groupBox3.Controls.Add(this.textBox8); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.TabStop = false; + // + // textBox9 + // + resources.ApplyResources(this.textBox9, "textBox9"); + this.textBox9.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textBox9.Name = "textBox9"; + this.textBox9.ReadOnly = true; + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // checkBox1 + // + resources.ApplyResources(this.checkBox1, "checkBox1"); + this.checkBox1.Name = "checkBox1"; + this.checkBox1.UseVisualStyleBackColor = true; + // + // textBox8 + // + resources.ApplyResources(this.textBox8, "textBox8"); + this.textBox8.Name = "textBox8"; + this.textBox8.TextChanged += new System.EventHandler(this.textBox_TextChanged); + // + // button2 + // + resources.ApplyResources(this.button2, "button2"); + this.button2.DialogResult = System.Windows.Forms.DialogResult.OK; + this.button2.Name = "button2"; + this.button2.UseVisualStyleBackColor = true; + // + // button3 + // + resources.ApplyResources(this.button3, "button3"); + this.button3.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.button3.Name = "button3"; + this.button3.UseVisualStyleBackColor = true; + // + // openFileDialog1 + // + resources.ApplyResources(this.openFileDialog1, "openFileDialog1"); + this.openFileDialog1.DefaultExt = "avm"; + // + // checkBox2 + // + resources.ApplyResources(this.checkBox2, "checkBox2"); + this.checkBox2.Name = "checkBox2"; + this.checkBox2.UseVisualStyleBackColor = true; + // + // checkBox3 + // + resources.ApplyResources(this.checkBox3, "checkBox3"); + this.checkBox3.Name = "checkBox3"; + this.checkBox3.UseVisualStyleBackColor = true; + // + // label8 + // + resources.ApplyResources(this.label8, "label8"); + this.label8.Name = "label8"; + // + // DeployContractDialog + // + resources.ApplyResources(this, "$this"); + this.AcceptButton = this.button2; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.button3; + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.groupBox3); + this.Controls.Add(this.button3); + this.Controls.Add(this.button2); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "DeployContractDialog"; + this.ShowInTaskbar = false; + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.groupBox3.ResumeLayout(false); + this.groupBox3.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox textBox3; + private System.Windows.Forms.TextBox textBox4; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.TextBox textBox5; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.TextBox textBox6; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.TextBox textBox7; + private System.Windows.Forms.GroupBox groupBox3; + private System.Windows.Forms.TextBox textBox8; + private System.Windows.Forms.CheckBox checkBox1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.Button button3; + private System.Windows.Forms.OpenFileDialog openFileDialog1; + private System.Windows.Forms.TextBox textBox9; + private System.Windows.Forms.CheckBox checkBox2; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.CheckBox checkBox3; + } +} diff --git a/src/Neo.GUI/GUI/DeployContractDialog.cs b/src/Neo.GUI/GUI/DeployContractDialog.cs new file mode 100644 index 0000000000..31ca5e6636 --- /dev/null +++ b/src/Neo.GUI/GUI/DeployContractDialog.cs @@ -0,0 +1,61 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// DeployContractDialog.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.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using System; +using System.IO; +using System.Windows.Forms; + +namespace Neo.GUI +{ + internal partial class DeployContractDialog : Form + { + public DeployContractDialog() + { + InitializeComponent(); + } + + public byte[] GetScript() + { + byte[] script = textBox8.Text.HexToBytes(); + string manifest = ""; + using ScriptBuilder sb = new ScriptBuilder(); + sb.EmitDynamicCall(NativeContract.ContractManagement.Hash, "deploy", script, manifest); + return sb.ToArray(); + } + + private void textBox_TextChanged(object sender, EventArgs e) + { + button2.Enabled = textBox1.TextLength > 0 + && textBox2.TextLength > 0 + && textBox3.TextLength > 0 + && textBox4.TextLength > 0 + && textBox5.TextLength > 0 + && textBox8.TextLength > 0; + try + { + textBox9.Text = textBox8.Text.HexToBytes().ToScriptHash().ToString(); + } + catch (FormatException) + { + textBox9.Text = ""; + } + } + + private void button1_Click(object sender, EventArgs e) + { + if (openFileDialog1.ShowDialog() != DialogResult.OK) return; + textBox8.Text = File.ReadAllBytes(openFileDialog1.FileName).ToHexString(); + } + } +} diff --git a/src/Neo.GUI/GUI/DeployContractDialog.es-ES.resx b/src/Neo.GUI/GUI/DeployContractDialog.es-ES.resx new file mode 100644 index 0000000000..7bd4a2e05b --- /dev/null +++ b/src/Neo.GUI/GUI/DeployContractDialog.es-ES.resx @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 3, 141 + + + 79, 17 + + + Descripción: + + + 31, 112 + + + 52, 17 + + + Correo: + + + 40, 83 + + + 43, 17 + + + Autor: + + + Versión: + + + 23, 25 + + + 60, 17 + + + Nombre: + + + 140, 51 + + + 374, 23 + + + 43, 54 + + + 91, 17 + + + Tipo devuelto: + + + 140, 22 + + + 374, 23 + + + 128, 17 + + + Lista de parámetros: + + + Metadatos + + + Cargar + + + 199, 21 + + + Es necesario almacenamiento + + + Código + + + 368, 530 + + + 83, 23 + + + Desplegar + + + Cancelar + + + Desplegar contrato + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/DeployContractDialog.resx b/src/Neo.GUI/GUI/DeployContractDialog.resx new file mode 100644 index 0000000000..16bd3de8c2 --- /dev/null +++ b/src/Neo.GUI/GUI/DeployContractDialog.resx @@ -0,0 +1,972 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Left, Right + + + Top, Bottom, Left, Right + + + + 114, 163 + + + 4, 4, 4, 4 + + + + True + + + Vertical + + + 545, 99 + + + 9 + + + textBox5 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + True + + + NoControl + + + 8, 165 + + + 4, 0, 4, 0 + + + 97, 20 + + + 8 + + + Description: + + + label5 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 1 + + + Top, Left, Right + + + 114, 128 + + + 4, 4, 4, 4 + + + 545, 27 + + + 7 + + + textBox4 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 2 + + + True + + + NoControl + + + 53, 132 + + + 4, 0, 4, 0 + + + 51, 20 + + + 6 + + + Email: + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 3 + + + Top, Left, Right + + + 114, 95 + + + 4, 4, 4, 4 + + + 545, 27 + + + 5 + + + textBox3 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 4 + + + True + + + NoControl + + + 42, 97 + + + 4, 0, 4, 0 + + + 64, 20 + + + 4 + + + Author: + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 5 + + + Top, Left, Right + + + 114, 60 + + + 4, 4, 4, 4 + + + 545, 27 + + + 3 + + + textBox2 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 6 + + + True + + + NoControl + + + 36, 64 + + + 4, 0, 4, 0 + + + 68, 20 + + + 2 + + + Version: + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 7 + + + Top, Left, Right + + + 114, 25 + + + 4, 4, 4, 4 + + + 545, 27 + + + 1 + + + textBox1 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 8 + + + True + + + 42, 29 + + + 4, 0, 4, 0 + + + 56, 20 + + + 0 + + + Name: + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 9 + + + 15, 15 + + + 4, 4, 4, 4 + + + 4, 4, 4, 4 + + + 669, 268 + + + 0 + + + Information + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + 15, 289 + + + 4, 4, 4, 4 + + + 4, 4, 4, 4 + + + 669, 97 + + + 1 + + + Metadata + + + groupBox2 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Top, Left, Right + + + 136, 60 + + + 4, 4, 4, 4 + + + 523, 27 + + + 3 + + + textBox7 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox2 + + + 0 + + + True + + + NoControl + + + 24, 64 + + + 4, 0, 4, 0 + + + 102, 20 + + + 2 + + + Return Type: + + + label7 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox2 + + + 1 + + + Top, Left, Right + + + 136, 25 + + + 4, 4, 4, 4 + + + 523, 27 + + + 1 + + + textBox6 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox2 + + + 2 + + + True + + + 8, 29 + + + 4, 0, 4, 0 + + + 117, 20 + + + 0 + + + Parameter List: + + + label6 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox2 + + + 3 + + + Top, Bottom, Left, Right + + + Bottom, Left + + + True + + + NoControl + + + 357, 189 + + + 4, 4, 4, 4 + + + 87, 24 + + + 3 + + + Payable + + + checkBox3 + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox3 + + + 0 + + + True + + + NoControl + + + 9, 162 + + + 4, 0, 4, 0 + + + 96, 20 + + + 3 + + + Script Hash: + + + label8 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox3 + + + 1 + + + Bottom, Left + + + True + + + NoControl + + + 180, 189 + + + 4, 4, 4, 4 + + + 127, 24 + + + 2 + + + Need Dyncall + + + checkBox2 + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox3 + + + 2 + + + Bottom, Left + + + 112, 162 + + + 4, 4, 4, 4 + + + 401, 20 + + + 4 + + + textBox9 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox3 + + + 3 + + + Bottom, Right + + + 564, 188 + + + 4, 4, 4, 4 + + + 96, 27 + + + 4 + + + Load + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox3 + + + 4 + + + Bottom, Left + + + True + + + 8, 189 + + + 4, 4, 4, 4 + + + 133, 24 + + + 1 + + + Need Storage + + + checkBox1 + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox3 + + + 5 + + + Top, Bottom, Left, Right + + + 8, 25 + + + 4, 4, 4, 4 + + + True + + + Vertical + + + 652, 155 + + + 0 + + + textBox8 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox3 + + + 6 + + + 15, 395 + + + 4, 4, 4, 4 + + + 4, 4, 4, 4 + + + 669, 223 + + + 2 + + + Code + + + groupBox3 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + Bottom, Right + + + False + + + 483, 624 + + + 4, 4, 4, 4 + + + 96, 27 + + + 3 + + + Deploy + + + button2 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 4 + + + Bottom, Right + + + NoControl + + + 588, 624 + + + 4, 4, 4, 4 + + + 96, 27 + + + 4 + + + Cancel + + + button3 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + 17, 17 + + + AVM File|*.avm + + + True + + + 9, 20 + + + 699, 665 + + + Microsoft YaHei, 9pt + + + 4, 5, 4, 5 + + + CenterScreen + + + Deploy Contract + + + openFileDialog1 + + + System.Windows.Forms.OpenFileDialog, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + DeployContractDialog + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/src/Neo.GUI/GUI/DeployContractDialog.zh-Hans.resx b/src/Neo.GUI/GUI/DeployContractDialog.zh-Hans.resx new file mode 100644 index 0000000000..ae91b44198 --- /dev/null +++ b/src/Neo.GUI/GUI/DeployContractDialog.zh-Hans.resx @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 70, 122 + + + 444, 75 + + + 30, 125 + + + 34, 15 + + + 说明: + + + 70, 96 + + + 444, 23 + + + 6, 99 + + + 58, 15 + + + 电子邮件: + + + 70, 71 + + + 444, 23 + + + 30, 74 + + + 34, 15 + + + 作者: + + + 70, 45 + + + 444, 23 + + + 30, 48 + + + 34, 15 + + + 版本: + + + 70, 19 + + + 444, 23 + + + 30, 22 + + + 34, 15 + + + 名称: + + + 信息 + + + 70, 45 + + + 444, 23 + + + 18, 48 + + + 46, 15 + + + 返回值: + + + 70, 19 + + + 444, 23 + + + 58, 15 + + + 参数列表: + + + 元数据 + + + 98, 19 + + + 需要动态调用 + + + 加载 + + + 110, 19 + + + 需要创建存储区 + + + 代码 + + + 部署 + + + 取消 + + + AVM文件|*.avm + + + 部署合约 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/DeveloperToolsForm.ContractParameters.cs b/src/Neo.GUI/GUI/DeveloperToolsForm.ContractParameters.cs new file mode 100644 index 0000000000..1912fe9152 --- /dev/null +++ b/src/Neo.GUI/GUI/DeveloperToolsForm.ContractParameters.cs @@ -0,0 +1,118 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// DeveloperToolsForm.ContractParameters.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 Akka.Actor; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Properties; +using Neo.SmartContract; +using Neo.Wallets; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using static Neo.Program; + +namespace Neo.GUI +{ + partial class DeveloperToolsForm + { + private ContractParametersContext context; + + private void listBox1_SelectedIndexChanged(object sender, EventArgs e) + { + if (listBox1.SelectedIndex < 0) return; + listBox2.Items.Clear(); + if (Service.CurrentWallet == null) return; + UInt160 hash = ((string)listBox1.SelectedItem).ToScriptHash(Service.NeoSystem.Settings.AddressVersion); + var parameters = context.GetParameters(hash); + if (parameters == null) + { + var parameterList = Service.CurrentWallet.GetAccount(hash).Contract.ParameterList; + if (parameterList != null) + { + var pList = new List(); + for (int i = 0; i < parameterList.Length; i++) + { + pList.Add(new ContractParameter(parameterList[i])); + context.Add(Service.CurrentWallet.GetAccount(hash).Contract, i, null); + } + } + } + listBox2.Items.AddRange(context.GetParameters(hash).ToArray()); + button4.Visible = context.Completed; + } + + private void listBox2_SelectedIndexChanged(object sender, EventArgs e) + { + if (listBox2.SelectedIndex < 0) return; + textBox1.Text = listBox2.SelectedItem.ToString(); + textBox2.Clear(); + } + + private void button1_Click(object sender, EventArgs e) + { + string input = InputBox.Show("ParametersContext", "ParametersContext"); + if (string.IsNullOrEmpty(input)) return; + try + { + context = ContractParametersContext.Parse(input, Service.NeoSystem.StoreView); + } + catch (FormatException ex) + { + MessageBox.Show(ex.Message); + return; + } + listBox1.Items.Clear(); + listBox2.Items.Clear(); + textBox1.Clear(); + textBox2.Clear(); + listBox1.Items.AddRange(context.ScriptHashes.Select(p => p.ToAddress(Service.NeoSystem.Settings.AddressVersion)).ToArray()); + button2.Enabled = true; + button4.Visible = context.Completed; + } + + private void button2_Click(object sender, EventArgs e) + { + InformationBox.Show(context.ToString(), "ParametersContext", "ParametersContext"); + } + + private void button3_Click(object sender, EventArgs e) + { + if (listBox1.SelectedIndex < 0) return; + if (listBox2.SelectedIndex < 0) return; + ContractParameter parameter = (ContractParameter)listBox2.SelectedItem; + parameter.SetValue(textBox2.Text); + listBox2.Items[listBox2.SelectedIndex] = parameter; + textBox1.Text = textBox2.Text; + button4.Visible = context.Completed; + } + + private void button4_Click(object sender, EventArgs e) + { + if (!(context.Verifiable is Transaction tx)) + { + MessageBox.Show("Only support to broadcast transaction."); + return; + } + tx.Witnesses = context.GetWitnesses(); + Blockchain.RelayResult reason = Service.NeoSystem.Blockchain.Ask(tx).Result; + if (reason.Result == VerifyResult.Succeed) + { + InformationBox.Show(tx.Hash.ToString(), Strings.RelaySuccessText, Strings.RelaySuccessTitle); + } + else + { + MessageBox.Show($"Transaction cannot be broadcast: {reason}"); + } + } + } +} diff --git a/src/Neo.GUI/GUI/DeveloperToolsForm.Designer.cs b/src/Neo.GUI/GUI/DeveloperToolsForm.Designer.cs new file mode 100644 index 0000000000..31faf836d9 --- /dev/null +++ b/src/Neo.GUI/GUI/DeveloperToolsForm.Designer.cs @@ -0,0 +1,259 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class DeveloperToolsForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DeveloperToolsForm)); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.propertyGrid1 = new System.Windows.Forms.PropertyGrid(); + this.button8 = new System.Windows.Forms.Button(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.button4 = new System.Windows.Forms.Button(); + this.button3 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.button1 = new System.Windows.Forms.Button(); + this.groupBox4 = new System.Windows.Forms.GroupBox(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.listBox2 = new System.Windows.Forms.ListBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.listBox1 = new System.Windows.Forms.ListBox(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.tabControl1.SuspendLayout(); + this.tabPage1.SuspendLayout(); + this.tabPage2.SuspendLayout(); + this.groupBox4.SuspendLayout(); + this.groupBox3.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + resources.ApplyResources(this.splitContainer1, "splitContainer1"); + this.splitContainer1.FixedPanel = System.Windows.Forms.FixedPanel.Panel2; + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + resources.ApplyResources(this.splitContainer1.Panel1, "splitContainer1.Panel1"); + this.splitContainer1.Panel1.Controls.Add(this.propertyGrid1); + // + // splitContainer1.Panel2 + // + resources.ApplyResources(this.splitContainer1.Panel2, "splitContainer1.Panel2"); + this.splitContainer1.Panel2.Controls.Add(this.button8); + // + // propertyGrid1 + // + resources.ApplyResources(this.propertyGrid1, "propertyGrid1"); + this.propertyGrid1.Name = "propertyGrid1"; + this.propertyGrid1.SelectedObjectsChanged += new System.EventHandler(this.propertyGrid1_SelectedObjectsChanged); + // + // button8 + // + resources.ApplyResources(this.button8, "button8"); + this.button8.Name = "button8"; + this.button8.UseVisualStyleBackColor = true; + this.button8.Click += new System.EventHandler(this.button8_Click); + // + // tabControl1 + // + resources.ApplyResources(this.tabControl1, "tabControl1"); + this.tabControl1.Controls.Add(this.tabPage1); + this.tabControl1.Controls.Add(this.tabPage2); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + // + // tabPage1 + // + resources.ApplyResources(this.tabPage1, "tabPage1"); + this.tabPage1.Controls.Add(this.splitContainer1); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.UseVisualStyleBackColor = true; + // + // tabPage2 + // + resources.ApplyResources(this.tabPage2, "tabPage2"); + this.tabPage2.Controls.Add(this.button4); + this.tabPage2.Controls.Add(this.button3); + this.tabPage2.Controls.Add(this.button2); + this.tabPage2.Controls.Add(this.button1); + this.tabPage2.Controls.Add(this.groupBox4); + this.tabPage2.Controls.Add(this.groupBox3); + this.tabPage2.Controls.Add(this.groupBox2); + this.tabPage2.Controls.Add(this.groupBox1); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.UseVisualStyleBackColor = true; + // + // button4 + // + resources.ApplyResources(this.button4, "button4"); + this.button4.Name = "button4"; + this.button4.UseVisualStyleBackColor = true; + this.button4.Click += new System.EventHandler(this.button4_Click); + // + // button3 + // + resources.ApplyResources(this.button3, "button3"); + this.button3.Name = "button3"; + this.button3.UseVisualStyleBackColor = true; + this.button3.Click += new System.EventHandler(this.button3_Click); + // + // button2 + // + resources.ApplyResources(this.button2, "button2"); + this.button2.Name = "button2"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // groupBox4 + // + resources.ApplyResources(this.groupBox4, "groupBox4"); + this.groupBox4.Controls.Add(this.textBox2); + this.groupBox4.Name = "groupBox4"; + this.groupBox4.TabStop = false; + // + // textBox2 + // + resources.ApplyResources(this.textBox2, "textBox2"); + this.textBox2.Name = "textBox2"; + // + // groupBox3 + // + resources.ApplyResources(this.groupBox3, "groupBox3"); + this.groupBox3.Controls.Add(this.textBox1); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.TabStop = false; + // + // textBox1 + // + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.Name = "textBox1"; + this.textBox1.ReadOnly = true; + // + // groupBox2 + // + resources.ApplyResources(this.groupBox2, "groupBox2"); + this.groupBox2.Controls.Add(this.listBox2); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.TabStop = false; + // + // listBox2 + // + resources.ApplyResources(this.listBox2, "listBox2"); + this.listBox2.FormattingEnabled = true; + this.listBox2.Name = "listBox2"; + this.listBox2.SelectedIndexChanged += new System.EventHandler(this.listBox2_SelectedIndexChanged); + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.listBox1); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // listBox1 + // + resources.ApplyResources(this.listBox1, "listBox1"); + this.listBox1.FormattingEnabled = true; + this.listBox1.Name = "listBox1"; + this.listBox1.SelectedIndexChanged += new System.EventHandler(this.listBox1_SelectedIndexChanged); + // + // DeveloperToolsForm + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.tabControl1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.KeyPreview = true; + this.MaximizeBox = false; + this.Name = "DeveloperToolsForm"; + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.tabControl1.ResumeLayout(false); + this.tabPage1.ResumeLayout(false); + this.tabPage2.ResumeLayout(false); + this.groupBox4.ResumeLayout(false); + this.groupBox4.PerformLayout(); + this.groupBox3.ResumeLayout(false); + this.groupBox3.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage tabPage2; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.GroupBox groupBox3; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.ListBox listBox1; + private System.Windows.Forms.ListBox listBox2; + private System.Windows.Forms.GroupBox groupBox4; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Button button3; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.Button button4; + private System.Windows.Forms.TabPage tabPage1; + private System.Windows.Forms.SplitContainer splitContainer1; + private System.Windows.Forms.PropertyGrid propertyGrid1; + private System.Windows.Forms.Button button8; + } +} diff --git a/src/Neo.GUI/GUI/DeveloperToolsForm.TxBuilder.cs b/src/Neo.GUI/GUI/DeveloperToolsForm.TxBuilder.cs new file mode 100644 index 0000000000..b8844fd85d --- /dev/null +++ b/src/Neo.GUI/GUI/DeveloperToolsForm.TxBuilder.cs @@ -0,0 +1,37 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// DeveloperToolsForm.TxBuilder.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.GUI.Wrappers; +using Neo.SmartContract; +using System; + +namespace Neo.GUI +{ + partial class DeveloperToolsForm + { + private void InitializeTxBuilder() + { + propertyGrid1.SelectedObject = new TransactionWrapper(); + } + + private void propertyGrid1_SelectedObjectsChanged(object sender, EventArgs e) + { + splitContainer1.Panel2.Enabled = propertyGrid1.SelectedObject != null; + } + + private void button8_Click(object sender, EventArgs e) + { + TransactionWrapper wrapper = (TransactionWrapper)propertyGrid1.SelectedObject; + ContractParametersContext context = new ContractParametersContext(Program.Service.NeoSystem.StoreView, wrapper.Unwrap(), Program.Service.NeoSystem.Settings.Network); + InformationBox.Show(context.ToString(), "ParametersContext", "ParametersContext"); + } + } +} diff --git a/src/Neo.GUI/GUI/DeveloperToolsForm.cs b/src/Neo.GUI/GUI/DeveloperToolsForm.cs new file mode 100644 index 0000000000..3c5d6ec952 --- /dev/null +++ b/src/Neo.GUI/GUI/DeveloperToolsForm.cs @@ -0,0 +1,24 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// DeveloperToolsForm.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.Windows.Forms; + +namespace Neo.GUI +{ + internal partial class DeveloperToolsForm : Form + { + public DeveloperToolsForm() + { + InitializeComponent(); + InitializeTxBuilder(); + } + } +} diff --git a/src/Neo.GUI/GUI/DeveloperToolsForm.es-ES.resx b/src/Neo.GUI/GUI/DeveloperToolsForm.es-ES.resx new file mode 100644 index 0000000000..9e330876eb --- /dev/null +++ b/src/Neo.GUI/GUI/DeveloperToolsForm.es-ES.resx @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Parametros de contexto + + + Parámetros del contrato + + + Emitir + + + Actualizar + + + Mostrar + + + Cargar + + + Nuevo valor + + + Valor actual + + + Parámetros + + + Hash del script + + + Herramienta de desarrollo + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/DeveloperToolsForm.resx b/src/Neo.GUI/GUI/DeveloperToolsForm.resx new file mode 100644 index 0000000000..63e49aa4b5 --- /dev/null +++ b/src/Neo.GUI/GUI/DeveloperToolsForm.resx @@ -0,0 +1,669 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Fill + + + + 3, 3 + + + Fill + + + 0, 0 + + + 444, 414 + + + + 1 + + + propertyGrid1 + + + System.Windows.Forms.PropertyGrid, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + splitContainer1.Panel1 + + + 0 + + + splitContainer1.Panel1 + + + System.Windows.Forms.SplitterPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + splitContainer1 + + + 0 + + + Bottom, Left, Right + + + NoControl + + + 3, 386 + + + 173, 23 + + + 3 + + + Get Parameters Context + + + button8 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + splitContainer1.Panel2 + + + 0 + + + False + + + splitContainer1.Panel2 + + + System.Windows.Forms.SplitterPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + splitContainer1 + + + 1 + + + 627, 414 + + + 444 + + + 1 + + + splitContainer1 + + + System.Windows.Forms.SplitContainer, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage1 + + + 0 + + + 4, 26 + + + 3, 3, 3, 3 + + + 633, 420 + + + 3 + + + Tx Builder + + + tabPage1 + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControl1 + + + 0 + + + Bottom, Left + + + 170, 389 + + + 75, 23 + + + 7 + + + Broadcast + + + False + + + button4 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + + 0 + + + Bottom, Right + + + 550, 389 + + + 75, 23 + + + 6 + + + Update + + + button3 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + + 1 + + + Bottom, Left + + + False + + + 89, 389 + + + 75, 23 + + + 5 + + + Show + + + button2 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + + 2 + + + Bottom, Left + + + 8, 389 + + + 75, 23 + + + 4 + + + Load + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + + 3 + + + Bottom, Left, Right + + + Fill + + + 3, 19 + + + True + + + 199, 98 + + + 0 + + + textBox2 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox4 + + + 0 + + + 420, 263 + + + 205, 120 + + + 3 + + + New Value + + + groupBox4 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + + 4 + + + Top, Bottom, Left, Right + + + Fill + + + 3, 19 + + + True + + + 199, 229 + + + 0 + + + textBox1 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox3 + + + 0 + + + 420, 6 + + + 205, 251 + + + 2 + + + Current Value + + + groupBox3 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + + 5 + + + Top, Bottom, Left + + + Fill + + + False + + + 17 + + + 3, 19 + + + 194, 355 + + + 0 + + + listBox2 + + + System.Windows.Forms.ListBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox2 + + + 0 + + + 214, 6 + + + 200, 377 + + + 1 + + + Parameters + + + groupBox2 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + + 6 + + + Top, Bottom, Left + + + Fill + + + False + + + 17 + + + 3, 19 + + + 194, 355 + + + 0 + + + listBox1 + + + System.Windows.Forms.ListBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + 8, 6 + + + 200, 377 + + + 0 + + + ScriptHash + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + + 7 + + + 4, 26 + + + 3, 3, 3, 3 + + + 633, 420 + + + 2 + + + Contract Parameters + + + tabPage2 + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControl1 + + + 1 + + + Fill + + + 0, 0 + + + 641, 450 + + + 0 + + + tabControl1 + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 7, 17 + + + 641, 450 + + + 微软雅黑, 9pt + + + CenterScreen + + + Neo Developer Tools + + + DeveloperToolsForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/DeveloperToolsForm.zh-Hans.resx b/src/Neo.GUI/GUI/DeveloperToolsForm.zh-Hans.resx new file mode 100644 index 0000000000..2b25fc62cb --- /dev/null +++ b/src/Neo.GUI/GUI/DeveloperToolsForm.zh-Hans.resx @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 获取合约参数上下文 + + + 交易构造器 + + + 广播 + + + 更新 + + + 显示 + + + 加载 + + + 新值 + + + 当前值 + + + 参数 + + + 合约参数 + + + NEO开发人员工具 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ElectionDialog.Designer.cs b/src/Neo.GUI/GUI/ElectionDialog.Designer.cs new file mode 100644 index 0000000000..512c24643a --- /dev/null +++ b/src/Neo.GUI/GUI/ElectionDialog.Designer.cs @@ -0,0 +1,92 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class ElectionDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ElectionDialog)); + this.label1 = new System.Windows.Forms.Label(); + this.comboBox1 = new System.Windows.Forms.ComboBox(); + this.button1 = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // comboBox1 + // + resources.ApplyResources(this.comboBox1, "comboBox1"); + this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBox1.FormattingEnabled = true; + this.comboBox1.Name = "comboBox1"; + this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged); + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.DialogResult = System.Windows.Forms.DialogResult.OK; + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + // + // ElectionDialog + // + this.AcceptButton = this.button1; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.button1); + this.Controls.Add(this.comboBox1); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ElectionDialog"; + this.ShowInTaskbar = false; + this.Load += new System.EventHandler(this.ElectionDialog_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.ComboBox comboBox1; + private System.Windows.Forms.Button button1; + } +} diff --git a/src/Neo.GUI/GUI/ElectionDialog.cs b/src/Neo.GUI/GUI/ElectionDialog.cs new file mode 100644 index 0000000000..ddc730589e --- /dev/null +++ b/src/Neo.GUI/GUI/ElectionDialog.cs @@ -0,0 +1,52 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ElectionDialog.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.Cryptography.ECC; +using Neo.IO; +using Neo.SmartContract.Native; +using Neo.VM; +using System; +using System.Linq; +using System.Windows.Forms; +using static Neo.Program; +using static Neo.SmartContract.Helper; + +namespace Neo.GUI +{ + public partial class ElectionDialog : Form + { + public ElectionDialog() + { + InitializeComponent(); + } + + public byte[] GetScript() + { + ECPoint pubkey = (ECPoint)comboBox1.SelectedItem; + using ScriptBuilder sb = new ScriptBuilder(); + sb.EmitDynamicCall(NativeContract.NEO.Hash, "registerValidator", pubkey); + return sb.ToArray(); + } + + private void ElectionDialog_Load(object sender, EventArgs e) + { + comboBox1.Items.AddRange(Service.CurrentWallet.GetAccounts().Where(p => !p.WatchOnly && IsSignatureContract(p.Contract.Script)).Select(p => p.GetKey().PublicKey).ToArray()); + } + + private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) + { + if (comboBox1.SelectedIndex >= 0) + { + button1.Enabled = true; + } + } + } +} diff --git a/src/Neo.GUI/GUI/ElectionDialog.es-ES.resx b/src/Neo.GUI/GUI/ElectionDialog.es-ES.resx new file mode 100644 index 0000000000..5ab76de86d --- /dev/null +++ b/src/Neo.GUI/GUI/ElectionDialog.es-ES.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Votación + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ElectionDialog.resx b/src/Neo.GUI/GUI/ElectionDialog.resx new file mode 100644 index 0000000000..ea655e7977 --- /dev/null +++ b/src/Neo.GUI/GUI/ElectionDialog.resx @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + True + + + + 12, 17 + + + 70, 17 + + + 0 + + + Public Key: + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + + Top, Left, Right + + + 83, 14 + + + 442, 25 + + + 9 + + + comboBox1 + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Bottom, Right + + + False + + + 450, 56 + + + 75, 26 + + + 12 + + + OK + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 7, 17 + + + 537, 95 + + + 微软雅黑, 9pt + + + 3, 5, 3, 5 + + + CenterScreen + + + Election + + + ElectionDialog + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ElectionDialog.zh-Hans.resx b/src/Neo.GUI/GUI/ElectionDialog.zh-Hans.resx new file mode 100644 index 0000000000..53e9edf8f2 --- /dev/null +++ b/src/Neo.GUI/GUI/ElectionDialog.zh-Hans.resx @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 24, 15 + + + 44, 17 + + + 公钥: + + + 73, 12 + + + 452, 25 + + + 确定 + + + + NoControl + + + 选举 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/Helper.cs b/src/Neo.GUI/GUI/Helper.cs new file mode 100644 index 0000000000..fe06e78429 --- /dev/null +++ b/src/Neo.GUI/GUI/Helper.cs @@ -0,0 +1,74 @@ +// 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 Akka.Actor; +using Neo.Network.P2P.Payloads; +using Neo.Properties; +using Neo.SmartContract; +using System; +using System.Collections.Generic; +using System.Windows.Forms; +using static Neo.Program; + +namespace Neo.GUI +{ + internal static class Helper + { + private static readonly Dictionary tool_forms = new Dictionary(); + + private static void Helper_FormClosing(object sender, FormClosingEventArgs e) + { + tool_forms.Remove(sender.GetType()); + } + + public static void Show() where T : Form, new() + { + Type t = typeof(T); + if (!tool_forms.ContainsKey(t)) + { + tool_forms.Add(t, new T()); + tool_forms[t].FormClosing += Helper_FormClosing; + } + tool_forms[t].Show(); + tool_forms[t].Activate(); + } + + public static void SignAndShowInformation(Transaction tx) + { + if (tx == null) + { + MessageBox.Show(Strings.InsufficientFunds); + return; + } + ContractParametersContext context; + try + { + context = new ContractParametersContext(Service.NeoSystem.StoreView, tx, Program.Service.NeoSystem.Settings.Network); + } + catch (InvalidOperationException) + { + MessageBox.Show(Strings.UnsynchronizedBlock); + return; + } + Service.CurrentWallet.Sign(context); + if (context.Completed) + { + tx.Witnesses = context.GetWitnesses(); + Service.NeoSystem.Blockchain.Tell(tx); + InformationBox.Show(tx.Hash.ToString(), Strings.SendTxSucceedMessage, Strings.SendTxSucceedTitle); + } + else + { + InformationBox.Show(context.ToString(), Strings.IncompletedSignatureMessage, Strings.IncompletedSignatureTitle); + } + } + } +} diff --git a/src/Neo.GUI/GUI/ImportCustomContractDialog.Designer.cs b/src/Neo.GUI/GUI/ImportCustomContractDialog.Designer.cs new file mode 100644 index 0000000000..d06a03890e --- /dev/null +++ b/src/Neo.GUI/GUI/ImportCustomContractDialog.Designer.cs @@ -0,0 +1,137 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class ImportCustomContractDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ImportCustomContractDialog)); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.button1 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.textBox3 = new System.Windows.Forms.TextBox(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // textBox1 + // + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.Name = "textBox1"; + this.textBox1.TextChanged += new System.EventHandler(this.Input_Changed); + // + // textBox2 + // + resources.ApplyResources(this.textBox2, "textBox2"); + this.textBox2.Name = "textBox2"; + this.textBox2.TextChanged += new System.EventHandler(this.Input_Changed); + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.textBox2); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.DialogResult = System.Windows.Forms.DialogResult.OK; + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + // + // button2 + // + resources.ApplyResources(this.button2, "button2"); + this.button2.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.button2.Name = "button2"; + this.button2.UseVisualStyleBackColor = true; + // + // textBox3 + // + resources.ApplyResources(this.textBox3, "textBox3"); + this.textBox3.Name = "textBox3"; + // + // ImportCustomContractDialog + // + this.AcceptButton = this.button1; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.button2; + this.Controls.Add(this.textBox3); + this.Controls.Add(this.button2); + this.Controls.Add(this.button1); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ImportCustomContractDialog"; + this.ShowInTaskbar = false; + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.TextBox textBox3; + } +} diff --git a/src/Neo.GUI/GUI/ImportCustomContractDialog.cs b/src/Neo.GUI/GUI/ImportCustomContractDialog.cs new file mode 100644 index 0000000000..b8df534699 --- /dev/null +++ b/src/Neo.GUI/GUI/ImportCustomContractDialog.cs @@ -0,0 +1,54 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ImportCustomContractDialog.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.SmartContract; +using Neo.Wallets; +using System; +using System.Linq; +using System.Windows.Forms; + +namespace Neo.GUI +{ + internal partial class ImportCustomContractDialog : Form + { + public Contract GetContract() + { + ContractParameterType[] parameterList = textBox1.Text.HexToBytes().Select(p => (ContractParameterType)p).ToArray(); + byte[] redeemScript = textBox2.Text.HexToBytes(); + return Contract.Create(parameterList, redeemScript); + } + + public KeyPair GetKey() + { + if (textBox3.TextLength == 0) return null; + byte[] privateKey; + try + { + privateKey = Wallet.GetPrivateKeyFromWIF(textBox3.Text); + } + catch (FormatException) + { + privateKey = textBox3.Text.HexToBytes(); + } + return new KeyPair(privateKey); + } + + public ImportCustomContractDialog() + { + InitializeComponent(); + } + + private void Input_Changed(object sender, EventArgs e) + { + button1.Enabled = textBox1.TextLength > 0 && textBox2.TextLength > 0; + } + } +} diff --git a/src/Neo.GUI/GUI/ImportCustomContractDialog.es-ES.resx b/src/Neo.GUI/GUI/ImportCustomContractDialog.es-ES.resx new file mode 100644 index 0000000000..a61aa86444 --- /dev/null +++ b/src/Neo.GUI/GUI/ImportCustomContractDialog.es-ES.resx @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 13, 15 + + + 23, 44 + + + 114, 16 + + + Lista de parámetros: + + + 143, 41 + + + 433, 23 + + + Confirmar + + + Cancelar + + + 143, 12 + + + 433, 23 + + + Importar contrato personalizado + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ImportCustomContractDialog.resx b/src/Neo.GUI/GUI/ImportCustomContractDialog.resx new file mode 100644 index 0000000000..f7b634476e --- /dev/null +++ b/src/Neo.GUI/GUI/ImportCustomContractDialog.resx @@ -0,0 +1,366 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + True + + + + 12, 15 + + + 124, 16 + + + 0 + + + Private Key (optional): + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 6 + + + True + + + 50, 44 + + + 86, 16 + + + 10 + + + Parameter List: + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 5 + + + + Top, Left, Right + + + 142, 41 + + + 434, 23 + + + 11 + + + textBox1 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 4 + + + Fill + + + 3, 19 + + + 131072 + + + True + + + Vertical + + + 558, 323 + + + 13 + + + textBox2 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + Top, Bottom, Left, Right + + + 12, 70 + + + 564, 345 + + + 14 + + + Script + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + Bottom, Right + + + False + + + 420, 421 + + + 75, 23 + + + 15 + + + OK + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + Bottom, Right + + + 501, 421 + + + 75, 23 + + + 16 + + + Cancel + + + button2 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 142, 12 + + + 434, 23 + + + 17 + + + textBox3 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 7, 16 + + + 588, 456 + + + 微软雅黑, 9pt + + + 3, 4, 3, 4 + + + CenterScreen + + + Import Custom Contract + + + ImportCustomContractDialog + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ImportCustomContractDialog.zh-Hans.resx b/src/Neo.GUI/GUI/ImportCustomContractDialog.zh-Hans.resx new file mode 100644 index 0000000000..78fbe3ff92 --- /dev/null +++ b/src/Neo.GUI/GUI/ImportCustomContractDialog.zh-Hans.resx @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 83, 16 + + + 私钥(可选): + + + 36, 44 + + + 59, 16 + + + 形参列表: + + + 101, 41 + + + 475, 23 + + + 脚本代码 + + + 确定 + + + 取消 + + + 101, 12 + + + 475, 23 + + + 导入自定义合约 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ImportPrivateKeyDialog.cs b/src/Neo.GUI/GUI/ImportPrivateKeyDialog.cs new file mode 100644 index 0000000000..9379f45900 --- /dev/null +++ b/src/Neo.GUI/GUI/ImportPrivateKeyDialog.cs @@ -0,0 +1,41 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ImportPrivateKeyDialog.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; +using System.Windows.Forms; + +namespace Neo.GUI +{ + internal partial class ImportPrivateKeyDialog : Form + { + public ImportPrivateKeyDialog() + { + InitializeComponent(); + } + + public string[] WifStrings + { + get + { + return textBox1.Lines; + } + set + { + textBox1.Lines = value; + } + } + + private void textBox1_TextChanged(object sender, EventArgs e) + { + button1.Enabled = textBox1.TextLength > 0; + } + } +} diff --git a/src/Neo.GUI/GUI/ImportPrivateKeyDialog.designer.cs b/src/Neo.GUI/GUI/ImportPrivateKeyDialog.designer.cs new file mode 100644 index 0000000000..e0df823ec7 --- /dev/null +++ b/src/Neo.GUI/GUI/ImportPrivateKeyDialog.designer.cs @@ -0,0 +1,102 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class ImportPrivateKeyDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ImportPrivateKeyDialog)); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.button1 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // textBox1 + // + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.Name = "textBox1"; + this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged); + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.DialogResult = System.Windows.Forms.DialogResult.OK; + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + // + // button2 + // + resources.ApplyResources(this.button2, "button2"); + this.button2.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.button2.Name = "button2"; + this.button2.UseVisualStyleBackColor = true; + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.textBox1); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // ImportPrivateKeyDialog + // + this.AcceptButton = this.button1; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.button2; + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.button2); + this.Controls.Add(this.button1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ImportPrivateKeyDialog"; + this.ShowInTaskbar = false; + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.GroupBox groupBox1; + } +} diff --git a/src/Neo.GUI/GUI/ImportPrivateKeyDialog.es-ES.resx b/src/Neo.GUI/GUI/ImportPrivateKeyDialog.es-ES.resx new file mode 100644 index 0000000000..86ff978402 --- /dev/null +++ b/src/Neo.GUI/GUI/ImportPrivateKeyDialog.es-ES.resx @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cancelar + + + Clave privada WIF: + + + + NoControl + + + Aceptar + + + Importar clave privada + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ImportPrivateKeyDialog.resx b/src/Neo.GUI/GUI/ImportPrivateKeyDialog.resx new file mode 100644 index 0000000000..5cf34443a4 --- /dev/null +++ b/src/Neo.GUI/GUI/ImportPrivateKeyDialog.resx @@ -0,0 +1,267 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Fill + + + + 3, 19 + + + + True + + + Vertical + + + 454, 79 + + + 0 + + + False + + + textBox1 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + Bottom, Right + + + False + + + 316, 119 + + + 75, 23 + + + 1 + + + OK + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + Bottom, Right + + + 397, 119 + + + 75, 23 + + + 2 + + + Cancel + + + button2 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Top, Bottom, Left, Right + + + 12, 12 + + + 460, 101 + + + 0 + + + WIF Private Key: + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 7, 17 + + + 484, 154 + + + 微软雅黑, 9pt + + + 3, 4, 3, 4 + + + CenterScreen + + + Import Private Key + + + ImportPrivateKeyDialog + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ImportPrivateKeyDialog.zh-Hans.resx b/src/Neo.GUI/GUI/ImportPrivateKeyDialog.zh-Hans.resx new file mode 100644 index 0000000000..5db4bf12ea --- /dev/null +++ b/src/Neo.GUI/GUI/ImportPrivateKeyDialog.zh-Hans.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 确定 + + + 取消 + + + WIF私钥: + + + 导入私钥 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/InformationBox.Designer.cs b/src/Neo.GUI/GUI/InformationBox.Designer.cs new file mode 100644 index 0000000000..8b41144a9b --- /dev/null +++ b/src/Neo.GUI/GUI/InformationBox.Designer.cs @@ -0,0 +1,100 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class InformationBox + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(InformationBox)); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.button1 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // textBox1 + // + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.Name = "textBox1"; + this.textBox1.ReadOnly = true; + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // button2 + // + resources.ApplyResources(this.button2, "button2"); + this.button2.DialogResult = System.Windows.Forms.DialogResult.OK; + this.button2.Name = "button2"; + this.button2.UseVisualStyleBackColor = true; + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // InformationBox + // + this.AcceptButton = this.button2; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.button2; + this.Controls.Add(this.label1); + this.Controls.Add(this.button2); + this.Controls.Add(this.button1); + this.Controls.Add(this.textBox1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "InformationBox"; + this.ShowInTaskbar = false; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.Label label1; + } +} diff --git a/src/Neo.GUI/GUI/InformationBox.cs b/src/Neo.GUI/GUI/InformationBox.cs new file mode 100644 index 0000000000..41516fd9db --- /dev/null +++ b/src/Neo.GUI/GUI/InformationBox.cs @@ -0,0 +1,44 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// InformationBox.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.Windows.Forms; + +namespace Neo.GUI +{ + internal partial class InformationBox : Form + { + public InformationBox() + { + InitializeComponent(); + } + + public static DialogResult Show(string text, string message = null, string title = null) + { + using InformationBox box = new InformationBox(); + box.textBox1.Text = text; + if (message != null) + { + box.label1.Text = message; + } + if (title != null) + { + box.Text = title; + } + return box.ShowDialog(); + } + + private void button1_Click(object sender, System.EventArgs e) + { + textBox1.SelectAll(); + textBox1.Copy(); + } + } +} diff --git a/src/Neo.GUI/GUI/InformationBox.es-ES.resx b/src/Neo.GUI/GUI/InformationBox.es-ES.resx new file mode 100644 index 0000000000..1af985f64c --- /dev/null +++ b/src/Neo.GUI/GUI/InformationBox.es-ES.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Copiar + + + Cancelar + + + Información + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/InformationBox.resx b/src/Neo.GUI/GUI/InformationBox.resx new file mode 100644 index 0000000000..3aec9d5ab6 --- /dev/null +++ b/src/Neo.GUI/GUI/InformationBox.resx @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Bottom, Left, Right + + + + 12, 29 + + + + True + + + Vertical + + + 489, 203 + + + 0 + + + textBox1 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + Bottom, Right + + + 345, 238 + + + 75, 23 + + + 1 + + + copy + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + Bottom, Right + + + 426, 238 + + + 75, 23 + + + 2 + + + close + + + button2 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + True + + + 12, 9 + + + 0, 17 + + + 3 + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 7, 17 + + + 513, 273 + + + 微软雅黑, 9pt + + + CenterScreen + + + InformationBox + + + InformationBox + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/InformationBox.zh-Hans.resx b/src/Neo.GUI/GUI/InformationBox.zh-Hans.resx new file mode 100644 index 0000000000..ab3b23dc17 --- /dev/null +++ b/src/Neo.GUI/GUI/InformationBox.zh-Hans.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 复制 + + + 关闭 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/InputBox.Designer.cs b/src/Neo.GUI/GUI/InputBox.Designer.cs new file mode 100644 index 0000000000..aa376ca91f --- /dev/null +++ b/src/Neo.GUI/GUI/InputBox.Designer.cs @@ -0,0 +1,102 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class InputBox + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(InputBox)); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.button1 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // textBox1 + // + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.Name = "textBox1"; + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.textBox1); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.DialogResult = System.Windows.Forms.DialogResult.OK; + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + // + // button2 + // + resources.ApplyResources(this.button2, "button2"); + this.button2.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.button2.Name = "button2"; + this.button2.UseVisualStyleBackColor = true; + // + // InputBox + // + this.AcceptButton = this.button1; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.button2; + this.Controls.Add(this.button2); + this.Controls.Add(this.button1); + this.Controls.Add(this.groupBox1); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "InputBox"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Button button2; + } +} diff --git a/src/Neo.GUI/GUI/InputBox.cs b/src/Neo.GUI/GUI/InputBox.cs new file mode 100644 index 0000000000..51884b3212 --- /dev/null +++ b/src/Neo.GUI/GUI/InputBox.cs @@ -0,0 +1,33 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// InputBox.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.Windows.Forms; + +namespace Neo.GUI +{ + internal partial class InputBox : Form + { + private InputBox(string text, string caption, string content) + { + InitializeComponent(); + this.Text = caption; + groupBox1.Text = text; + textBox1.Text = content; + } + + public static string Show(string text, string caption, string content = "") + { + using InputBox dialog = new InputBox(text, caption, content); + if (dialog.ShowDialog() != DialogResult.OK) return null; + return dialog.textBox1.Text; + } + } +} diff --git a/src/Neo.GUI/GUI/InputBox.es-ES.resx b/src/Neo.GUI/GUI/InputBox.es-ES.resx new file mode 100644 index 0000000000..3e13191c48 --- /dev/null +++ b/src/Neo.GUI/GUI/InputBox.es-ES.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Aceptar + + + Cancelar + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/InputBox.resx b/src/Neo.GUI/GUI/InputBox.resx new file mode 100644 index 0000000000..84533a5c93 --- /dev/null +++ b/src/Neo.GUI/GUI/InputBox.resx @@ -0,0 +1,249 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 2 + + + True + + + 0 + + + + 7, 17 + + + InputBox + + + 75, 23 + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + button2 + + + InputBox + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 390, 118 + + + 420, 193 + + + 2 + + + + CenterScreen + + + 75, 23 + + + 2, 2, 2, 2 + + + groupBox1 + + + 0 + + + 3, 19 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 12, 12 + + + 1 + + + 0 + + + Fill + + + $this + + + groupBox1 + + + 0 + + + 1 + + + button1 + + + textBox1 + + + OK + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 252, 158 + + + Microsoft YaHei UI, 9pt + + + 396, 140 + + + 333, 158 + + + Cancel + + + $this + + + True + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/InputBox.zh-Hans.resx b/src/Neo.GUI/GUI/InputBox.zh-Hans.resx new file mode 100644 index 0000000000..0ede664604 --- /dev/null +++ b/src/Neo.GUI/GUI/InputBox.zh-Hans.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 确定 + + + 取消 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/InvokeContractDialog.Designer.cs b/src/Neo.GUI/GUI/InvokeContractDialog.Designer.cs new file mode 100644 index 0000000000..04f845def9 --- /dev/null +++ b/src/Neo.GUI/GUI/InvokeContractDialog.Designer.cs @@ -0,0 +1,270 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class InvokeContractDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(InvokeContractDialog)); + this.button6 = new System.Windows.Forms.Button(); + this.textBox6 = new System.Windows.Forms.TextBox(); + this.button3 = new System.Windows.Forms.Button(); + this.button4 = new System.Windows.Forms.Button(); + this.label6 = new System.Windows.Forms.Label(); + this.label7 = new System.Windows.Forms.Label(); + this.button5 = new System.Windows.Forms.Button(); + this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tabPage3 = new System.Windows.Forms.TabPage(); + this.button8 = new System.Windows.Forms.Button(); + this.textBox9 = new System.Windows.Forms.TextBox(); + this.label10 = new System.Windows.Forms.Label(); + this.comboBox1 = new System.Windows.Forms.ComboBox(); + this.label9 = new System.Windows.Forms.Label(); + this.button7 = new System.Windows.Forms.Button(); + this.textBox8 = new System.Windows.Forms.TextBox(); + this.label8 = new System.Windows.Forms.Label(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.textBox7 = new System.Windows.Forms.TextBox(); + this.openFileDialog2 = new System.Windows.Forms.OpenFileDialog(); + this.tabControl1.SuspendLayout(); + this.tabPage3.SuspendLayout(); + this.tabPage2.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // button6 + // + resources.ApplyResources(this.button6, "button6"); + this.button6.Name = "button6"; + this.button6.UseVisualStyleBackColor = true; + this.button6.Click += new System.EventHandler(this.button6_Click); + // + // textBox6 + // + resources.ApplyResources(this.textBox6, "textBox6"); + this.textBox6.Name = "textBox6"; + this.textBox6.TextChanged += new System.EventHandler(this.textBox6_TextChanged); + // + // button3 + // + resources.ApplyResources(this.button3, "button3"); + this.button3.DialogResult = System.Windows.Forms.DialogResult.OK; + this.button3.Name = "button3"; + this.button3.UseVisualStyleBackColor = true; + // + // button4 + // + resources.ApplyResources(this.button4, "button4"); + this.button4.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.button4.Name = "button4"; + this.button4.UseVisualStyleBackColor = true; + // + // label6 + // + resources.ApplyResources(this.label6, "label6"); + this.label6.Name = "label6"; + // + // label7 + // + resources.ApplyResources(this.label7, "label7"); + this.label7.Name = "label7"; + // + // button5 + // + resources.ApplyResources(this.button5, "button5"); + this.button5.Name = "button5"; + this.button5.UseVisualStyleBackColor = true; + this.button5.Click += new System.EventHandler(this.button5_Click); + // + // openFileDialog1 + // + resources.ApplyResources(this.openFileDialog1, "openFileDialog1"); + this.openFileDialog1.DefaultExt = "avm"; + // + // tabControl1 + // + resources.ApplyResources(this.tabControl1, "tabControl1"); + this.tabControl1.Controls.Add(this.tabPage3); + this.tabControl1.Controls.Add(this.tabPage2); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + // + // tabPage3 + // + resources.ApplyResources(this.tabPage3, "tabPage3"); + this.tabPage3.Controls.Add(this.button8); + this.tabPage3.Controls.Add(this.textBox9); + this.tabPage3.Controls.Add(this.label10); + this.tabPage3.Controls.Add(this.comboBox1); + this.tabPage3.Controls.Add(this.label9); + this.tabPage3.Controls.Add(this.button7); + this.tabPage3.Controls.Add(this.textBox8); + this.tabPage3.Controls.Add(this.label8); + this.tabPage3.Name = "tabPage3"; + this.tabPage3.UseVisualStyleBackColor = true; + // + // button8 + // + resources.ApplyResources(this.button8, "button8"); + this.button8.Name = "button8"; + this.button8.UseVisualStyleBackColor = true; + this.button8.Click += new System.EventHandler(this.button8_Click); + // + // textBox9 + // + resources.ApplyResources(this.textBox9, "textBox9"); + this.textBox9.Name = "textBox9"; + this.textBox9.ReadOnly = true; + // + // label10 + // + resources.ApplyResources(this.label10, "label10"); + this.label10.Name = "label10"; + // + // comboBox1 + // + resources.ApplyResources(this.comboBox1, "comboBox1"); + this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBox1.FormattingEnabled = true; + this.comboBox1.Name = "comboBox1"; + this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged); + // + // label9 + // + resources.ApplyResources(this.label9, "label9"); + this.label9.Name = "label9"; + // + // button7 + // + resources.ApplyResources(this.button7, "button7"); + this.button7.Name = "button7"; + this.button7.UseVisualStyleBackColor = true; + this.button7.Click += new System.EventHandler(this.button7_Click); + // + // textBox8 + // + resources.ApplyResources(this.textBox8, "textBox8"); + this.textBox8.Name = "textBox8"; + this.textBox8.ReadOnly = true; + // + // label8 + // + resources.ApplyResources(this.label8, "label8"); + this.label8.Name = "label8"; + // + // tabPage2 + // + resources.ApplyResources(this.tabPage2, "tabPage2"); + this.tabPage2.Controls.Add(this.button6); + this.tabPage2.Controls.Add(this.textBox6); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.UseVisualStyleBackColor = true; + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.textBox7); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // textBox7 + // + resources.ApplyResources(this.textBox7, "textBox7"); + this.textBox7.Name = "textBox7"; + this.textBox7.ReadOnly = true; + // + // openFileDialog2 + // + resources.ApplyResources(this.openFileDialog2, "openFileDialog2"); + this.openFileDialog2.DefaultExt = "abi.json"; + // + // InvokeContractDialog + // + resources.ApplyResources(this, "$this"); + this.AcceptButton = this.button3; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.button4; + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.tabControl1); + this.Controls.Add(this.button5); + this.Controls.Add(this.label7); + this.Controls.Add(this.label6); + this.Controls.Add(this.button4); + this.Controls.Add(this.button3); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "InvokeContractDialog"; + this.ShowInTaskbar = false; + this.tabControl1.ResumeLayout(false); + this.tabPage3.ResumeLayout(false); + this.tabPage3.PerformLayout(); + this.tabPage2.ResumeLayout(false); + this.tabPage2.PerformLayout(); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private System.Windows.Forms.TextBox textBox6; + private System.Windows.Forms.Button button3; + private System.Windows.Forms.Button button4; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.Button button5; + private System.Windows.Forms.Button button6; + private System.Windows.Forms.OpenFileDialog openFileDialog1; + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage tabPage2; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TextBox textBox7; + private System.Windows.Forms.TabPage tabPage3; + private System.Windows.Forms.TextBox textBox8; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.Button button7; + private System.Windows.Forms.OpenFileDialog openFileDialog2; + private System.Windows.Forms.Label label9; + private System.Windows.Forms.ComboBox comboBox1; + private System.Windows.Forms.Button button8; + private System.Windows.Forms.TextBox textBox9; + private System.Windows.Forms.Label label10; + } +} diff --git a/src/Neo.GUI/GUI/InvokeContractDialog.cs b/src/Neo.GUI/GUI/InvokeContractDialog.cs new file mode 100644 index 0000000000..4e46799f5c --- /dev/null +++ b/src/Neo.GUI/GUI/InvokeContractDialog.cs @@ -0,0 +1,146 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// InvokeContractDialog.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.Json; +using Neo.Network.P2P.Payloads; +using Neo.Properties; +using Neo.SmartContract; +using Neo.VM; +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using static Neo.Program; + +namespace Neo.GUI +{ + internal partial class InvokeContractDialog : Form + { + private readonly Transaction tx; + private JObject abi; + private UInt160 script_hash; + private ContractParameter[] parameters; + + public InvokeContractDialog() + { + InitializeComponent(); + } + + public InvokeContractDialog(Transaction tx) : this() + { + this.tx = tx; + tabControl1.SelectedTab = tabPage2; + textBox6.Text = tx.Script.Span.ToHexString(); + textBox6.ReadOnly = true; + } + + public InvokeContractDialog(byte[] script) : this() + { + tabControl1.SelectedTab = tabPage2; + textBox6.Text = script.ToHexString(); + } + + public Transaction GetTransaction() + { + byte[] script = textBox6.Text.Trim().HexToBytes(); + return tx ?? Service.CurrentWallet.MakeTransaction(Service.NeoSystem.StoreView, script); + } + + private void UpdateScript() + { + using ScriptBuilder sb = new ScriptBuilder(); + sb.EmitDynamicCall(script_hash, (string)comboBox1.SelectedItem, parameters); + textBox6.Text = sb.ToArray().ToHexString(); + } + + private void textBox6_TextChanged(object sender, EventArgs e) + { + button3.Enabled = false; + button5.Enabled = textBox6.TextLength > 0; + } + + private void button5_Click(object sender, EventArgs e) + { + byte[] script; + try + { + script = textBox6.Text.Trim().HexToBytes(); + } + catch (FormatException ex) + { + MessageBox.Show(ex.Message); + return; + } + Transaction tx_test = tx ?? new Transaction + { + Signers = new Signer[0], + Attributes = new TransactionAttribute[0], + Script = script, + Witnesses = new Witness[0] + }; + using ApplicationEngine engine = ApplicationEngine.Run(tx_test.Script, Service.NeoSystem.StoreView, container: tx_test); + StringBuilder sb = new StringBuilder(); + sb.AppendLine($"VM State: {engine.State}"); + sb.AppendLine($"Gas Consumed: {engine.GasConsumed}"); + sb.AppendLine($"Evaluation Stack: {new JArray(engine.ResultStack.Select(p => p.ToParameter().ToJson()))}"); + textBox7.Text = sb.ToString(); + if (engine.State != VMState.FAULT) + { + label7.Text = engine.GasConsumed + " gas"; + button3.Enabled = true; + } + else + { + MessageBox.Show(Strings.ExecutionFailed); + } + } + + private void button6_Click(object sender, EventArgs e) + { + if (openFileDialog1.ShowDialog() != DialogResult.OK) return; + textBox6.Text = File.ReadAllBytes(openFileDialog1.FileName).ToHexString(); + } + + private void button7_Click(object sender, EventArgs e) + { + if (openFileDialog2.ShowDialog() != DialogResult.OK) return; + abi = (JObject)JToken.Parse(File.ReadAllText(openFileDialog2.FileName)); + script_hash = UInt160.Parse(abi["hash"].AsString()); + textBox8.Text = script_hash.ToString(); + comboBox1.Items.Clear(); + comboBox1.Items.AddRange(((JArray)abi["functions"]).Select(p => p["name"].AsString()).Where(p => p != abi["entrypoint"].AsString()).ToArray()); + textBox9.Clear(); + button8.Enabled = false; + } + + private void button8_Click(object sender, EventArgs e) + { + using (ParametersEditor dialog = new ParametersEditor(parameters)) + { + dialog.ShowDialog(); + } + UpdateScript(); + } + + private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) + { + if (!(comboBox1.SelectedItem is string method)) return; + JArray functions = (JArray)abi["functions"]; + var function = functions.First(p => p["name"].AsString() == method); + JArray _params = (JArray)function["parameters"]; + parameters = _params.Select(p => new ContractParameter(p["type"].AsEnum())).ToArray(); + textBox9.Text = string.Join(", ", _params.Select(p => p["name"].AsString())); + button8.Enabled = parameters.Length > 0; + UpdateScript(); + } + } +} diff --git a/src/Neo.GUI/GUI/InvokeContractDialog.es-ES.resx b/src/Neo.GUI/GUI/InvokeContractDialog.es-ES.resx new file mode 100644 index 0000000000..20f5cf4799 --- /dev/null +++ b/src/Neo.GUI/GUI/InvokeContractDialog.es-ES.resx @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cargar + + + Invocar + + + Cancelar + + + + 38, 17 + + + Tasa: + + + 79, 17 + + + no evaluada + + + Prueba + + + 78, 17 + + + Parámetros: + + + Invocar contrato + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/InvokeContractDialog.resx b/src/Neo.GUI/GUI/InvokeContractDialog.resx new file mode 100644 index 0000000000..df3c2f0ab5 --- /dev/null +++ b/src/Neo.GUI/GUI/InvokeContractDialog.resx @@ -0,0 +1,735 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Bottom, Right + + + + 376, 190 + + + 75, 23 + + + + 1 + + + Load + + + button6 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + + 0 + + + Top, Bottom, Left, Right + + + 6, 6 + + + True + + + Vertical + + + 445, 178 + + + 0 + + + textBox6 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + + 1 + + + Bottom, Right + + + False + + + 321, 482 + + + 75, 23 + + + 6 + + + Invoke + + + button3 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 6 + + + Bottom, Right + + + NoControl + + + 402, 482 + + + 75, 23 + + + 7 + + + Cancel + + + button4 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 5 + + + Bottom, Left + + + True + + + 12, 482 + + + 31, 17 + + + 3 + + + Fee: + + + label6 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 4 + + + Bottom, Left + + + True + + + 49, 482 + + + 87, 17 + + + 4 + + + not evaluated + + + label7 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + Bottom, Right + + + False + + + 240, 482 + + + 75, 23 + + + 5 + + + Test + + + button5 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + 17, 17 + + + AVM File|*.avm + + + Top, Right + + + False + + + NoControl + + + 426, 67 + + + 25, 25 + + + 17 + + + ... + + + button8 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage3 + + + 0 + + + Top, Left, Right + + + 89, 68 + + + 331, 23 + + + 16 + + + textBox9 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage3 + + + 1 + + + True + + + NoControl + + + 6, 71 + + + 77, 17 + + + 15 + + + Parameters: + + + label10 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage3 + + + 2 + + + 89, 36 + + + 362, 25 + + + 14 + + + comboBox1 + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage3 + + + 3 + + + True + + + NoControl + + + 26, 39 + + + 57, 17 + + + 13 + + + Method: + + + label9 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage3 + + + 4 + + + Bottom, Right + + + NoControl + + + 354, 188 + + + 97, 25 + + + 12 + + + Open ABI File + + + button7 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage3 + + + 5 + + + Top, Left, Right + + + 89, 7 + + + 362, 23 + + + 2 + + + textBox8 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage3 + + + 6 + + + True + + + NoControl + + + 10, 10 + + + 73, 17 + + + 1 + + + ScriptHash: + + + label8 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage3 + + + 7 + + + 4, 26 + + + 3, 3, 3, 3 + + + 457, 219 + + + 2 + + + ABI + + + tabPage3 + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControl1 + + + 0 + + + 4, 26 + + + 3, 3, 3, 3 + + + 457, 219 + + + 1 + + + Custom + + + tabPage2 + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControl1 + + + 1 + + + 12, 12 + + + 465, 249 + + + 8 + + + tabControl1 + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Fill + + + 3, 19 + + + True + + + Both + + + 459, 184 + + + 0 + + + False + + + textBox7 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + 12, 267 + + + 465, 206 + + + 9 + + + Results + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + 165, 17 + + + ABI File|*.abi.json + + + True + + + 7, 17 + + + 489, 514 + + + 微软雅黑, 9pt + + + 3, 4, 3, 4 + + + CenterScreen + + + Invoke Contract + + + openFileDialog1 + + + System.Windows.Forms.OpenFileDialog, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + openFileDialog2 + + + System.Windows.Forms.OpenFileDialog, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + InvokeContractDialog + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/InvokeContractDialog.zh-Hans.resx b/src/Neo.GUI/GUI/InvokeContractDialog.zh-Hans.resx new file mode 100644 index 0000000000..d39deccbfa --- /dev/null +++ b/src/Neo.GUI/GUI/InvokeContractDialog.zh-Hans.resx @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 加载 + + + 调用 + + + 取消 + + + + 47, 17 + + + 手续费: + + + 65, 482 + + + 44, 17 + + + 未评估 + + + 试运行 + + + AVM文件|*.avm + + + 24, 71 + + + 59, 17 + + + 参数列表: + + + 48, 39 + + + 35, 17 + + + 方法: + + + 打开ABI文件 + + + 自定义 + + + 运行结果 + + + ABI文件|*.abi.json + + + 调用合约 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/MainForm.Designer.cs b/src/Neo.GUI/GUI/MainForm.Designer.cs new file mode 100644 index 0000000000..2f4da98d9c --- /dev/null +++ b/src/Neo.GUI/GUI/MainForm.Designer.cs @@ -0,0 +1,732 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class MainForm + { + /// + /// 必需的设计器变量。 + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 清理所有正在使用的资源。 + /// + /// 如果应释放托管资源,为 true;否则为 false。 + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows 窗体设计器生成的代码 + + /// + /// 设计器支持所需的方法 - 不要 + /// 使用代码编辑器修改此方法的内容。 + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); + this.menuStrip1 = new System.Windows.Forms.MenuStrip(); + this.钱包WToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.创建钱包数据库NToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.打开钱包数据库OToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.修改密码CToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); + this.退出XToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.交易TToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.转账TToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator(); + this.签名SToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.高级AToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.deployContractToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.invokeContractToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator11 = new System.Windows.Forms.ToolStripSeparator(); + this.选举EToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.signDataToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator9 = new System.Windows.Forms.ToolStripSeparator(); + this.optionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.帮助HToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.查看帮助VToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.官网WToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); + this.开发人员工具TToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.consoleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator(); + this.关于AntSharesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.listView1 = new System.Windows.Forms.ListView(); + this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader4 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader11 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); + this.创建新地址NToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.导入私钥IToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.importWIFToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator10 = new System.Windows.Forms.ToolStripSeparator(); + this.importWatchOnlyAddressToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.创建智能合约SToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.多方签名MToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator12 = new System.Windows.Forms.ToolStripSeparator(); + this.自定义CToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator6 = new System.Windows.Forms.ToolStripSeparator(); + this.查看私钥VToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.viewContractToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.voteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.复制到剪贴板CToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.删除DToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.statusStrip1 = new System.Windows.Forms.StatusStrip(); + this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel(); + this.lbl_height = new System.Windows.Forms.ToolStripStatusLabel(); + this.toolStripStatusLabel4 = new System.Windows.Forms.ToolStripStatusLabel(); + this.lbl_count_node = new System.Windows.Forms.ToolStripStatusLabel(); + this.toolStripProgressBar1 = new System.Windows.Forms.ToolStripProgressBar(); + this.toolStripStatusLabel2 = new System.Windows.Forms.ToolStripStatusLabel(); + this.toolStripStatusLabel3 = new System.Windows.Forms.ToolStripStatusLabel(); + this.timer1 = new System.Windows.Forms.Timer(this.components); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.listView2 = new System.Windows.Forms.ListView(); + this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader6 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader5 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.tabPage3 = new System.Windows.Forms.TabPage(); + this.listView3 = new System.Windows.Forms.ListView(); + this.columnHeader7 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader8 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader9 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader10 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.contextMenuStrip3 = new System.Windows.Forms.ContextMenuStrip(this.components); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.menuStrip1.SuspendLayout(); + this.contextMenuStrip1.SuspendLayout(); + this.statusStrip1.SuspendLayout(); + this.tabControl1.SuspendLayout(); + this.tabPage1.SuspendLayout(); + this.tabPage2.SuspendLayout(); + this.tabPage3.SuspendLayout(); + this.contextMenuStrip3.SuspendLayout(); + this.SuspendLayout(); + // + // menuStrip1 + // + resources.ApplyResources(this.menuStrip1, "menuStrip1"); + this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.钱包WToolStripMenuItem, + this.交易TToolStripMenuItem, + this.高级AToolStripMenuItem, + this.帮助HToolStripMenuItem}); + this.menuStrip1.Name = "menuStrip1"; + // + // 钱包WToolStripMenuItem + // + resources.ApplyResources(this.钱包WToolStripMenuItem, "钱包WToolStripMenuItem"); + this.钱包WToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.创建钱包数据库NToolStripMenuItem, + this.打开钱包数据库OToolStripMenuItem, + this.toolStripSeparator1, + this.修改密码CToolStripMenuItem, + this.toolStripSeparator2, + this.退出XToolStripMenuItem}); + this.钱包WToolStripMenuItem.Name = "钱包WToolStripMenuItem"; + // + // 创建钱包数据库NToolStripMenuItem + // + resources.ApplyResources(this.创建钱包数据库NToolStripMenuItem, "创建钱包数据库NToolStripMenuItem"); + this.创建钱包数据库NToolStripMenuItem.Name = "创建钱包数据库NToolStripMenuItem"; + this.创建钱包数据库NToolStripMenuItem.Click += new System.EventHandler(this.创建钱包数据库NToolStripMenuItem_Click); + // + // 打开钱包数据库OToolStripMenuItem + // + resources.ApplyResources(this.打开钱包数据库OToolStripMenuItem, "打开钱包数据库OToolStripMenuItem"); + this.打开钱包数据库OToolStripMenuItem.Name = "打开钱包数据库OToolStripMenuItem"; + this.打开钱包数据库OToolStripMenuItem.Click += new System.EventHandler(this.打开钱包数据库OToolStripMenuItem_Click); + // + // toolStripSeparator1 + // + resources.ApplyResources(this.toolStripSeparator1, "toolStripSeparator1"); + this.toolStripSeparator1.Name = "toolStripSeparator1"; + // + // 修改密码CToolStripMenuItem + // + resources.ApplyResources(this.修改密码CToolStripMenuItem, "修改密码CToolStripMenuItem"); + this.修改密码CToolStripMenuItem.Name = "修改密码CToolStripMenuItem"; + this.修改密码CToolStripMenuItem.Click += new System.EventHandler(this.修改密码CToolStripMenuItem_Click); + // + // toolStripSeparator2 + // + resources.ApplyResources(this.toolStripSeparator2, "toolStripSeparator2"); + this.toolStripSeparator2.Name = "toolStripSeparator2"; + // + // 退出XToolStripMenuItem + // + resources.ApplyResources(this.退出XToolStripMenuItem, "退出XToolStripMenuItem"); + this.退出XToolStripMenuItem.Name = "退出XToolStripMenuItem"; + this.退出XToolStripMenuItem.Click += new System.EventHandler(this.退出XToolStripMenuItem_Click); + // + // 交易TToolStripMenuItem + // + resources.ApplyResources(this.交易TToolStripMenuItem, "交易TToolStripMenuItem"); + this.交易TToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.转账TToolStripMenuItem, + this.toolStripSeparator5, + this.签名SToolStripMenuItem}); + this.交易TToolStripMenuItem.Name = "交易TToolStripMenuItem"; + // + // 转账TToolStripMenuItem + // + resources.ApplyResources(this.转账TToolStripMenuItem, "转账TToolStripMenuItem"); + this.转账TToolStripMenuItem.Name = "转账TToolStripMenuItem"; + this.转账TToolStripMenuItem.Click += new System.EventHandler(this.转账TToolStripMenuItem_Click); + // + // toolStripSeparator5 + // + resources.ApplyResources(this.toolStripSeparator5, "toolStripSeparator5"); + this.toolStripSeparator5.Name = "toolStripSeparator5"; + // + // 签名SToolStripMenuItem + // + resources.ApplyResources(this.签名SToolStripMenuItem, "签名SToolStripMenuItem"); + this.签名SToolStripMenuItem.Name = "签名SToolStripMenuItem"; + this.签名SToolStripMenuItem.Click += new System.EventHandler(this.签名SToolStripMenuItem_Click); + // + // 高级AToolStripMenuItem + // + resources.ApplyResources(this.高级AToolStripMenuItem, "高级AToolStripMenuItem"); + this.高级AToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.deployContractToolStripMenuItem, + this.invokeContractToolStripMenuItem, + this.toolStripSeparator11, + this.选举EToolStripMenuItem, + this.signDataToolStripMenuItem, + this.toolStripSeparator9, + this.optionsToolStripMenuItem}); + this.高级AToolStripMenuItem.Name = "高级AToolStripMenuItem"; + // + // deployContractToolStripMenuItem + // + resources.ApplyResources(this.deployContractToolStripMenuItem, "deployContractToolStripMenuItem"); + this.deployContractToolStripMenuItem.Name = "deployContractToolStripMenuItem"; + this.deployContractToolStripMenuItem.Click += new System.EventHandler(this.deployContractToolStripMenuItem_Click); + // + // invokeContractToolStripMenuItem + // + resources.ApplyResources(this.invokeContractToolStripMenuItem, "invokeContractToolStripMenuItem"); + this.invokeContractToolStripMenuItem.Name = "invokeContractToolStripMenuItem"; + this.invokeContractToolStripMenuItem.Click += new System.EventHandler(this.invokeContractToolStripMenuItem_Click); + // + // toolStripSeparator11 + // + resources.ApplyResources(this.toolStripSeparator11, "toolStripSeparator11"); + this.toolStripSeparator11.Name = "toolStripSeparator11"; + // + // 选举EToolStripMenuItem + // + resources.ApplyResources(this.选举EToolStripMenuItem, "选举EToolStripMenuItem"); + this.选举EToolStripMenuItem.Name = "选举EToolStripMenuItem"; + this.选举EToolStripMenuItem.Click += new System.EventHandler(this.选举EToolStripMenuItem_Click); + // + // signDataToolStripMenuItem + // + resources.ApplyResources(this.signDataToolStripMenuItem, "signDataToolStripMenuItem"); + this.signDataToolStripMenuItem.Name = "signDataToolStripMenuItem"; + this.signDataToolStripMenuItem.Click += new System.EventHandler(this.signDataToolStripMenuItem_Click); + // + // toolStripSeparator9 + // + resources.ApplyResources(this.toolStripSeparator9, "toolStripSeparator9"); + this.toolStripSeparator9.Name = "toolStripSeparator9"; + // + // optionsToolStripMenuItem + // + resources.ApplyResources(this.optionsToolStripMenuItem, "optionsToolStripMenuItem"); + this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem"; + this.optionsToolStripMenuItem.Click += new System.EventHandler(this.optionsToolStripMenuItem_Click); + // + // 帮助HToolStripMenuItem + // + resources.ApplyResources(this.帮助HToolStripMenuItem, "帮助HToolStripMenuItem"); + this.帮助HToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.查看帮助VToolStripMenuItem, + this.官网WToolStripMenuItem, + this.toolStripSeparator3, + this.开发人员工具TToolStripMenuItem, + this.consoleToolStripMenuItem, + this.toolStripSeparator4, + this.关于AntSharesToolStripMenuItem}); + this.帮助HToolStripMenuItem.Name = "帮助HToolStripMenuItem"; + // + // 查看帮助VToolStripMenuItem + // + resources.ApplyResources(this.查看帮助VToolStripMenuItem, "查看帮助VToolStripMenuItem"); + this.查看帮助VToolStripMenuItem.Name = "查看帮助VToolStripMenuItem"; + // + // 官网WToolStripMenuItem + // + resources.ApplyResources(this.官网WToolStripMenuItem, "官网WToolStripMenuItem"); + this.官网WToolStripMenuItem.Name = "官网WToolStripMenuItem"; + this.官网WToolStripMenuItem.Click += new System.EventHandler(this.官网WToolStripMenuItem_Click); + // + // toolStripSeparator3 + // + resources.ApplyResources(this.toolStripSeparator3, "toolStripSeparator3"); + this.toolStripSeparator3.Name = "toolStripSeparator3"; + // + // 开发人员工具TToolStripMenuItem + // + resources.ApplyResources(this.开发人员工具TToolStripMenuItem, "开发人员工具TToolStripMenuItem"); + this.开发人员工具TToolStripMenuItem.Name = "开发人员工具TToolStripMenuItem"; + this.开发人员工具TToolStripMenuItem.Click += new System.EventHandler(this.开发人员工具TToolStripMenuItem_Click); + // + // consoleToolStripMenuItem + // + resources.ApplyResources(this.consoleToolStripMenuItem, "consoleToolStripMenuItem"); + this.consoleToolStripMenuItem.Name = "consoleToolStripMenuItem"; + this.consoleToolStripMenuItem.Click += new System.EventHandler(this.consoleToolStripMenuItem_Click); + // + // toolStripSeparator4 + // + resources.ApplyResources(this.toolStripSeparator4, "toolStripSeparator4"); + this.toolStripSeparator4.Name = "toolStripSeparator4"; + // + // 关于AntSharesToolStripMenuItem + // + resources.ApplyResources(this.关于AntSharesToolStripMenuItem, "关于AntSharesToolStripMenuItem"); + this.关于AntSharesToolStripMenuItem.Name = "关于AntSharesToolStripMenuItem"; + this.关于AntSharesToolStripMenuItem.Click += new System.EventHandler(this.关于AntSharesToolStripMenuItem_Click); + // + // listView1 + // + resources.ApplyResources(this.listView1, "listView1"); + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader1, + this.columnHeader4, + this.columnHeader11}); + this.listView1.ContextMenuStrip = this.contextMenuStrip1; + this.listView1.FullRowSelect = true; + this.listView1.GridLines = true; + this.listView1.Groups.AddRange(new System.Windows.Forms.ListViewGroup[] { + ((System.Windows.Forms.ListViewGroup)(resources.GetObject("listView1.Groups"))), + ((System.Windows.Forms.ListViewGroup)(resources.GetObject("listView1.Groups1"))), + ((System.Windows.Forms.ListViewGroup)(resources.GetObject("listView1.Groups2")))}); + this.listView1.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.listView1.HideSelection = false; + this.listView1.Name = "listView1"; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.DoubleClick += new System.EventHandler(this.listView1_DoubleClick); + // + // columnHeader1 + // + resources.ApplyResources(this.columnHeader1, "columnHeader1"); + // + // columnHeader4 + // + resources.ApplyResources(this.columnHeader4, "columnHeader4"); + // + // columnHeader11 + // + resources.ApplyResources(this.columnHeader11, "columnHeader11"); + // + // contextMenuStrip1 + // + resources.ApplyResources(this.contextMenuStrip1, "contextMenuStrip1"); + this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.创建新地址NToolStripMenuItem, + this.导入私钥IToolStripMenuItem, + this.创建智能合约SToolStripMenuItem, + this.toolStripSeparator6, + this.查看私钥VToolStripMenuItem, + this.viewContractToolStripMenuItem, + this.voteToolStripMenuItem, + this.复制到剪贴板CToolStripMenuItem, + this.删除DToolStripMenuItem}); + this.contextMenuStrip1.Name = "contextMenuStrip1"; + this.contextMenuStrip1.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStrip1_Opening); + // + // 创建新地址NToolStripMenuItem + // + resources.ApplyResources(this.创建新地址NToolStripMenuItem, "创建新地址NToolStripMenuItem"); + this.创建新地址NToolStripMenuItem.Name = "创建新地址NToolStripMenuItem"; + this.创建新地址NToolStripMenuItem.Click += new System.EventHandler(this.创建新地址NToolStripMenuItem_Click); + // + // 导入私钥IToolStripMenuItem + // + resources.ApplyResources(this.导入私钥IToolStripMenuItem, "导入私钥IToolStripMenuItem"); + this.导入私钥IToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.importWIFToolStripMenuItem, + this.toolStripSeparator10, + this.importWatchOnlyAddressToolStripMenuItem}); + this.导入私钥IToolStripMenuItem.Name = "导入私钥IToolStripMenuItem"; + // + // importWIFToolStripMenuItem + // + resources.ApplyResources(this.importWIFToolStripMenuItem, "importWIFToolStripMenuItem"); + this.importWIFToolStripMenuItem.Name = "importWIFToolStripMenuItem"; + this.importWIFToolStripMenuItem.Click += new System.EventHandler(this.importWIFToolStripMenuItem_Click); + // + // toolStripSeparator10 + // + resources.ApplyResources(this.toolStripSeparator10, "toolStripSeparator10"); + this.toolStripSeparator10.Name = "toolStripSeparator10"; + // + // importWatchOnlyAddressToolStripMenuItem + // + resources.ApplyResources(this.importWatchOnlyAddressToolStripMenuItem, "importWatchOnlyAddressToolStripMenuItem"); + this.importWatchOnlyAddressToolStripMenuItem.Name = "importWatchOnlyAddressToolStripMenuItem"; + this.importWatchOnlyAddressToolStripMenuItem.Click += new System.EventHandler(this.importWatchOnlyAddressToolStripMenuItem_Click); + // + // 创建智能合约SToolStripMenuItem + // + resources.ApplyResources(this.创建智能合约SToolStripMenuItem, "创建智能合约SToolStripMenuItem"); + this.创建智能合约SToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.多方签名MToolStripMenuItem, + this.toolStripSeparator12, + this.自定义CToolStripMenuItem}); + this.创建智能合约SToolStripMenuItem.Name = "创建智能合约SToolStripMenuItem"; + // + // 多方签名MToolStripMenuItem + // + resources.ApplyResources(this.多方签名MToolStripMenuItem, "多方签名MToolStripMenuItem"); + this.多方签名MToolStripMenuItem.Name = "多方签名MToolStripMenuItem"; + this.多方签名MToolStripMenuItem.Click += new System.EventHandler(this.多方签名MToolStripMenuItem_Click); + // + // toolStripSeparator12 + // + resources.ApplyResources(this.toolStripSeparator12, "toolStripSeparator12"); + this.toolStripSeparator12.Name = "toolStripSeparator12"; + // + // 自定义CToolStripMenuItem + // + resources.ApplyResources(this.自定义CToolStripMenuItem, "自定义CToolStripMenuItem"); + this.自定义CToolStripMenuItem.Name = "自定义CToolStripMenuItem"; + this.自定义CToolStripMenuItem.Click += new System.EventHandler(this.自定义CToolStripMenuItem_Click); + // + // toolStripSeparator6 + // + resources.ApplyResources(this.toolStripSeparator6, "toolStripSeparator6"); + this.toolStripSeparator6.Name = "toolStripSeparator6"; + // + // 查看私钥VToolStripMenuItem + // + resources.ApplyResources(this.查看私钥VToolStripMenuItem, "查看私钥VToolStripMenuItem"); + this.查看私钥VToolStripMenuItem.Name = "查看私钥VToolStripMenuItem"; + this.查看私钥VToolStripMenuItem.Click += new System.EventHandler(this.查看私钥VToolStripMenuItem_Click); + // + // viewContractToolStripMenuItem + // + resources.ApplyResources(this.viewContractToolStripMenuItem, "viewContractToolStripMenuItem"); + this.viewContractToolStripMenuItem.Name = "viewContractToolStripMenuItem"; + this.viewContractToolStripMenuItem.Click += new System.EventHandler(this.viewContractToolStripMenuItem_Click); + // + // voteToolStripMenuItem + // + resources.ApplyResources(this.voteToolStripMenuItem, "voteToolStripMenuItem"); + this.voteToolStripMenuItem.Name = "voteToolStripMenuItem"; + this.voteToolStripMenuItem.Click += new System.EventHandler(this.voteToolStripMenuItem_Click); + // + // 复制到剪贴板CToolStripMenuItem + // + resources.ApplyResources(this.复制到剪贴板CToolStripMenuItem, "复制到剪贴板CToolStripMenuItem"); + this.复制到剪贴板CToolStripMenuItem.Name = "复制到剪贴板CToolStripMenuItem"; + this.复制到剪贴板CToolStripMenuItem.Click += new System.EventHandler(this.复制到剪贴板CToolStripMenuItem_Click); + // + // 删除DToolStripMenuItem + // + resources.ApplyResources(this.删除DToolStripMenuItem, "删除DToolStripMenuItem"); + this.删除DToolStripMenuItem.Name = "删除DToolStripMenuItem"; + this.删除DToolStripMenuItem.Click += new System.EventHandler(this.删除DToolStripMenuItem_Click); + // + // statusStrip1 + // + resources.ApplyResources(this.statusStrip1, "statusStrip1"); + this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripStatusLabel1, + this.lbl_height, + this.toolStripStatusLabel4, + this.lbl_count_node, + this.toolStripProgressBar1, + this.toolStripStatusLabel2, + this.toolStripStatusLabel3}); + this.statusStrip1.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.HorizontalStackWithOverflow; + this.statusStrip1.Name = "statusStrip1"; + this.statusStrip1.SizingGrip = false; + // + // toolStripStatusLabel1 + // + resources.ApplyResources(this.toolStripStatusLabel1, "toolStripStatusLabel1"); + this.toolStripStatusLabel1.Name = "toolStripStatusLabel1"; + // + // lbl_height + // + resources.ApplyResources(this.lbl_height, "lbl_height"); + this.lbl_height.Name = "lbl_height"; + // + // toolStripStatusLabel4 + // + resources.ApplyResources(this.toolStripStatusLabel4, "toolStripStatusLabel4"); + this.toolStripStatusLabel4.Name = "toolStripStatusLabel4"; + // + // lbl_count_node + // + resources.ApplyResources(this.lbl_count_node, "lbl_count_node"); + this.lbl_count_node.Name = "lbl_count_node"; + // + // toolStripProgressBar1 + // + resources.ApplyResources(this.toolStripProgressBar1, "toolStripProgressBar1"); + this.toolStripProgressBar1.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right; + this.toolStripProgressBar1.Maximum = 15; + this.toolStripProgressBar1.Name = "toolStripProgressBar1"; + this.toolStripProgressBar1.Step = 1; + // + // toolStripStatusLabel2 + // + resources.ApplyResources(this.toolStripStatusLabel2, "toolStripStatusLabel2"); + this.toolStripStatusLabel2.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right; + this.toolStripStatusLabel2.Name = "toolStripStatusLabel2"; + // + // toolStripStatusLabel3 + // + resources.ApplyResources(this.toolStripStatusLabel3, "toolStripStatusLabel3"); + this.toolStripStatusLabel3.IsLink = true; + this.toolStripStatusLabel3.Name = "toolStripStatusLabel3"; + this.toolStripStatusLabel3.Click += new System.EventHandler(this.toolStripStatusLabel3_Click); + // + // timer1 + // + this.timer1.Enabled = true; + this.timer1.Interval = 500; + this.timer1.Tick += new System.EventHandler(this.timer1_Tick); + // + // tabControl1 + // + resources.ApplyResources(this.tabControl1, "tabControl1"); + this.tabControl1.Controls.Add(this.tabPage1); + this.tabControl1.Controls.Add(this.tabPage2); + this.tabControl1.Controls.Add(this.tabPage3); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + // + // tabPage1 + // + resources.ApplyResources(this.tabPage1, "tabPage1"); + this.tabPage1.Controls.Add(this.listView1); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.UseVisualStyleBackColor = true; + // + // tabPage2 + // + resources.ApplyResources(this.tabPage2, "tabPage2"); + this.tabPage2.Controls.Add(this.listView2); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.UseVisualStyleBackColor = true; + // + // listView2 + // + resources.ApplyResources(this.listView2, "listView2"); + this.listView2.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader2, + this.columnHeader6, + this.columnHeader3, + this.columnHeader5}); + this.listView2.FullRowSelect = true; + this.listView2.GridLines = true; + this.listView2.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.listView2.HideSelection = false; + this.listView2.Name = "listView2"; + this.listView2.ShowGroups = false; + this.listView2.UseCompatibleStateImageBehavior = false; + this.listView2.View = System.Windows.Forms.View.Details; + this.listView2.DoubleClick += new System.EventHandler(this.listView2_DoubleClick); + // + // columnHeader2 + // + resources.ApplyResources(this.columnHeader2, "columnHeader2"); + // + // columnHeader6 + // + resources.ApplyResources(this.columnHeader6, "columnHeader6"); + // + // columnHeader3 + // + resources.ApplyResources(this.columnHeader3, "columnHeader3"); + // + // columnHeader5 + // + resources.ApplyResources(this.columnHeader5, "columnHeader5"); + // + // tabPage3 + // + resources.ApplyResources(this.tabPage3, "tabPage3"); + this.tabPage3.Controls.Add(this.listView3); + this.tabPage3.Name = "tabPage3"; + this.tabPage3.UseVisualStyleBackColor = true; + // + // listView3 + // + resources.ApplyResources(this.listView3, "listView3"); + this.listView3.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader7, + this.columnHeader8, + this.columnHeader9, + this.columnHeader10}); + this.listView3.ContextMenuStrip = this.contextMenuStrip3; + this.listView3.FullRowSelect = true; + this.listView3.GridLines = true; + this.listView3.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.listView3.HideSelection = false; + this.listView3.Name = "listView3"; + this.listView3.ShowGroups = false; + this.listView3.UseCompatibleStateImageBehavior = false; + this.listView3.View = System.Windows.Forms.View.Details; + this.listView3.DoubleClick += new System.EventHandler(this.listView3_DoubleClick); + // + // columnHeader7 + // + resources.ApplyResources(this.columnHeader7, "columnHeader7"); + // + // columnHeader8 + // + resources.ApplyResources(this.columnHeader8, "columnHeader8"); + // + // columnHeader9 + // + resources.ApplyResources(this.columnHeader9, "columnHeader9"); + // + // columnHeader10 + // + resources.ApplyResources(this.columnHeader10, "columnHeader10"); + // + // contextMenuStrip3 + // + resources.ApplyResources(this.contextMenuStrip3, "contextMenuStrip3"); + this.contextMenuStrip3.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripMenuItem1}); + this.contextMenuStrip3.Name = "contextMenuStrip3"; + // + // toolStripMenuItem1 + // + resources.ApplyResources(this.toolStripMenuItem1, "toolStripMenuItem1"); + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Click += new System.EventHandler(this.toolStripMenuItem1_Click); + // + // MainForm + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.tabControl1); + this.Controls.Add(this.statusStrip1); + this.Controls.Add(this.menuStrip1); + this.MainMenuStrip = this.menuStrip1; + this.Name = "MainForm"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); + this.Load += new System.EventHandler(this.MainForm_Load); + this.menuStrip1.ResumeLayout(false); + this.menuStrip1.PerformLayout(); + this.contextMenuStrip1.ResumeLayout(false); + this.statusStrip1.ResumeLayout(false); + this.statusStrip1.PerformLayout(); + this.tabControl1.ResumeLayout(false); + this.tabPage1.ResumeLayout(false); + this.tabPage2.ResumeLayout(false); + this.tabPage3.ResumeLayout(false); + this.contextMenuStrip3.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.MenuStrip menuStrip1; + private System.Windows.Forms.ToolStripMenuItem 钱包WToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem 创建钱包数据库NToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem 打开钱包数据库OToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; + private System.Windows.Forms.ToolStripMenuItem 修改密码CToolStripMenuItem; + private System.Windows.Forms.ColumnHeader columnHeader1; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; + private System.Windows.Forms.ToolStripMenuItem 退出XToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem 帮助HToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem 查看帮助VToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem 官网WToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator3; + private System.Windows.Forms.ToolStripMenuItem 开发人员工具TToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator4; + private System.Windows.Forms.ToolStripMenuItem 关于AntSharesToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem 交易TToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem 签名SToolStripMenuItem; + private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; + private System.Windows.Forms.ToolStripMenuItem 创建新地址NToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem 导入私钥IToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator6; + private System.Windows.Forms.ToolStripMenuItem 查看私钥VToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem 复制到剪贴板CToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem 删除DToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator5; + private System.Windows.Forms.StatusStrip statusStrip1; + private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel1; + private System.Windows.Forms.ToolStripStatusLabel lbl_height; + private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel4; + private System.Windows.Forms.ToolStripStatusLabel lbl_count_node; + private System.Windows.Forms.Timer timer1; + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage tabPage1; + private System.Windows.Forms.TabPage tabPage2; + private System.Windows.Forms.ListView listView2; + private System.Windows.Forms.ColumnHeader columnHeader2; + private System.Windows.Forms.ColumnHeader columnHeader3; + private System.Windows.Forms.ToolStripMenuItem 转账TToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem 高级AToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem 创建智能合约SToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem 多方签名MToolStripMenuItem; + private System.Windows.Forms.ListView listView1; + private System.Windows.Forms.ColumnHeader columnHeader5; + private System.Windows.Forms.ColumnHeader columnHeader6; + private System.Windows.Forms.ToolStripMenuItem importWIFToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem 选举EToolStripMenuItem; + private System.Windows.Forms.TabPage tabPage3; + private System.Windows.Forms.ListView listView3; + private System.Windows.Forms.ColumnHeader columnHeader7; + private System.Windows.Forms.ColumnHeader columnHeader8; + private System.Windows.Forms.ColumnHeader columnHeader9; + private System.Windows.Forms.ToolStripProgressBar toolStripProgressBar1; + private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel2; + private System.Windows.Forms.ToolStripMenuItem 自定义CToolStripMenuItem; + private System.Windows.Forms.ColumnHeader columnHeader10; + private System.Windows.Forms.ContextMenuStrip contextMenuStrip3; + private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem1; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator9; + private System.Windows.Forms.ToolStripMenuItem optionsToolStripMenuItem; + private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel3; + private System.Windows.Forms.ToolStripMenuItem viewContractToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator10; + private System.Windows.Forms.ToolStripMenuItem importWatchOnlyAddressToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem voteToolStripMenuItem; + private System.Windows.Forms.ColumnHeader columnHeader4; + private System.Windows.Forms.ColumnHeader columnHeader11; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator11; + private System.Windows.Forms.ToolStripMenuItem deployContractToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem invokeContractToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator12; + private System.Windows.Forms.ToolStripMenuItem signDataToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem consoleToolStripMenuItem; + } +} + diff --git a/src/Neo.GUI/GUI/MainForm.cs b/src/Neo.GUI/GUI/MainForm.cs new file mode 100644 index 0000000000..3114e542b6 --- /dev/null +++ b/src/Neo.GUI/GUI/MainForm.cs @@ -0,0 +1,616 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MainForm.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 Akka.Actor; +using Neo.IO.Actors; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Properties; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.Wallets; +using Neo.Wallets.NEP6; +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Numerics; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Windows.Forms; +using System.Xml.Linq; +using static Neo.Program; +using static Neo.SmartContract.Helper; +using VMArray = Neo.VM.Types.Array; + +namespace Neo.GUI +{ + internal partial class MainForm : Form + { + private bool check_nep5_balance = false; + private DateTime persistence_time = DateTime.MinValue; + private IActorRef actor; + + public MainForm(XDocument xdoc = null) + { + InitializeComponent(); + + toolStripProgressBar1.Maximum = (int)Service.NeoSystem.Settings.TimePerBlock.TotalSeconds; + + if (xdoc != null) + { + Version version = Assembly.GetExecutingAssembly().GetName().Version; + Version latest = Version.Parse(xdoc.Element("update").Attribute("latest").Value); + if (version < latest) + { + toolStripStatusLabel3.Tag = xdoc; + toolStripStatusLabel3.Text += $": {latest}"; + toolStripStatusLabel3.Visible = true; + } + } + } + + private void AddAccount(WalletAccount account, bool selected = false) + { + ListViewItem item = listView1.Items[account.Address]; + if (item != null) + { + if (!account.WatchOnly && ((WalletAccount)item.Tag).WatchOnly) + { + listView1.Items.Remove(item); + item = null; + } + } + if (item == null) + { + string groupName = account.WatchOnly ? "watchOnlyGroup" : IsSignatureContract(account.Contract.Script) ? "standardContractGroup" : "nonstandardContractGroup"; + item = listView1.Items.Add(new ListViewItem(new[] + { + new ListViewItem.ListViewSubItem + { + Name = "address", + Text = account.Address + }, + new ListViewItem.ListViewSubItem + { + Name = NativeContract.NEO.Symbol + }, + new ListViewItem.ListViewSubItem + { + Name = NativeContract.GAS.Symbol + } + }, -1, listView1.Groups[groupName]) + { + Name = account.Address, + Tag = account + }); + } + item.Selected = selected; + } + + private void Blockchain_PersistCompleted(Blockchain.PersistCompleted e) + { + if (IsDisposed) return; + persistence_time = DateTime.UtcNow; + if (Service.CurrentWallet != null) + check_nep5_balance = true; + BeginInvoke(new Action(RefreshConfirmations)); + } + + private static void OpenBrowser(string url) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Process.Start(new ProcessStartInfo("cmd", $"/c start {url}")); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + Process.Start("xdg-open", url); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + Process.Start("open", url); + } + } + + private void Service_WalletChanged(object sender, Wallet wallet) + { + if (InvokeRequired) + { + Invoke(new EventHandler(Service_WalletChanged), sender, wallet); + return; + } + + listView3.Items.Clear(); + 修改密码CToolStripMenuItem.Enabled = wallet != null; + 交易TToolStripMenuItem.Enabled = wallet != null; + signDataToolStripMenuItem.Enabled = wallet != null; + deployContractToolStripMenuItem.Enabled = wallet != null; + invokeContractToolStripMenuItem.Enabled = wallet != null; + 选举EToolStripMenuItem.Enabled = wallet != null; + 创建新地址NToolStripMenuItem.Enabled = wallet != null; + 导入私钥IToolStripMenuItem.Enabled = wallet != null; + 创建智能合约SToolStripMenuItem.Enabled = wallet != null; + listView1.Items.Clear(); + if (wallet != null) + { + foreach (WalletAccount account in wallet.GetAccounts().ToArray()) + { + AddAccount(account); + } + } + check_nep5_balance = true; + } + + private void RefreshConfirmations() + { + foreach (ListViewItem item in listView3.Items) + { + uint? height = item.Tag as uint?; + int? confirmations = (int)NativeContract.Ledger.CurrentIndex(Service.NeoSystem.StoreView) - (int?)height + 1; + if (confirmations <= 0) confirmations = null; + item.SubItems["confirmations"].Text = confirmations?.ToString() ?? Strings.Unconfirmed; + } + } + + private void MainForm_Load(object sender, EventArgs e) + { + actor = Service.NeoSystem.ActorSystem.ActorOf(EventWrapper.Props(Blockchain_PersistCompleted)); + Service.WalletChanged += Service_WalletChanged; + } + + private void MainForm_FormClosing(object sender, FormClosingEventArgs e) + { + if (actor != null) + Service.NeoSystem.ActorSystem.Stop(actor); + Service.WalletChanged -= Service_WalletChanged; + } + + private void timer1_Tick(object sender, EventArgs e) + { + uint height = NativeContract.Ledger.CurrentIndex(Service.NeoSystem.StoreView); + uint headerHeight = Service.NeoSystem.HeaderCache.Last?.Index ?? height; + + lbl_height.Text = $"{height}/{headerHeight}"; + lbl_count_node.Text = Service.LocalNode.ConnectedCount.ToString(); + TimeSpan persistence_span = DateTime.UtcNow - persistence_time; + if (persistence_span < TimeSpan.Zero) persistence_span = TimeSpan.Zero; + if (persistence_span > Service.NeoSystem.Settings.TimePerBlock) + { + toolStripProgressBar1.Style = ProgressBarStyle.Marquee; + } + else + { + toolStripProgressBar1.Value = persistence_span.Seconds; + toolStripProgressBar1.Style = ProgressBarStyle.Blocks; + } + if (Service.CurrentWallet is null) return; + if (!check_nep5_balance || persistence_span < TimeSpan.FromSeconds(2)) return; + check_nep5_balance = false; + UInt160[] addresses = Service.CurrentWallet.GetAccounts().Select(p => p.ScriptHash).ToArray(); + if (addresses.Length == 0) return; + using var snapshot = Service.NeoSystem.GetSnapshot(); + foreach (UInt160 assetId in NEP5Watched) + { + byte[] script; + using (ScriptBuilder sb = new ScriptBuilder()) + { + for (int i = addresses.Length - 1; i >= 0; i--) + sb.EmitDynamicCall(assetId, "balanceOf", addresses[i]); + sb.Emit(OpCode.DEPTH, OpCode.PACK); + sb.EmitDynamicCall(assetId, "decimals"); + sb.EmitDynamicCall(assetId, "name"); + script = sb.ToArray(); + } + using ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, gas: 0_20000000L * addresses.Length); + if (engine.State.HasFlag(VMState.FAULT)) continue; + string name = engine.ResultStack.Pop().GetString(); + byte decimals = (byte)engine.ResultStack.Pop().GetInteger(); + BigInteger[] balances = ((VMArray)engine.ResultStack.Pop()).Select(p => p.GetInteger()).ToArray(); + string symbol = null; + if (assetId.Equals(NativeContract.NEO.Hash)) + symbol = NativeContract.NEO.Symbol; + else if (assetId.Equals(NativeContract.GAS.Hash)) + symbol = NativeContract.GAS.Symbol; + if (symbol != null) + for (int i = 0; i < addresses.Length; i++) + listView1.Items[addresses[i].ToAddress(Service.NeoSystem.Settings.AddressVersion)].SubItems[symbol].Text = new BigDecimal(balances[i], decimals).ToString(); + BigInteger amount = balances.Sum(); + if (amount == 0) + { + listView2.Items.RemoveByKey(assetId.ToString()); + continue; + } + BigDecimal balance = new BigDecimal(amount, decimals); + if (listView2.Items.ContainsKey(assetId.ToString())) + { + listView2.Items[assetId.ToString()].SubItems["value"].Text = balance.ToString(); + } + else + { + listView2.Items.Add(new ListViewItem(new[] + { + new ListViewItem.ListViewSubItem + { + Name = "name", + Text = name + }, + new ListViewItem.ListViewSubItem + { + Name = "type", + Text = "NEP-5" + }, + new ListViewItem.ListViewSubItem + { + Name = "value", + Text = balance.ToString() + }, + new ListViewItem.ListViewSubItem + { + ForeColor = Color.Gray, + Name = "issuer", + Text = $"ScriptHash:{assetId}" + } + }, -1) + { + Name = assetId.ToString(), + UseItemStyleForSubItems = false + }); + } + } + } + + private void 创建钱包数据库NToolStripMenuItem_Click(object sender, EventArgs e) + { + using CreateWalletDialog dialog = new CreateWalletDialog(); + if (dialog.ShowDialog() != DialogResult.OK) return; + Service.CreateWallet(dialog.WalletPath, dialog.Password); + } + + private void 打开钱包数据库OToolStripMenuItem_Click(object sender, EventArgs e) + { + using OpenWalletDialog dialog = new OpenWalletDialog(); + if (dialog.ShowDialog() != DialogResult.OK) return; + try + { + Service.OpenWallet(dialog.WalletPath, dialog.Password); + } + catch (CryptographicException) + { + MessageBox.Show(Strings.PasswordIncorrect); + } + } + + private void 修改密码CToolStripMenuItem_Click(object sender, EventArgs e) + { + using ChangePasswordDialog dialog = new ChangePasswordDialog(); + if (dialog.ShowDialog() != DialogResult.OK) return; + if (Service.CurrentWallet.ChangePassword(dialog.OldPassword, dialog.NewPassword)) + { + if (Service.CurrentWallet is NEP6Wallet wallet) + wallet.Save(); + MessageBox.Show(Strings.ChangePasswordSuccessful); + } + else + { + MessageBox.Show(Strings.PasswordIncorrect); + } + } + + private void 退出XToolStripMenuItem_Click(object sender, EventArgs e) + { + Close(); + } + + private void 转账TToolStripMenuItem_Click(object sender, EventArgs e) + { + Transaction tx; + using (TransferDialog dialog = new TransferDialog()) + { + if (dialog.ShowDialog() != DialogResult.OK) return; + tx = dialog.GetTransaction(); + } + using (InvokeContractDialog dialog = new InvokeContractDialog(tx)) + { + if (dialog.ShowDialog() != DialogResult.OK) return; + tx = dialog.GetTransaction(); + } + Helper.SignAndShowInformation(tx); + } + + private void 签名SToolStripMenuItem_Click(object sender, EventArgs e) + { + using SigningTxDialog dialog = new SigningTxDialog(); + dialog.ShowDialog(); + } + + private void deployContractToolStripMenuItem_Click(object sender, EventArgs e) + { + try + { + byte[] script; + using (DeployContractDialog dialog = new DeployContractDialog()) + { + if (dialog.ShowDialog() != DialogResult.OK) return; + script = dialog.GetScript(); + } + using (InvokeContractDialog dialog = new InvokeContractDialog(script)) + { + if (dialog.ShowDialog() != DialogResult.OK) return; + Helper.SignAndShowInformation(dialog.GetTransaction()); + } + } + catch { } + } + + private void invokeContractToolStripMenuItem_Click(object sender, EventArgs e) + { + using InvokeContractDialog dialog = new InvokeContractDialog(); + if (dialog.ShowDialog() != DialogResult.OK) return; + try + { + Helper.SignAndShowInformation(dialog.GetTransaction()); + } + catch + { + return; + } + } + + private void 选举EToolStripMenuItem_Click(object sender, EventArgs e) + { + try + { + byte[] script; + using (ElectionDialog dialog = new ElectionDialog()) + { + if (dialog.ShowDialog() != DialogResult.OK) return; + script = dialog.GetScript(); + } + using (InvokeContractDialog dialog = new InvokeContractDialog(script)) + { + if (dialog.ShowDialog() != DialogResult.OK) return; + Helper.SignAndShowInformation(dialog.GetTransaction()); + } + } + catch { } + } + + private void signDataToolStripMenuItem_Click(object sender, EventArgs e) + { + using SigningDialog dialog = new SigningDialog(); + dialog.ShowDialog(); + } + + private void optionsToolStripMenuItem_Click(object sender, EventArgs e) + { + } + + private void 官网WToolStripMenuItem_Click(object sender, EventArgs e) + { + OpenBrowser("https://neo.org/"); + } + + private void 开发人员工具TToolStripMenuItem_Click(object sender, EventArgs e) + { + Helper.Show(); + } + + private void consoleToolStripMenuItem_Click(object sender, EventArgs e) + { + Helper.Show(); + } + + private void 关于AntSharesToolStripMenuItem_Click(object sender, EventArgs e) + { + MessageBox.Show($"{Strings.AboutMessage} {Strings.AboutVersion}{Assembly.GetExecutingAssembly().GetName().Version}", Strings.About); + } + + private void contextMenuStrip1_Opening(object sender, CancelEventArgs e) + { + 查看私钥VToolStripMenuItem.Enabled = + listView1.SelectedIndices.Count == 1 && + !((WalletAccount)listView1.SelectedItems[0].Tag).WatchOnly && + IsSignatureContract(((WalletAccount)listView1.SelectedItems[0].Tag).Contract.Script); + viewContractToolStripMenuItem.Enabled = + listView1.SelectedIndices.Count == 1 && + !((WalletAccount)listView1.SelectedItems[0].Tag).WatchOnly; + voteToolStripMenuItem.Enabled = + listView1.SelectedIndices.Count == 1 && + !((WalletAccount)listView1.SelectedItems[0].Tag).WatchOnly && + !string.IsNullOrEmpty(listView1.SelectedItems[0].SubItems[NativeContract.NEO.Symbol].Text) && + decimal.Parse(listView1.SelectedItems[0].SubItems[NativeContract.NEO.Symbol].Text) > 0; + 复制到剪贴板CToolStripMenuItem.Enabled = listView1.SelectedIndices.Count == 1; + 删除DToolStripMenuItem.Enabled = listView1.SelectedIndices.Count > 0; + } + + private void 创建新地址NToolStripMenuItem_Click(object sender, EventArgs e) + { + listView1.SelectedIndices.Clear(); + WalletAccount account = Service.CurrentWallet.CreateAccount(); + AddAccount(account, true); + if (Service.CurrentWallet is NEP6Wallet wallet) + wallet.Save(); + } + + private void importWIFToolStripMenuItem_Click(object sender, EventArgs e) + { + using ImportPrivateKeyDialog dialog = new ImportPrivateKeyDialog(); + if (dialog.ShowDialog() != DialogResult.OK) return; + listView1.SelectedIndices.Clear(); + foreach (string wif in dialog.WifStrings) + { + WalletAccount account; + try + { + account = Service.CurrentWallet.Import(wif); + } + catch (FormatException) + { + continue; + } + AddAccount(account, true); + } + if (Service.CurrentWallet is NEP6Wallet wallet) + wallet.Save(); + } + + private void importWatchOnlyAddressToolStripMenuItem_Click(object sender, EventArgs e) + { + string text = InputBox.Show(Strings.Address, Strings.ImportWatchOnlyAddress); + if (string.IsNullOrEmpty(text)) return; + using (StringReader reader = new StringReader(text)) + { + while (true) + { + string address = reader.ReadLine(); + if (address == null) break; + address = address.Trim(); + if (string.IsNullOrEmpty(address)) continue; + UInt160 scriptHash; + try + { + scriptHash = address.ToScriptHash(Service.NeoSystem.Settings.AddressVersion); + } + catch (FormatException) + { + continue; + } + WalletAccount account = Service.CurrentWallet.CreateAccount(scriptHash); + AddAccount(account, true); + } + } + if (Service.CurrentWallet is NEP6Wallet wallet) + wallet.Save(); + } + + private void 多方签名MToolStripMenuItem_Click(object sender, EventArgs e) + { + using CreateMultiSigContractDialog dialog = new CreateMultiSigContractDialog(); + if (dialog.ShowDialog() != DialogResult.OK) return; + Contract contract = dialog.GetContract(); + if (contract == null) + { + MessageBox.Show(Strings.AddContractFailedMessage); + return; + } + WalletAccount account = Service.CurrentWallet.CreateAccount(contract, dialog.GetKey()); + if (Service.CurrentWallet is NEP6Wallet wallet) + wallet.Save(); + listView1.SelectedIndices.Clear(); + AddAccount(account, true); + } + + private void 自定义CToolStripMenuItem_Click(object sender, EventArgs e) + { + using ImportCustomContractDialog dialog = new ImportCustomContractDialog(); + if (dialog.ShowDialog() != DialogResult.OK) return; + Contract contract = dialog.GetContract(); + WalletAccount account = Service.CurrentWallet.CreateAccount(contract, dialog.GetKey()); + if (Service.CurrentWallet is NEP6Wallet wallet) + wallet.Save(); + listView1.SelectedIndices.Clear(); + AddAccount(account, true); + } + + private void 查看私钥VToolStripMenuItem_Click(object sender, EventArgs e) + { + WalletAccount account = (WalletAccount)listView1.SelectedItems[0].Tag; + using ViewPrivateKeyDialog dialog = new ViewPrivateKeyDialog(account); + dialog.ShowDialog(); + } + + private void viewContractToolStripMenuItem_Click(object sender, EventArgs e) + { + WalletAccount account = (WalletAccount)listView1.SelectedItems[0].Tag; + using ViewContractDialog dialog = new ViewContractDialog(account.Contract); + dialog.ShowDialog(); + } + + private void voteToolStripMenuItem_Click(object sender, EventArgs e) + { + try + { + WalletAccount account = (WalletAccount)listView1.SelectedItems[0].Tag; + byte[] script; + using (VotingDialog dialog = new VotingDialog(account.ScriptHash)) + { + if (dialog.ShowDialog() != DialogResult.OK) return; + script = dialog.GetScript(); + } + using (InvokeContractDialog dialog = new InvokeContractDialog(script)) + { + if (dialog.ShowDialog() != DialogResult.OK) return; + Helper.SignAndShowInformation(dialog.GetTransaction()); + } + } + catch { } + } + + private void 复制到剪贴板CToolStripMenuItem_Click(object sender, EventArgs e) + { + try + { + Clipboard.SetText(listView1.SelectedItems[0].Text); + } + catch (ExternalException) { } + } + + private void 删除DToolStripMenuItem_Click(object sender, EventArgs e) + { + if (MessageBox.Show(Strings.DeleteAddressConfirmationMessage, Strings.DeleteAddressConfirmationCaption, MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) != DialogResult.Yes) + return; + WalletAccount[] accounts = listView1.SelectedItems.OfType().Select(p => (WalletAccount)p.Tag).ToArray(); + foreach (WalletAccount account in accounts) + { + listView1.Items.RemoveByKey(account.Address); + Service.CurrentWallet.DeleteAccount(account.ScriptHash); + } + if (Service.CurrentWallet is NEP6Wallet wallet) + wallet.Save(); + check_nep5_balance = true; + } + + private void toolStripMenuItem1_Click(object sender, EventArgs e) + { + if (listView3.SelectedItems.Count == 0) return; + Clipboard.SetDataObject(listView3.SelectedItems[0].SubItems[1].Text); + } + + private void listView1_DoubleClick(object sender, EventArgs e) + { + if (listView1.SelectedIndices.Count == 0) return; + OpenBrowser($"https://neoscan.io/address/{listView1.SelectedItems[0].Text}"); + } + + private void listView2_DoubleClick(object sender, EventArgs e) + { + if (listView2.SelectedIndices.Count == 0) return; + OpenBrowser($"https://neoscan.io/asset/{listView2.SelectedItems[0].Name[2..]}"); + } + + private void listView3_DoubleClick(object sender, EventArgs e) + { + if (listView3.SelectedIndices.Count == 0) return; + OpenBrowser($"https://neoscan.io/transaction/{listView3.SelectedItems[0].Name[2..]}"); + } + + private void toolStripStatusLabel3_Click(object sender, EventArgs e) + { + using UpdateDialog dialog = new UpdateDialog((XDocument)toolStripStatusLabel3.Tag); + dialog.ShowDialog(); + } + } +} diff --git a/src/Neo.GUI/GUI/MainForm.es-ES.resx b/src/Neo.GUI/GUI/MainForm.es-ES.resx new file mode 100644 index 0000000000..468113ceba --- /dev/null +++ b/src/Neo.GUI/GUI/MainForm.es-ES.resx @@ -0,0 +1,437 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 210, 22 + + + &Nueva base de datos... + + + 210, 22 + + + &Abrir base de datos... + + + 207, 6 + + + 210, 22 + + + &Cambiar contraseña... + + + 207, 6 + + + 210, 22 + + + &Salir + + + 82, 21 + + + &Monedero + + + 141, 22 + + + &Transferir... + + + 138, 6 + + + 141, 22 + + + &Firma... + + + 89, 21 + + + &Transacción + + + 198, 22 + + + &Desplegar contrato... + + + 198, 22 + + + I&nvocar contrato... + + + 195, 6 + + + 198, 22 + + + &Votación... + + + 198, 22 + + + 195, 6 + + + 198, 22 + + + &Opciones... + + + A&vanzado + + + 259, 22 + + + &Obtener ayuda + + + 259, 22 + + + &Web oficial + + + 256, 6 + + + 259, 22 + + + &Herramienta de desarrollo + + + 259, 22 + + + 256, 6 + + + 259, 22 + + + &Acerca de NEO + + + 56, 21 + + + &Ayuda + + + Dirección + + + 275, 22 + + + Crear &nueva dirección + + + 266, 22 + + + Importar desde &WIF... + + + 263, 6 + + + 266, 22 + + + Importar dirección sólo lectur&a... + + + 275, 22 + + + &Importar + + + 178, 22 + + + &Múltiples firmas... + + + 175, 6 + + + 178, 22 + + + &Personalizado... + + + 275, 22 + + + Crear nueva &dirección de contrato + + + 272, 6 + + + 275, 22 + + + Ver clave &privada + + + 275, 22 + + + Ver c&ontrato + + + 275, 22 + + + &Votar... + + + 275, 22 + + + &Copiar al portapapeles + + + 275, 22 + + + &Eliminar... + + + 276, 186 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACJTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5MaXN0Vmlld0dyb3VwBAAAAAZIZWFkZXIPSGVhZGVyQWxpZ25tZW50A1Rh + ZwROYW1lAQQCAShTeXN0ZW0uV2luZG93cy5Gb3Jtcy5Ib3Jpem9udGFsQWxpZ25tZW50AgAAAAIAAAAG + AwAAABBDdWVudGEgZXN0w6FuZGFyBfz///8oU3lzdGVtLldpbmRvd3MuRm9ybXMuSG9yaXpvbnRhbEFs + aWdubWVudAEAAAAHdmFsdWVfXwAIAgAAAAAAAAAKBgUAAAAVc3RhbmRhcmRDb250cmFjdEdyb3VwCw== + + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACJTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5MaXN0Vmlld0dyb3VwBAAAAAZIZWFkZXIPSGVhZGVyQWxpZ25tZW50A1Rh + ZwROYW1lAQQCAShTeXN0ZW0uV2luZG93cy5Gb3Jtcy5Ib3Jpem9udGFsQWxpZ25tZW50AgAAAAIAAAAG + AwAAABZEaXJlY2Npw7NuIGRlIGNvbnRyYXRvBfz///8oU3lzdGVtLldpbmRvd3MuRm9ybXMuSG9yaXpv + bnRhbEFsaWdubWVudAEAAAAHdmFsdWVfXwAIAgAAAAAAAAAKBgUAAAAYbm9uc3RhbmRhcmRDb250cmFj + dEdyb3VwCw== + + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACJTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5MaXN0Vmlld0dyb3VwBAAAAAZIZWFkZXIPSGVhZGVyQWxpZ25tZW50A1Rh + ZwROYW1lAQQCAShTeXN0ZW0uV2luZG93cy5Gb3Jtcy5Ib3Jpem9udGFsQWxpZ25tZW50AgAAAAIAAAAG + AwAAABhEaXJlY2Npw7NuIHPDs2xvIGxlY3R1cmEF/P///yhTeXN0ZW0uV2luZG93cy5Gb3Jtcy5Ib3Jp + em9udGFsQWxpZ25tZW50AQAAAAd2YWx1ZV9fAAgCAAAAAAAAAAoGBQAAAA53YXRjaE9ubHlHcm91cAs= + + + + 58, 17 + + + Tamaño: + + + 74, 17 + + + Conectado: + + + 172, 17 + + + Esperando próximo bloque: + + + 152, 17 + + + Descargar nueva versión + + + Cuenta + + + Activo + + + Tipo + + + Saldo + + + Emisor + + + Activos + + + Fecha + + + ID de la transacción + + + Confirmación + + + Tipo + + + 147, 22 + + + &Copiar TXID + + + 148, 26 + + + Historial de transacciones + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/MainForm.resx b/src/Neo.GUI/GUI/MainForm.resx new file mode 100644 index 0000000000..21c7f5a2b2 --- /dev/null +++ b/src/Neo.GUI/GUI/MainForm.resx @@ -0,0 +1,1488 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + 216, 22 + + + &New Wallet Database... + + + 216, 22 + + + &Open Wallet Database... + + + 213, 6 + + + + False + + + 216, 22 + + + &Change Password... + + + 213, 6 + + + 216, 22 + + + E&xit + + + 56, 21 + + + &Wallet + + + 140, 22 + + + &Transfer... + + + 137, 6 + + + 140, 22 + + + &Signature... + + + False + + + 87, 21 + + + &Transaction + + + False + + + 179, 22 + + + &Deploy Contract... + + + False + + + 179, 22 + + + In&voke Contract... + + + 176, 6 + + + False + + + 179, 22 + + + &Election... + + + False + + + 179, 22 + + + &Sign Message... + + + 176, 6 + + + 179, 22 + + + &Options... + + + 77, 21 + + + &Advanced + + + 194, 22 + + + Check for &Help + + + 194, 22 + + + Official &Web + + + 191, 6 + + + + F12 + + + 194, 22 + + + Developer &Tool + + + 194, 22 + + + &Console + + + 191, 6 + + + 194, 22 + + + &About NEO + + + 47, 21 + + + &Help + + + 0, 0 + + + 7, 3, 0, 3 + + + 903, 27 + + + 0 + + + menuStrip1 + + + menuStrip1 + + + System.Windows.Forms.MenuStrip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 4 + + + Address + + + 300 + + + NEO + + + 120 + + + GAS + + + 120 + + + 348, 17 + + + False + + + 198, 22 + + + Create &New Add. + + + 248, 22 + + + Import from &WIF... + + + 245, 6 + + + 248, 22 + + + Import Watch-Only &Address... + + + False + + + 198, 22 + + + &Import + + + 174, 22 + + + &Multi-Signature... + + + 171, 6 + + + 174, 22 + + + &Custom... + + + False + + + 198, 22 + + + Create Contract &Add. + + + 195, 6 + + + False + + + 198, 22 + + + View &Private Key + + + False + + + 198, 22 + + + View C&ontract + + + False + + + 198, 22 + + + &Vote... + + + False + + + Ctrl+C + + + False + + + 198, 22 + + + &Copy to Clipboard + + + False + + + 198, 22 + + + &Delete... + + + 199, 186 + + + contextMenuStrip1 + + + System.Windows.Forms.ContextMenuStrip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Fill + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACJTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5MaXN0Vmlld0dyb3VwBAAAAAZIZWFkZXIPSGVhZGVyQWxpZ25tZW50A1Rh + ZwROYW1lAQQCAShTeXN0ZW0uV2luZG93cy5Gb3Jtcy5Ib3Jpem9udGFsQWxpZ25tZW50AgAAAAIAAAAG + AwAAABBTdGFuZGFyZCBBY2NvdW50Bfz///8oU3lzdGVtLldpbmRvd3MuRm9ybXMuSG9yaXpvbnRhbEFs + aWdubWVudAEAAAAHdmFsdWVfXwAIAgAAAAAAAAAKBgUAAAAVc3RhbmRhcmRDb250cmFjdEdyb3VwCw== + + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACJTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5MaXN0Vmlld0dyb3VwBAAAAAZIZWFkZXIPSGVhZGVyQWxpZ25tZW50A1Rh + ZwROYW1lAQQCAShTeXN0ZW0uV2luZG93cy5Gb3Jtcy5Ib3Jpem9udGFsQWxpZ25tZW50AgAAAAIAAAAG + AwAAABBDb250cmFjdCBBZGRyZXNzBfz///8oU3lzdGVtLldpbmRvd3MuRm9ybXMuSG9yaXpvbnRhbEFs + aWdubWVudAEAAAAHdmFsdWVfXwAIAgAAAAAAAAAKBgUAAAAYbm9uc3RhbmRhcmRDb250cmFjdEdyb3Vw + Cw== + + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACJTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5MaXN0Vmlld0dyb3VwBAAAAAZIZWFkZXIPSGVhZGVyQWxpZ25tZW50A1Rh + ZwROYW1lAQQCAShTeXN0ZW0uV2luZG93cy5Gb3Jtcy5Ib3Jpem9udGFsQWxpZ25tZW50AgAAAAIAAAAG + AwAAABJXYXRjaC1Pbmx5IEFkZHJlc3MF/P///yhTeXN0ZW0uV2luZG93cy5Gb3Jtcy5Ib3Jpem9udGFs + QWxpZ25tZW50AQAAAAd2YWx1ZV9fAAgCAAAAAAAAAAoGBQAAAA53YXRjaE9ubHlHcm91cAs= + + + + 3, 3 + + + 889, 521 + + + 1 + + + listView1 + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage1 + + + 0 + + + 137, 17 + + + 49, 17 + + + Height: + + + 27, 17 + + + 0/0 + + + 73, 17 + + + Connected: + + + 15, 17 + + + 0 + + + 100, 16 + + + 140, 17 + + + Waiting for next block: + + + 145, 17 + + + Download New Version + + + False + + + 0, 584 + + + 903, 22 + + + 2 + + + statusStrip1 + + + statusStrip1 + + + System.Windows.Forms.StatusStrip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + 258, 17 + + + 4, 26 + + + 3, 3, 3, 3 + + + 895, 527 + + + 0 + + + Account + + + tabPage1 + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControl1 + + + 0 + + + Asset + + + 160 + + + Type + + + 100 + + + Balance + + + 192 + + + Issuer + + + 398 + + + Fill + + + 3, 3 + + + 889, 521 + + + 2 + + + listView2 + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + + 0 + + + 4, 26 + + + 3, 3, 3, 3 + + + 895, 527 + + + 1 + + + Asset + + + tabPage2 + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControl1 + + + 1 + + + Time + + + 132 + + + Transaction ID + + + 482 + + + confirm + + + 78 + + + Transaction Type + + + 163 + + + 513, 17 + + + 138, 22 + + + &Copy TXID + + + 139, 26 + + + contextMenuStrip3 + + + System.Windows.Forms.ContextMenuStrip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Fill + + + 3, 3 + + + 889, 521 + + + 0 + + + listView3 + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage3 + + + 0 + + + 4, 26 + + + 3, 3, 3, 3 + + + 895, 527 + + + 2 + + + Transaction History + + + tabPage3 + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControl1 + + + 2 + + + Fill + + + 0, 27 + + + 903, 557 + + + 3 + + + tabControl1 + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + True + + + 7, 17 + + + 903, 606 + + + Microsoft YaHei UI, 9pt + + + + AAABAAEAQEAAAAEAIAAoQgAAFgAAACgAAABAAAAAgAAAAAEAIAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAA2G8OANdrdgDWaJMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAADadhYA2XOFANhv7wDXav8A1WarAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAANx+HgDbepMA2nb1ANhx/wDXbP8A1mf/ANVjqwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3oUoAN2BoQDcffsA23j/ANlz/wDYbv8A1mn/ANVk/wDU + YKsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgjTYA34mvAN6E/QDcf/8A23r/ANp1/wDY + cP8A12v/ANZm/wDUYf8A012rAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5JgAAOKUQgDhkL0A4Iz/AN+H/wDd + gv8A3H3/ANp4/wDZc/8A2G7/ANZo/wDVZP8A017/ANJaqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADmnwIA5JtQAOOXyQDi + k/8A4Y7/AN+J/wDehP8A3H//ANt6/wDadf8A2HD/ANdr/wDVZv8A1GH/ANNc/wDRV6sAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOinBADm + o14A5Z/XAOSa/wDjlf8A4ZD/AOCL/wDehv8A3YH/ANx8/wDad/8A2XL/ANdt/wDWaP8A1WP/ANNe/wDS + Wf8A0VWrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAOipRgDnpuEA5qH/AOWc/wDjl/8A4pL/AOCN/wDfiP8A3oP/ANx+/wDbef8A2XT/ANhv/wDX + av8A1WX/ANRg/wDSW/8A0Vb/ANBSqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADop34A56P/AOWe/wDkmf8A4pT/AOGP/wDgiv8A3oX/AN2A/wDb + e/8A2nb/ANlx/wDXbP8A1mf/ANRi/wDTXf8A0lj/ANBT/wDPT6sAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAL0NMAC9DXIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA56R+AOah/wDknP8A45f/AOKS/wDg + jf8A34j/AN2D/wDcff8A23n/ANlz/wDYb/8A1mn/ANVk/wDUX/8A0lr/ANFV/wDPUP8AzkyrAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvQ08AL0NtwC9Df8AvQ2/AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOaifgDl + nv8A5Jn/AOKU/wDhj/8A34r/AN6F/wDdgP8A23v/ANp2/wDYcf8A12z/ANZn/wDUYv8A013/ANFY/wDQ + U/8Az07/AM1JqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL0NAAC9DUoAvQ3FAL0N/wC9Df8AvQ3/AL0NvwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAADln34A5Jv/AOOW/wDhkf8A4Iz/AN+H/wDdgv8A3H3/ANp4/wDZc/8A2G7/ANZp/wDV + ZP8A01//ANJa/wDRVf8Az1D/AM5L/wDNR6sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvQ0EAL0NWAC9DdEAvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Db8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5Jx+AOOY/wDik/8A4Y7/AN+J/wDehP8A3H//ANt6/wDa + df8A2HD/ANdr/wDVZv8A1GH/ANNc/wDRV/8A0FL/AM5N/wDNSP8AzESrAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC9DQgAvQ1mAL0N3QC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ2/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOSZfgDjlf8A4ZD/AOCL/wDe + hv8A3YH/ANx8/wDad/8A2XL/ANdt/wDWaP8A1WP/ANNe/wDSWf8A0FT/AM9P/wDOSv8AzEX/AMtBqwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL0NDgC9 + DXQAvQ3nAL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0NvwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADj + ln4A4pL/AOCO/wDfiP8A3oT/ANx+/wDbef8A2XT/ANhv/wDXav8A1WX/ANRg/wDSW/8A0Vb/ANBR/wDO + TP8AzUf/AMxC/wDKPqsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAvQ0UAL0NgwC9De8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Db8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAA4pN+AOGQ/wDgi/8A3ob/AN2B/wDbfP8A2nf/ANly/wDXbf8A1mj/ANRj/wDT + Xv8A0ln/ANBU/wDPT/8AzUn/AMxF/wDLP/8AyjurAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAC9DR4AvQ2RAL0N9QC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ2/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOGRfgDgjf8A34j/AN2D/wDcfv8A23n/ANl0/wDY + b/8A12r/ANVl/wDUYP8A0lv/ANFW/wDQUf8Azkz/AM1H/wDLQv8Ayj3/AMk4qwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAL0NKAC9DZ8AvQ35AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0NvwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADhjn4A4Ir/AN6F/wDd + gP8A23v/ANp2/wDZcf8A12z/ANZn/wDUYv8A013/ANJY/wDQU/8Az07/AM1J/wDMRP8Ayz//AMk6/wDI + NqsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC9DdsAvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Db8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAA4It+AN+H/wDdgv8A3H3/ANt4/wDZc/8A2G7/ANZp/wDVZP8A1F//ANJa/wDRVf8Az1D/AM5L/wDN + Rv8Ay0H/AMo8/wDIN/8AxzOrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvQ3bAL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ2/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAN+IfgDehP8A3X//ANt6/wDadf8A2HD/ANdr/wDWZv8A1GH/ANNc/wDR + V/8A0FL/AM9N/wDNSP8AzEP/AMo+/wDJOf8AyDT/AMYwqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAL0N2wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0NvwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADehX4A3YL/ANx9/wDaeP8A2XP/ANhu/wDW + af8A1WT/ANNe/wDSWv8A0VT/AM9Q/wDOSv8AzEX/AMtA/wDKO/8AyDb/AMcx/wDGLasAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC9DdsAvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Db8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3YN+ANx//wDb + ev8A2nX/ANhw/wDXa/8A1Wb/ANRh/wDTXP8A0Vf/ANBS/wDOTf8AzUj/AMxD/wDKPv8AyTn/AMc0/wDG + L/8AxSqrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvQ3bAL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ2/AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAN2AfgDcfP8A2nf/ANly/wDXbf8A1mj/ANVj/wDTXv8A0ln/ANBU/wDPT/8Azkr/AMxF/wDL + QP8AyTv/AMg2/wDHMf8AxSz/AMQoqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL0N2wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0NvwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADcfX4A23n/ANl0/wDYb/8A12r/ANVl/wDUYP8A0lv/ANFW/wDQ + Uf8Azkz/AM1H/wDLQv8Ayj3/AMk4/wDHM/8Axi7/AMQp/wDDJasAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAC9DdsAvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Db8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA23p+ANp2/wDZcf8A12z/ANZn/wDU + Yv8A013/ANJY/wDQU/8Az07/AM1J/wDMRP8Ayz//AMk6/wDINf8AxjD/AMUr/wDEJv8AwiKrAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvQ3bAL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ2/AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANp3fgDZ + c/8A2G//ANZp/wDVZf8A1F//ANJa/wDRVf8Az1D/AM5L/wDNRv8Ay0H/AMo8/wDIN/8AxzL/AMYt/wDE + KP8AwyP/AMIfqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL0N2wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0NvwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAADZdH4A2HH/ANds/wDWZ/8A1GL/ANNd/wDRWP8A0FP/AM9O/wDNSf8AzET/AMo//wDJ + Ov8AyDX/AMYw/wDFKv8Awyb/AMIg/wDBHKsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC9 + DdsAvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Db8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2XJ+ANhu/wDWaf8A1WT/ANNf/wDSWv8A0VX/AM9Q/wDO + S/8AzEb/AMtB/wDKPP8AyDf/AMcy/wDFLf8AxCj/AMMj/wDBHv8AwBmrAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAvQ3bAL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ2/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANhvfgDXa/8A1Wb/ANRh/wDT + XP8A0Vf/ANBS/wDOTf8AzUj/AMxD/wDKPv8AyTn/AMg0/wDGL/8AxSr/AMMl/wDCIP8AwRv/AL8XqwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL0N2wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0NvwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADX + bH4A1mj/ANVj/wDTXv8A0ln/ANBU/wDPT/8Azkr/AMxF/wDLQP8AyTv/AMg2/wDHMf8AxSz/AMQn/wDD + Iv8AwR3/AMAY/wC/FKsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC9DdsAvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Db8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAA1ml+ANVl/wDUYP8A0lv/ANFW/wDQUf8Azkz/AM1H/wDMQv8Ayj3/AMk4/wDH + M/8Axi7/AMUp/wDDJP8Awh//AMAa/wC/Ff8AvhGrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAvQ3bAL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ2/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANZmfgDVY/8A017/ANJZ/wDQVP8Az0//AM5K/wDM + Rf8Ayz//AMk7/wDINf8AxzH/AMUr/wDEJv8AwiH/AMEc/wDAF/8AvhL/AL0OqwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAL0N2wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0NvwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADVZH4A1GD/ANJb/wDR + Vv8A0FH/AM5M/wDNR/8Ay0L/AMo9/wDJOP8AxzP/AMYu/wDEKf8AwyT/AMIf/wDAGv8AvxX/AL0Q/wC9 + DasAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC9DdsAvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9DL8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAA1GF+ANNd/wDSWP8A0FP/AM9O/wDNSf8AzET/AMs//wDJOv8AyDX/AMYw/wDFK/8AxCb/AMIh/wDB + HP8Avxf/AL4S/wC9Df8AvQ2rAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvQ3bAL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + DP8AvQy/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAANNefgDSWv8A0VX/AM9Q/wDOS/8AzUb/AMtB/wDKPP8AyDf/AMcy/wDG + Lf8AxCj/AMMj/wDBHv8AwBn/AL8U/wC9D/8AvQ3VAL0NTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAL0N2wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQz/ALwMvwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADSW34A0Vf/ANBS/wDPTf8AzUj/AMxD/wDK + Pv8AyTn/AMg0/wDGL/8AxSr/AMMl/wDCIP8AwRv/AL8W/wC+EskAvQ5QAL0NMgC9DakAvQ3RAL0NdgC9 + DRwAAAAAAAAAAAAAAAAAAAAAAAAAAAC9DdsAvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQz/ALwM/wC8C78AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0lh+ANFU/wDP + UP8Azkr/AMxG/wDLQP8Ayjv/AMg2/wDHMf8AxSz/AMQn/wDDIv8AwR3/AMAZuwC/FUIAvQ0+AL0NtwC9 + Df8AvQ3/AL0N/wC9Df8AvQ39AL0NvQC9DWIAvQ0OAAAAAAAAAAAAvQ3bAL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQz/AL0M/wC8C/8AvAu/AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAANFVfgDQUv8Azk3/AM1I/wDMQ/8Ayj7/AMk5/wDHNP8Axi//AMUq/wDDJf0AwiCtAMEcNgC/ + FEwAvhDFAL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N9wC9DakAvQ1OAL0NJgC9 + DXwAvQ3XAL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0M/wC8 + DP8AvAv/ALwLvwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQU34Az0//AM5K/wDMRf8Ay0D/AMk7/wDINv8AxzH/AMUs+QDE + J58AwyMsAMEbWAC/F9EAvhP/AL0O/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9DesAvQ2VAL0NOAC9DTQAvQ2RAL0N6QC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0M/wC9DP8AvAv/ALwL/wC8C78AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAz1B+AM5M/wDNR/8Ay0L/AMo9/wDJ + OP8AxzP1AMYvkwDFKiYAwyNmAMIf3QDAGv8AvxX/AL0Q/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3bAL0NgQC9DSgAvQ1IAL0NpQC9 + DfUAvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9DP8AvAz/ALwL/wC8C/8AvAq/AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM5NfgDN + Sf8AzET/AMs//wDJOu8AyDaFAMcxIgDFKnQAxCbnAMIh/wDBHP8Avxf/AL4S/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0NyQC9DWwAvQ0gAL0NXAC9DbkAvQ37AL0N/wC9DP8AvAz/ALwL/wC8C/8AvAr/ALwKvwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAADOSn4AzUb/AMtC5wDKPXYAyDciAMcxgwDGLe8AxCj/AMMj/wDBHv8AwBn/AL8U/wC9 + D/8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N+wC9DbMAvQ1YAL0NIgC9DXAAvQzNALwM/wC8 + C/8AvAv/ALwK/wC7Cr8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzUhgAMxFaADKPSYAyTmRAMg09QDGMP8AxSv/AMMm/wDC + IP8AwRz/AL8W/wC+Ev8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + DfEAvQyfALwMRAC8CywAvAuHALwK4QC8Cv8Auwm/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADLQEQAyjztAMg3/wDH + Mv8Axi3/AMQo/wDDI/8AwR7/AMAZ/wC/FP8AvQ//AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0M/wC8DP8AvAvlALwLiwC8CjAAuwo+ALsJagAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAMk5CgDINFoAxi+3AMUq+wDDJf8AwiD/AMEb/wC/Fv8AvhH/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0M/wC8DP8AvAv/ALwL/wC8Cv8AvAr/ALsJxQC7 + CRoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADEJxYAwyJuAMEdywDAGP8AvhP/AL0O/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0M/wC9DP8AvAv/ALwL/wC8 + Cv8AvArpALsKeAC7CRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAvxUoAL4RgwC9Dd8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + DP8AvAz/ALwL/wC8C98AvApqALwKCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvQ0AAL0NPAC9DZcAvQ3tAL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9DP8AvAz/ALwL1QC8C1wAvAsEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL0NBgC9 + DVAAvQ2rAL0N9wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQzJALwMUAC8DAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC9DRAAvQ1kAL0NwQC9Df0AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9 + Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9DbsAvQ1CAL0MAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvQ0eAL0NeAC9 + DdUAvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ3/AL0N/wC9Df8AvQ39AL0NrQC9DTQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAL0NMAC9DY0AvQ3lAL0N/wC9Df8AvQ3/AL0N/wC9DfkAvQ2fAL0NKAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL0NAgC9DUYAvQ2hAL0N6QC9 + DZEAvQ0eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA/////////////////////////////////////////////////////////+//////////D/// + //////wP////////8A/////////AD////////wAP///////8AA////////AAD///////wAAP///////A + AA///////8AAD///8f//wAAP///B///AAA///wH//8AAD//8Af//wAAP//AB///AAA//gAH//8AAD/4A + Af//wAAP+AAB///AAA/wAAH//8AAD/AAAf//wAAP8AAB///AAA/wAAH//8AAD/AAAf//wAAP8AAB///A + AA/wAAH//8AAD/AAAf//wAAP8AAB///AAA/wAAH//8AAD/AAAf//wAAP8AAB///AAA/wAAH//8AAD/AA + Af//wAAP8AAB///AAA/wAAH//8AAD/AAAf//wAAf8AAB///AAGfwAAH//8ABgPAAAf//wAYAHAAB///A + GAADAAH//8BgAABgAf//wYAAABwB///MAAAAA4H///AAAAAAYf//4AAAAAAP///4AAAAAAP///8AAAAA + D////8AAAAA/////+AAAAP//////AAAD///////gAA////////wAP////////wD/////////4/////// + //////////////////////////////////////////////////8= + + + + 3, 4, 3, 4 + + + CenterScreen + + + neo-gui + + + 钱包WToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 创建钱包数据库NToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 打开钱包数据库OToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripSeparator1 + + + System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 修改密码CToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripSeparator2 + + + System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 退出XToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 交易TToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 转账TToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripSeparator5 + + + System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 签名SToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 高级AToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + deployContractToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + invokeContractToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripSeparator11 + + + System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 选举EToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + signDataToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripSeparator9 + + + System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + optionsToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 帮助HToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 查看帮助VToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 官网WToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripSeparator3 + + + System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 开发人员工具TToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + consoleToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripSeparator4 + + + System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 关于AntSharesToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + columnHeader1 + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + columnHeader4 + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + columnHeader11 + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 创建新地址NToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 导入私钥IToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + importWIFToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripSeparator10 + + + System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + importWatchOnlyAddressToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 创建智能合约SToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 多方签名MToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripSeparator12 + + + System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 自定义CToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripSeparator6 + + + System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 查看私钥VToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + viewContractToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + voteToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 复制到剪贴板CToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 删除DToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripStatusLabel1 + + + System.Windows.Forms.ToolStripStatusLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + lbl_height + + + System.Windows.Forms.ToolStripStatusLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripStatusLabel4 + + + System.Windows.Forms.ToolStripStatusLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + lbl_count_node + + + System.Windows.Forms.ToolStripStatusLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripProgressBar1 + + + System.Windows.Forms.ToolStripProgressBar, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripStatusLabel2 + + + System.Windows.Forms.ToolStripStatusLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripStatusLabel3 + + + System.Windows.Forms.ToolStripStatusLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + timer1 + + + System.Windows.Forms.Timer, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + columnHeader2 + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + columnHeader6 + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + columnHeader3 + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + columnHeader5 + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + columnHeader7 + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + columnHeader8 + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + columnHeader9 + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + columnHeader10 + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripMenuItem1 + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + MainForm + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/src/Neo.GUI/GUI/MainForm.zh-Hans.resx b/src/Neo.GUI/GUI/MainForm.zh-Hans.resx new file mode 100644 index 0000000000..086c4e916a --- /dev/null +++ b/src/Neo.GUI/GUI/MainForm.zh-Hans.resx @@ -0,0 +1,445 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 187, 22 + + + 创建钱包数据库(&N)... + + + 187, 22 + + + 打开钱包数据库(&O)... + + + 184, 6 + + + 187, 22 + + + 修改密码(&C)... + + + 184, 6 + + + 187, 22 + + + 退出(&X) + + + 64, 21 + + + 钱包(&W) + + + 124, 22 + + + 转账(&T)... + + + 121, 6 + + + 124, 22 + + + 签名(&S)... + + + 59, 21 + + + 交易(&T) + + + 150, 22 + + + 部署合约(&D)... + + + 150, 22 + + + 调用合约(&V)... + + + 147, 6 + + + 150, 22 + + + 选举(&E)... + + + 150, 22 + + + 消息签名(&S)... + + + 147, 6 + + + 150, 22 + + + 选项(&O)... + + + 60, 21 + + + 高级(&A) + + + 191, 22 + + + 查看帮助(&H) + + + 191, 22 + + + 官网(&W) + + + 188, 6 + + + 191, 22 + + + 开发人员工具(&T) + + + 191, 22 + + + 控制台(&C) + + + 188, 6 + + + 191, 22 + + + 关于&NEO + + + 61, 21 + + + 帮助(&H) + + + 地址 + + + 164, 22 + + + 创建新地址(&N) + + + 173, 22 + + + 导入&WIF... + + + 170, 6 + + + 173, 22 + + + 导入监视地址(&A)... + + + 164, 22 + + + 导入(&I) + + + 153, 22 + + + 多方签名(&M)... + + + 150, 6 + + + 153, 22 + + + 自定义(&C)... + + + 164, 22 + + + 创建合约地址(&A) + + + 161, 6 + + + 164, 22 + + + 查看私钥(&P) + + + 164, 22 + + + 查看合约(&O) + + + 164, 22 + + + 投票(&V)... + + + 164, 22 + + + 复制到剪贴板(&C) + + + 164, 22 + + + 删除(&D)... + + + 165, 186 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACJTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5MaXN0Vmlld0dyb3VwBAAAAAZIZWFkZXIPSGVhZGVyQWxpZ25tZW50A1Rh + ZwROYW1lAQQCAShTeXN0ZW0uV2luZG93cy5Gb3Jtcy5Ib3Jpem9udGFsQWxpZ25tZW50AgAAAAIAAAAG + AwAAAAzmoIflh4botKbmiLcF/P///yhTeXN0ZW0uV2luZG93cy5Gb3Jtcy5Ib3Jpem9udGFsQWxpZ25t + ZW50AQAAAAd2YWx1ZV9fAAgCAAAAAAAAAAoGBQAAABVzdGFuZGFyZENvbnRyYWN0R3JvdXAL + + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACJTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5MaXN0Vmlld0dyb3VwBAAAAAZIZWFkZXIPSGVhZGVyQWxpZ25tZW50A1Rh + ZwROYW1lAQQCAShTeXN0ZW0uV2luZG93cy5Gb3Jtcy5Ib3Jpem9udGFsQWxpZ25tZW50AgAAAAIAAAAG + AwAAAAzlkIjnuqblnLDlnYAF/P///yhTeXN0ZW0uV2luZG93cy5Gb3Jtcy5Ib3Jpem9udGFsQWxpZ25t + ZW50AQAAAAd2YWx1ZV9fAAgCAAAAAAAAAAoGBQAAABhub25zdGFuZGFyZENvbnRyYWN0R3JvdXAL + + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACJTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5MaXN0Vmlld0dyb3VwBAAAAAZIZWFkZXIPSGVhZGVyQWxpZ25tZW50A1Rh + ZwROYW1lAQQCAShTeXN0ZW0uV2luZG93cy5Gb3Jtcy5Ib3Jpem9udGFsQWxpZ25tZW50AgAAAAIAAAAG + AwAAAAznm5Hop4blnLDlnYAF/P///yhTeXN0ZW0uV2luZG93cy5Gb3Jtcy5Ib3Jpem9udGFsQWxpZ25t + ZW50AQAAAAd2YWx1ZV9fAAgCAAAAAAAAAAoGBQAAAA53YXRjaE9ubHlHcm91cAs= + + + + 35, 17 + + + 高度: + + + 47, 17 + + + 连接数: + + + 95, 17 + + + 等待下一个区块: + + + 68, 17 + + + 发现新版本 + + + 账户 + + + 资产 + + + 类型 + + + 余额 + + + 发行者 + + + 资产 + + + 时间 + + + 交易编号 + + + 确认 + + + 交易类型 + + + 137, 22 + + + 复制交易ID + + + 138, 26 + + + 交易记录 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/OpenWalletDialog.cs b/src/Neo.GUI/GUI/OpenWalletDialog.cs new file mode 100644 index 0000000000..a43352a9ed --- /dev/null +++ b/src/Neo.GUI/GUI/OpenWalletDialog.cs @@ -0,0 +1,66 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// OpenWalletDialog.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; +using System.Windows.Forms; + +namespace Neo.GUI +{ + internal partial class OpenWalletDialog : Form + { + public OpenWalletDialog() + { + InitializeComponent(); + } + + public string Password + { + get + { + return textBox2.Text; + } + set + { + textBox2.Text = value; + } + } + + public string WalletPath + { + get + { + return textBox1.Text; + } + set + { + textBox1.Text = value; + } + } + + private void textBox_TextChanged(object sender, EventArgs e) + { + if (textBox1.TextLength == 0 || textBox2.TextLength == 0) + { + button2.Enabled = false; + return; + } + button2.Enabled = true; + } + + private void button1_Click(object sender, EventArgs e) + { + if (openFileDialog1.ShowDialog() == DialogResult.OK) + { + textBox1.Text = openFileDialog1.FileName; + } + } + } +} diff --git a/src/Neo.GUI/GUI/OpenWalletDialog.designer.cs b/src/Neo.GUI/GUI/OpenWalletDialog.designer.cs new file mode 100644 index 0000000000..9db0ac01b8 --- /dev/null +++ b/src/Neo.GUI/GUI/OpenWalletDialog.designer.cs @@ -0,0 +1,125 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class OpenWalletDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(OpenWalletDialog)); + this.button2 = new System.Windows.Forms.Button(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.button1 = new System.Windows.Forms.Button(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog(); + this.SuspendLayout(); + // + // button2 + // + resources.ApplyResources(this.button2, "button2"); + this.button2.DialogResult = System.Windows.Forms.DialogResult.OK; + this.button2.Name = "button2"; + this.button2.UseVisualStyleBackColor = true; + // + // textBox2 + // + resources.ApplyResources(this.textBox2, "textBox2"); + this.textBox2.Name = "textBox2"; + this.textBox2.UseSystemPasswordChar = true; + this.textBox2.TextChanged += new System.EventHandler(this.textBox_TextChanged); + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // textBox1 + // + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.Name = "textBox1"; + this.textBox1.ReadOnly = true; + this.textBox1.TextChanged += new System.EventHandler(this.textBox_TextChanged); + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // openFileDialog1 + // + this.openFileDialog1.DefaultExt = "json"; + resources.ApplyResources(this.openFileDialog1, "openFileDialog1"); + // + // OpenWalletDialog + // + this.AcceptButton = this.button2; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.button2); + this.Controls.Add(this.textBox2); + this.Controls.Add(this.label2); + this.Controls.Add(this.button1); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "OpenWalletDialog"; + this.ShowInTaskbar = false; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button button2; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.OpenFileDialog openFileDialog1; + } +} diff --git a/src/Neo.GUI/GUI/OpenWalletDialog.es-ES.resx b/src/Neo.GUI/GUI/OpenWalletDialog.es-ES.resx new file mode 100644 index 0000000000..bf023cf9a0 --- /dev/null +++ b/src/Neo.GUI/GUI/OpenWalletDialog.es-ES.resx @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 142, 41 + + + 65, 44 + + + 71, 16 + + + Contraseña: + + + Buscar... + + + 142, 12 + + + 204, 23 + + + 124, 16 + + + Fichero de nomedero: + + + Abrir monedero + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/OpenWalletDialog.resx b/src/Neo.GUI/GUI/OpenWalletDialog.resx new file mode 100644 index 0000000000..8b5a8e2e01 --- /dev/null +++ b/src/Neo.GUI/GUI/OpenWalletDialog.resx @@ -0,0 +1,315 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 8 + + + 15 + + + Password: + + + 5 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 7, 16 + + + + Top, Left, Right + + + True + + + OK + + + 16, 44 + + + 352, 68 + + + $this + + + Wallet File: + + + button2 + + + OpenWalletDialog + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Top, Right + + + $this + + + $this + + + 12, 15 + + + textBox1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 263, 23 + + + 439, 103 + + + System.Windows.Forms.OpenFileDialog, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Bottom, Right + + + CenterScreen + + + 75, 23 + + + openFileDialog1 + + + textBox2 + + + label1 + + + label2 + + + 75, 23 + + + 9 + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Browse + + + False + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + NEP-6 Wallet|*.json|SQLite Wallet|*.db3 + + + 83, 12 + + + 61, 16 + + + 3 + + + 150, 23 + + + True + + + 65, 16 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 1 + + + 11 + + + 83, 41 + + + 4 + + + 10 + + + button1 + + + 0 + + + $this + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 352, 12 + + + 微软雅黑, 9pt + + + Open Wallet + + + 2 + + + 12 + + + $this + + + True + + + 17, 17 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/OpenWalletDialog.zh-Hans.resx b/src/Neo.GUI/GUI/OpenWalletDialog.zh-Hans.resx new file mode 100644 index 0000000000..5c4bf63eda --- /dev/null +++ b/src/Neo.GUI/GUI/OpenWalletDialog.zh-Hans.resx @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 确定 + + + 101, 41 + + + 60, 44 + + + 35, 16 + + + 密码: + + + 浏览 + + + 101, 12 + + + 245, 23 + + + 83, 16 + + + 钱包文件位置: + + + NEP-6钱包文件|*.json|SQLite钱包文件|*.db3 + + + 打开钱包 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ParametersEditor.Designer.cs b/src/Neo.GUI/GUI/ParametersEditor.Designer.cs new file mode 100644 index 0000000000..5d6d92d268 --- /dev/null +++ b/src/Neo.GUI/GUI/ParametersEditor.Designer.cs @@ -0,0 +1,200 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class ParametersEditor + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ParametersEditor)); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.listView1 = new System.Windows.Forms.ListView(); + this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.panel1 = new System.Windows.Forms.Panel(); + this.button4 = new System.Windows.Forms.Button(); + this.button3 = new System.Windows.Forms.Button(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.button2 = new System.Windows.Forms.Button(); + this.button1 = new System.Windows.Forms.Button(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.groupBox1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.groupBox3.SuspendLayout(); + this.SuspendLayout(); + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.listView1); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // listView1 + // + resources.ApplyResources(this.listView1, "listView1"); + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader1, + this.columnHeader2, + this.columnHeader3}); + this.listView1.FullRowSelect = true; + this.listView1.GridLines = true; + this.listView1.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.ShowGroups = false; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.listView1_SelectedIndexChanged); + // + // columnHeader1 + // + resources.ApplyResources(this.columnHeader1, "columnHeader1"); + // + // columnHeader2 + // + resources.ApplyResources(this.columnHeader2, "columnHeader2"); + // + // columnHeader3 + // + resources.ApplyResources(this.columnHeader3, "columnHeader3"); + // + // panel1 + // + resources.ApplyResources(this.panel1, "panel1"); + this.panel1.Controls.Add(this.button4); + this.panel1.Controls.Add(this.button3); + this.panel1.Name = "panel1"; + // + // button4 + // + resources.ApplyResources(this.button4, "button4"); + this.button4.Name = "button4"; + this.button4.UseVisualStyleBackColor = true; + this.button4.Click += new System.EventHandler(this.button4_Click); + // + // button3 + // + resources.ApplyResources(this.button3, "button3"); + this.button3.Name = "button3"; + this.button3.UseVisualStyleBackColor = true; + this.button3.Click += new System.EventHandler(this.button3_Click); + // + // groupBox2 + // + resources.ApplyResources(this.groupBox2, "groupBox2"); + this.groupBox2.Controls.Add(this.textBox1); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.TabStop = false; + // + // textBox1 + // + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.Name = "textBox1"; + this.textBox1.ReadOnly = true; + // + // groupBox3 + // + resources.ApplyResources(this.groupBox3, "groupBox3"); + this.groupBox3.Controls.Add(this.panel1); + this.groupBox3.Controls.Add(this.button2); + this.groupBox3.Controls.Add(this.button1); + this.groupBox3.Controls.Add(this.textBox2); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.TabStop = false; + // + // button2 + // + resources.ApplyResources(this.button2, "button2"); + this.button2.Name = "button2"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // textBox2 + // + resources.ApplyResources(this.textBox2, "textBox2"); + this.textBox2.Name = "textBox2"; + this.textBox2.TextChanged += new System.EventHandler(this.textBox2_TextChanged); + // + // ParametersEditor + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.groupBox3); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.groupBox1); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ParametersEditor"; + this.ShowInTaskbar = false; + this.groupBox1.ResumeLayout(false); + this.panel1.ResumeLayout(false); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.groupBox3.ResumeLayout(false); + this.groupBox3.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.ListView listView1; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.GroupBox groupBox3; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.ColumnHeader columnHeader1; + private System.Windows.Forms.ColumnHeader columnHeader2; + private System.Windows.Forms.ColumnHeader columnHeader3; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.Button button3; + private System.Windows.Forms.Button button4; + private System.Windows.Forms.Panel panel1; + } +} diff --git a/src/Neo.GUI/GUI/ParametersEditor.cs b/src/Neo.GUI/GUI/ParametersEditor.cs new file mode 100644 index 0000000000..19eac82c31 --- /dev/null +++ b/src/Neo.GUI/GUI/ParametersEditor.cs @@ -0,0 +1,198 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ParametersEditor.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.Cryptography.ECC; +using Neo.SmartContract; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Numerics; +using System.Windows.Forms; + +namespace Neo.GUI +{ + internal partial class ParametersEditor : Form + { + private readonly IList parameters; + + public ParametersEditor(IList parameters) + { + InitializeComponent(); + this.parameters = parameters; + listView1.Items.AddRange(parameters.Select((p, i) => new ListViewItem(new[] + { + new ListViewItem.ListViewSubItem + { + Name = "index", + Text = $"[{i}]" + }, + new ListViewItem.ListViewSubItem + { + Name = "type", + Text = p.Type.ToString() + }, + new ListViewItem.ListViewSubItem + { + Name = "value", + Text = p.ToString() + } + }, -1) + { + Tag = p + }).ToArray()); + panel1.Enabled = !parameters.IsReadOnly; + } + + private void listView1_SelectedIndexChanged(object sender, EventArgs e) + { + if (listView1.SelectedIndices.Count > 0) + { + textBox1.Text = listView1.SelectedItems[0].SubItems["value"].Text; + textBox2.Enabled = ((ContractParameter)listView1.SelectedItems[0].Tag).Type != ContractParameterType.Array; + button2.Enabled = !textBox2.Enabled; + button4.Enabled = true; + } + else + { + textBox1.Clear(); + textBox2.Enabled = true; + button2.Enabled = false; + button4.Enabled = false; + } + textBox2.Clear(); + } + + private void textBox2_TextChanged(object sender, EventArgs e) + { + button1.Enabled = listView1.SelectedIndices.Count > 0 && textBox2.TextLength > 0; + button3.Enabled = textBox2.TextLength > 0; + } + + private void button1_Click(object sender, EventArgs e) + { + if (listView1.SelectedIndices.Count == 0) return; + ContractParameter parameter = (ContractParameter)listView1.SelectedItems[0].Tag; + try + { + parameter.SetValue(textBox2.Text); + listView1.SelectedItems[0].SubItems["value"].Text = parameter.ToString(); + textBox1.Text = listView1.SelectedItems[0].SubItems["value"].Text; + textBox2.Clear(); + } + catch (Exception err) + { + MessageBox.Show(err.Message); + } + } + + private void button2_Click(object sender, EventArgs e) + { + if (listView1.SelectedIndices.Count == 0) return; + ContractParameter parameter = (ContractParameter)listView1.SelectedItems[0].Tag; + using ParametersEditor dialog = new ParametersEditor((IList)parameter.Value); + dialog.ShowDialog(); + listView1.SelectedItems[0].SubItems["value"].Text = parameter.ToString(); + textBox1.Text = listView1.SelectedItems[0].SubItems["value"].Text; + } + + private void button3_Click(object sender, EventArgs e) + { + string s = textBox2.Text; + ContractParameter parameter = new ContractParameter(); + if (string.Equals(s, "true", StringComparison.OrdinalIgnoreCase)) + { + parameter.Type = ContractParameterType.Boolean; + parameter.Value = true; + } + else if (string.Equals(s, "false", StringComparison.OrdinalIgnoreCase)) + { + parameter.Type = ContractParameterType.Boolean; + parameter.Value = false; + } + else if (long.TryParse(s, out long num)) + { + parameter.Type = ContractParameterType.Integer; + parameter.Value = num; + } + else if (s.StartsWith("0x")) + { + if (UInt160.TryParse(s, out UInt160 i160)) + { + parameter.Type = ContractParameterType.Hash160; + parameter.Value = i160; + } + else if (UInt256.TryParse(s, out UInt256 i256)) + { + parameter.Type = ContractParameterType.Hash256; + parameter.Value = i256; + } + else if (BigInteger.TryParse(s.Substring(2), NumberStyles.AllowHexSpecifier, null, out BigInteger bi)) + { + parameter.Type = ContractParameterType.Integer; + parameter.Value = bi; + } + else + { + parameter.Type = ContractParameterType.String; + parameter.Value = s; + } + } + else if (ECPoint.TryParse(s, ECCurve.Secp256r1, out ECPoint point)) + { + parameter.Type = ContractParameterType.PublicKey; + parameter.Value = point; + } + else + { + try + { + parameter.Value = s.HexToBytes(); + parameter.Type = ContractParameterType.ByteArray; + } + catch (FormatException) + { + parameter.Type = ContractParameterType.String; + parameter.Value = s; + } + } + parameters.Add(parameter); + listView1.Items.Add(new ListViewItem(new[] + { + new ListViewItem.ListViewSubItem + { + Name = "index", + Text = $"[{listView1.Items.Count}]" + }, + new ListViewItem.ListViewSubItem + { + Name = "type", + Text = parameter.Type.ToString() + }, + new ListViewItem.ListViewSubItem + { + Name = "value", + Text = parameter.ToString() + } + }, -1) + { + Tag = parameter + }); + } + + private void button4_Click(object sender, EventArgs e) + { + int index = listView1.SelectedIndices[0]; + parameters.RemoveAt(index); + listView1.Items.RemoveAt(index); + } + } +} diff --git a/src/Neo.GUI/GUI/ParametersEditor.es-ES.resx b/src/Neo.GUI/GUI/ParametersEditor.es-ES.resx new file mode 100644 index 0000000000..21e7fad664 --- /dev/null +++ b/src/Neo.GUI/GUI/ParametersEditor.es-ES.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Tipo + + + Valor + + + Lista de parámetros + + + Valor anterior + + + Actualizar + + + Nuevo valor + + + Definir parámetros + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ParametersEditor.resx b/src/Neo.GUI/GUI/ParametersEditor.resx new file mode 100644 index 0000000000..a2f2d31c9e --- /dev/null +++ b/src/Neo.GUI/GUI/ParametersEditor.resx @@ -0,0 +1,489 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + False + + + + + + + 661, 485 + + + ParametersEditor + + + False + + + False + + + + + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox3 + + + panel1 + + + Value + + + 2 + + + 0 + + + Update + + + 0 + + + + Top, Bottom, Left, Right + + + 1 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 233, 126 + + + 3 + + + groupBox3 + + + True + + + 3, 22 + + + 23, 23 + + + 7, 17 + + + 1 + + + 74, 23 + + + 3, 4, 3, 4 + + + panel1 + + + Bottom, Right + + + 0 + + + Type + + + CenterScreen + + + Top, Bottom, Left, Right + + + False + + + textBox1 + + + Parameter List + + + 75, 23 + + + groupBox3 + + + 75, 23 + + + 380, 436 + + + 233, 250 + + + 0 + + + 3, 19 + + + 410, 290 + + + 0 + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + panel1 + + + 29, 0 + + + 1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 80, 154 + + + 161, 154 + + + 3 + + + Edit Array + + + Bottom, Left, Right + + + Old Value + + + 410, 12 + + + groupBox2 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 微软雅黑, 9pt + + + groupBox3 + + + 2 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + button1 + + + listView1 + + + columnHeader3 + + + - + + + 12, 12 + + + columnHeader1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 392, 461 + + + 0 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Fill + + + 0 + + + 3, 154 + + + True + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Top, Bottom, Left, Right + + + System.Windows.Forms.Panel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Bottom, Left, Right + + + $this + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + NoControl + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 0 + + + 0, 0 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 239, 272 + + + 200 + + + columnHeader2 + + + 50 + + + groupBox2 + + + button2 + + + groupBox3 + + + 2 + + + button4 + + + 1 + + + 23, 23 + + + 1 + + + 2 + + + Bottom, Right + + + Set Parameters + + + groupBox1 + + + groupBox1 + + + 0, 0, 0, 0 + + + New Value + + + 0 + + + 100 + + + $this + + + textBox2 + + + 1 + + + $this + + + 2 + + + Top, Bottom, Left + + + button3 + + + 6, 19 + + + 239, 183 + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ParametersEditor.zh-Hans.resx b/src/Neo.GUI/GUI/ParametersEditor.zh-Hans.resx new file mode 100644 index 0000000000..8db893dbac --- /dev/null +++ b/src/Neo.GUI/GUI/ParametersEditor.zh-Hans.resx @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 参数列表 + + + 类型 + + + + + + 当前值 + + + 新值 + + + 编辑数组 + + + 更新 + + + 设置参数 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/PayToDialog.Designer.cs b/src/Neo.GUI/GUI/PayToDialog.Designer.cs new file mode 100644 index 0000000000..100d32f50d --- /dev/null +++ b/src/Neo.GUI/GUI/PayToDialog.Designer.cs @@ -0,0 +1,142 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class PayToDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(PayToDialog)); + this.label1 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.button1 = new System.Windows.Forms.Button(); + this.label3 = new System.Windows.Forms.Label(); + this.comboBox1 = new System.Windows.Forms.ComboBox(); + this.label4 = new System.Windows.Forms.Label(); + this.textBox3 = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // textBox1 + // + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.Name = "textBox1"; + this.textBox1.TextChanged += new System.EventHandler(this.textBox_TextChanged); + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // textBox2 + // + resources.ApplyResources(this.textBox2, "textBox2"); + this.textBox2.Name = "textBox2"; + this.textBox2.TextChanged += new System.EventHandler(this.textBox_TextChanged); + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.DialogResult = System.Windows.Forms.DialogResult.OK; + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + // + // label3 + // + resources.ApplyResources(this.label3, "label3"); + this.label3.Name = "label3"; + // + // comboBox1 + // + resources.ApplyResources(this.comboBox1, "comboBox1"); + this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBox1.FormattingEnabled = true; + this.comboBox1.Name = "comboBox1"; + this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged); + // + // label4 + // + resources.ApplyResources(this.label4, "label4"); + this.label4.Name = "label4"; + // + // textBox3 + // + resources.ApplyResources(this.textBox3, "textBox3"); + this.textBox3.Name = "textBox3"; + this.textBox3.ReadOnly = true; + // + // PayToDialog + // + this.AcceptButton = this.button1; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.textBox3); + this.Controls.Add(this.label4); + this.Controls.Add(this.comboBox1); + this.Controls.Add(this.label3); + this.Controls.Add(this.button1); + this.Controls.Add(this.textBox2); + this.Controls.Add(this.label2); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "PayToDialog"; + this.ShowInTaskbar = false; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.ComboBox comboBox1; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.TextBox textBox3; + } +} diff --git a/src/Neo.GUI/GUI/PayToDialog.cs b/src/Neo.GUI/GUI/PayToDialog.cs new file mode 100644 index 0000000000..ad024b7ca5 --- /dev/null +++ b/src/Neo.GUI/GUI/PayToDialog.cs @@ -0,0 +1,106 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// PayToDialog.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.Wallets; +using System; +using System.Windows.Forms; +using static Neo.Program; + +namespace Neo.GUI +{ + internal partial class PayToDialog : Form + { + public PayToDialog(AssetDescriptor asset = null, UInt160 scriptHash = null) + { + InitializeComponent(); + if (asset is null) + { + foreach (UInt160 assetId in NEP5Watched) + { + try + { + comboBox1.Items.Add(new AssetDescriptor(Service.NeoSystem.StoreView, Service.NeoSystem.Settings, assetId)); + } + catch (ArgumentException) + { + continue; + } + } + } + else + { + comboBox1.Items.Add(asset); + comboBox1.SelectedIndex = 0; + comboBox1.Enabled = false; + } + if (scriptHash != null) + { + textBox1.Text = scriptHash.ToAddress(Service.NeoSystem.Settings.AddressVersion); + textBox1.ReadOnly = true; + } + } + + public TxOutListBoxItem GetOutput() + { + AssetDescriptor asset = (AssetDescriptor)comboBox1.SelectedItem; + return new TxOutListBoxItem + { + AssetName = asset.AssetName, + AssetId = asset.AssetId, + Value = BigDecimal.Parse(textBox2.Text, asset.Decimals), + ScriptHash = textBox1.Text.ToScriptHash(Service.NeoSystem.Settings.AddressVersion) + }; + } + + private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) + { + if (comboBox1.SelectedItem is AssetDescriptor asset) + { + textBox3.Text = Service.CurrentWallet.GetAvailable(Service.NeoSystem.StoreView, asset.AssetId).ToString(); + } + else + { + textBox3.Text = ""; + } + textBox_TextChanged(this, EventArgs.Empty); + } + + private void textBox_TextChanged(object sender, EventArgs e) + { + if (comboBox1.SelectedIndex < 0 || textBox1.TextLength == 0 || textBox2.TextLength == 0) + { + button1.Enabled = false; + return; + } + try + { + textBox1.Text.ToScriptHash(Service.NeoSystem.Settings.AddressVersion); + } + catch (FormatException) + { + button1.Enabled = false; + return; + } + AssetDescriptor asset = (AssetDescriptor)comboBox1.SelectedItem; + if (!BigDecimal.TryParse(textBox2.Text, asset.Decimals, out BigDecimal amount)) + { + button1.Enabled = false; + return; + } + if (amount.Sign <= 0) + { + button1.Enabled = false; + return; + } + button1.Enabled = true; + } + } +} diff --git a/src/Neo.GUI/GUI/PayToDialog.es-ES.resx b/src/Neo.GUI/GUI/PayToDialog.es-ES.resx new file mode 100644 index 0000000000..525ae78e9a --- /dev/null +++ b/src/Neo.GUI/GUI/PayToDialog.es-ES.resx @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 12, 88 + + + 56, 17 + + + Pagar a: + + + 28, 122 + + + 40, 17 + + + Total: + + + Aceptar + + + 22, 17 + + + 46, 17 + + + Activo: + + + 24, 54 + + + 44, 17 + + + Saldo: + + + Pago + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/PayToDialog.resx b/src/Neo.GUI/GUI/PayToDialog.resx new file mode 100644 index 0000000000..2c575df5ab --- /dev/null +++ b/src/Neo.GUI/GUI/PayToDialog.resx @@ -0,0 +1,384 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + True + + + + 21, 88 + + + 47, 17 + + + 4 + + + Pay to: + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 8 + + + + Top, Left, Right + + + 74, 85 + + + 468, 23 + + + 5 + + + textBox1 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 7 + + + True + + + 12, 122 + + + 56, 17 + + + 6 + + + Amount: + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 6 + + + Top, Left, Right + + + 74, 119 + + + 468, 23 + + + 7 + + + textBox2 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 5 + + + Top, Right + + + False + + + 467, 157 + + + 75, 23 + + + 8 + + + OK + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 4 + + + True + + + 26, 17 + + + 42, 17 + + + 0 + + + Asset: + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + Top, Left, Right + + + 74, 14 + + + 468, 25 + + + 1 + + + comboBox1 + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + True + + + 12, 54 + + + 56, 17 + + + 2 + + + Balance: + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Top, Left, Right + + + 74, 51 + + + 468, 23 + + + 3 + + + textBox3 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 7, 17 + + + 554, 192 + + + 微软雅黑, 9pt + + + 3, 4, 3, 4 + + + CenterScreen + + + Payment + + + PayToDialog + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/PayToDialog.zh-Hans.resx b/src/Neo.GUI/GUI/PayToDialog.zh-Hans.resx new file mode 100644 index 0000000000..9f55c74270 --- /dev/null +++ b/src/Neo.GUI/GUI/PayToDialog.zh-Hans.resx @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 12, 75 + + + 59, 17 + + + 对方账户: + + + 77, 72 + + + 308, 23 + + + 36, 104 + + + 35, 17 + + + 数额: + + + 77, 101 + + + 227, 23 + + + 310, 101 + + + 确定 + + + 36, 15 + + + 35, 17 + + + 资产: + + + 77, 12 + + + 308, 25 + + + 36, 46 + + + 35, 17 + + + 余额: + + + 77, 43 + + + 308, 23 + + + 397, 134 + + + 支付 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/QueueReader.cs b/src/Neo.GUI/GUI/QueueReader.cs new file mode 100644 index 0000000000..eac36f35f4 --- /dev/null +++ b/src/Neo.GUI/GUI/QueueReader.cs @@ -0,0 +1,49 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// QueueReader.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.Collections.Generic; +using System.IO; +using System.Threading; + +namespace Neo.GUI +{ + internal class QueueReader : TextReader + { + private readonly Queue queue = new Queue(); + private string current; + private int index; + + public void Enqueue(string str) + { + queue.Enqueue(str); + } + + public override int Peek() + { + while (string.IsNullOrEmpty(current)) + { + while (!queue.TryDequeue(out current)) + Thread.Sleep(100); + index = 0; + } + return current[index]; + } + + public override int Read() + { + int c = Peek(); + if (c != -1) + if (++index >= current.Length) + current = null; + return c; + } + } +} diff --git a/src/Neo.GUI/GUI/SigningDialog.Designer.cs b/src/Neo.GUI/GUI/SigningDialog.Designer.cs new file mode 100644 index 0000000000..8c03f84901 --- /dev/null +++ b/src/Neo.GUI/GUI/SigningDialog.Designer.cs @@ -0,0 +1,172 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class SigningDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SigningDialog)); + this.button1 = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.button2 = new System.Windows.Forms.Button(); + this.button3 = new System.Windows.Forms.Button(); + this.cmbFormat = new System.Windows.Forms.ComboBox(); + this.cmbAddress = new System.Windows.Forms.ComboBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.SuspendLayout(); + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.textBox1); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // textBox1 + // + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.Name = "textBox1"; + // + // groupBox2 + // + resources.ApplyResources(this.groupBox2, "groupBox2"); + this.groupBox2.Controls.Add(this.textBox2); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.TabStop = false; + // + // textBox2 + // + resources.ApplyResources(this.textBox2, "textBox2"); + this.textBox2.Name = "textBox2"; + this.textBox2.ReadOnly = true; + // + // button2 + // + resources.ApplyResources(this.button2, "button2"); + this.button2.Name = "button2"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // button3 + // + resources.ApplyResources(this.button3, "button3"); + this.button3.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.button3.Name = "button3"; + this.button3.UseVisualStyleBackColor = true; + // + // cmbFormat + // + resources.ApplyResources(this.cmbFormat, "cmbFormat"); + this.cmbFormat.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbFormat.FormattingEnabled = true; + this.cmbFormat.Items.AddRange(new object[] { + resources.GetString("cmbFormat.Items"), + resources.GetString("cmbFormat.Items1")}); + this.cmbFormat.Name = "cmbFormat"; + // + // cmbAddress + // + resources.ApplyResources(this.cmbAddress, "cmbAddress"); + this.cmbAddress.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbAddress.FormattingEnabled = true; + this.cmbAddress.Name = "cmbAddress"; + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // SigningDialog + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.button3; + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.cmbAddress); + this.Controls.Add(this.cmbFormat); + this.Controls.Add(this.button3); + this.Controls.Add(this.button2); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.button1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "SigningDialog"; + this.ShowInTaskbar = false; + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button button1; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.Button button3; + private System.Windows.Forms.ComboBox cmbFormat; + private System.Windows.Forms.ComboBox cmbAddress; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + } +} diff --git a/src/Neo.GUI/GUI/SigningDialog.cs b/src/Neo.GUI/GUI/SigningDialog.cs new file mode 100644 index 0000000000..d235d9691a --- /dev/null +++ b/src/Neo.GUI/GUI/SigningDialog.cs @@ -0,0 +1,107 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// SigningDialog.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.Cryptography; +using Neo.Properties; +using Neo.Wallets; +using System; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using static Neo.Program; + +namespace Neo.GUI +{ + internal partial class SigningDialog : Form + { + private class WalletEntry + { + public WalletAccount Account; + + public override string ToString() + { + if (!string.IsNullOrEmpty(Account.Label)) + { + return $"[{Account.Label}] " + Account.Address; + } + return Account.Address; + } + } + + + public SigningDialog() + { + InitializeComponent(); + + cmbFormat.SelectedIndex = 0; + cmbAddress.Items.AddRange(Service.CurrentWallet.GetAccounts() + .Where(u => u.HasKey) + .Select(u => new WalletEntry() { Account = u }) + .ToArray()); + + if (cmbAddress.Items.Count > 0) + { + cmbAddress.SelectedIndex = 0; + } + else + { + textBox2.Enabled = false; + button1.Enabled = false; + } + } + + private void button1_Click(object sender, EventArgs e) + { + if (textBox1.Text == "") + { + MessageBox.Show(Strings.SigningFailedNoDataMessage); + return; + } + + byte[] raw, signedData; + try + { + switch (cmbFormat.SelectedIndex) + { + case 0: raw = Encoding.UTF8.GetBytes(textBox1.Text); break; + case 1: raw = textBox1.Text.HexToBytes(); break; + default: return; + } + } + catch (Exception err) + { + MessageBox.Show(err.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + var account = (WalletEntry)cmbAddress.SelectedItem; + var keys = account.Account.GetKey(); + + try + { + signedData = Crypto.Sign(raw, keys.PrivateKey, keys.PublicKey.EncodePoint(false).Skip(1).ToArray()); + } + catch (Exception err) + { + MessageBox.Show(err.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + textBox2.Text = signedData?.ToHexString(); + } + + private void button2_Click(object sender, EventArgs e) + { + textBox2.SelectAll(); + textBox2.Copy(); + } + } +} diff --git a/src/Neo.GUI/GUI/SigningDialog.es-ES.resx b/src/Neo.GUI/GUI/SigningDialog.es-ES.resx new file mode 100644 index 0000000000..c440dbe4b2 --- /dev/null +++ b/src/Neo.GUI/GUI/SigningDialog.es-ES.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Firma + + + Entrada + + + Salida + + + Copiar + + + Cancelar + + + Emitir + + + Firma + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/SigningDialog.resx b/src/Neo.GUI/GUI/SigningDialog.resx new file mode 100644 index 0000000000..d462a50fac --- /dev/null +++ b/src/Neo.GUI/GUI/SigningDialog.resx @@ -0,0 +1,462 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top + + + + 189, 269 + + + 75, 23 + + + + 2 + + + Sign + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 8 + + + Top, Left, Right + + + Fill + + + 3, 19 + + + True + + + Vertical + + + 424, 139 + + + 1 + + + textBox1 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + 12, 97 + + + 430, 161 + + + 3 + + + Input + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 7 + + + Top, Bottom, Left, Right + + + Fill + + + 3, 19 + + + True + + + Vertical + + + 424, 117 + + + 1 + + + textBox2 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox2 + + + 0 + + + 12, 298 + + + 430, 139 + + + 4 + + + Output + + + groupBox2 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 6 + + + Bottom, Right + + + 286, 453 + + + 75, 23 + + + 5 + + + Copy + + + button2 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 5 + + + Bottom, Right + + + 367, 453 + + + 75, 23 + + + 6 + + + Close + + + button3 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 4 + + + Text + + + Hex + + + 367, 48 + + + 2, 3, 2, 3 + + + 72, 25 + + + 7 + + + cmbFormat + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + 15, 48 + + + 2, 3, 2, 3 + + + 349, 25 + + + 8 + + + cmbAddress + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + True + + + 12, 21 + + + 2, 0, 2, 0 + + + 56, 17 + + + 9 + + + Address + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + True + + + NoControl + + + 364, 21 + + + 2, 0, 2, 0 + + + 49, 17 + + + 9 + + + Format + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 7, 17 + + + 454, 488 + + + Microsoft YaHei UI, 9pt + + + 3, 4, 3, 4 + + + CenterScreen + + + Signature + + + SigningDialog + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/SigningDialog.zh-Hans.resx b/src/Neo.GUI/GUI/SigningDialog.zh-Hans.resx new file mode 100644 index 0000000000..282612d0f8 --- /dev/null +++ b/src/Neo.GUI/GUI/SigningDialog.zh-Hans.resx @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 签名 + + + 输入 + + + 输出 + + + 复制 + + + 关闭 + + + + 32, 17 + + + 地址 + + + 32, 17 + + + 格式 + + + 签名 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/SigningTxDialog.Designer.cs b/src/Neo.GUI/GUI/SigningTxDialog.Designer.cs new file mode 100644 index 0000000000..45ecb20563 --- /dev/null +++ b/src/Neo.GUI/GUI/SigningTxDialog.Designer.cs @@ -0,0 +1,142 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class SigningTxDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SigningTxDialog)); + this.button1 = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.button2 = new System.Windows.Forms.Button(); + this.button3 = new System.Windows.Forms.Button(); + this.button4 = new System.Windows.Forms.Button(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.SuspendLayout(); + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.textBox1); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // textBox1 + // + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.Name = "textBox1"; + // + // groupBox2 + // + resources.ApplyResources(this.groupBox2, "groupBox2"); + this.groupBox2.Controls.Add(this.textBox2); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.TabStop = false; + // + // textBox2 + // + resources.ApplyResources(this.textBox2, "textBox2"); + this.textBox2.Name = "textBox2"; + this.textBox2.ReadOnly = true; + // + // button2 + // + resources.ApplyResources(this.button2, "button2"); + this.button2.Name = "button2"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // button3 + // + resources.ApplyResources(this.button3, "button3"); + this.button3.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.button3.Name = "button3"; + this.button3.UseVisualStyleBackColor = true; + // + // button4 + // + resources.ApplyResources(this.button4, "button4"); + this.button4.Name = "button4"; + this.button4.UseVisualStyleBackColor = true; + this.button4.Click += new System.EventHandler(this.button4_Click); + // + // SigningTxDialog + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.button3; + this.Controls.Add(this.button4); + this.Controls.Add(this.button3); + this.Controls.Add(this.button2); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.button1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "SigningTxDialog"; + this.ShowInTaskbar = false; + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button button1; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.Button button3; + private System.Windows.Forms.Button button4; + } +} diff --git a/src/Neo.GUI/GUI/SigningTxDialog.cs b/src/Neo.GUI/GUI/SigningTxDialog.cs new file mode 100644 index 0000000000..16e79efe58 --- /dev/null +++ b/src/Neo.GUI/GUI/SigningTxDialog.cs @@ -0,0 +1,66 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// SigningTxDialog.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 Akka.Actor; +using Neo.Network.P2P.Payloads; +using Neo.Properties; +using Neo.SmartContract; +using System; +using System.Windows.Forms; +using static Neo.Program; + +namespace Neo.GUI +{ + internal partial class SigningTxDialog : Form + { + public SigningTxDialog() + { + InitializeComponent(); + } + + private void button1_Click(object sender, EventArgs e) + { + if (textBox1.Text == "") + { + MessageBox.Show(Strings.SigningFailedNoDataMessage); + return; + } + ContractParametersContext context = ContractParametersContext.Parse(textBox1.Text, Service.NeoSystem.StoreView); + if (!Service.CurrentWallet.Sign(context)) + { + MessageBox.Show(Strings.SigningFailedKeyNotFoundMessage); + return; + } + textBox2.Text = context.ToString(); + if (context.Completed) button4.Visible = true; + } + + private void button2_Click(object sender, EventArgs e) + { + textBox2.SelectAll(); + textBox2.Copy(); + } + + private void button4_Click(object sender, EventArgs e) + { + ContractParametersContext context = ContractParametersContext.Parse(textBox2.Text, Service.NeoSystem.StoreView); + if (!(context.Verifiable is Transaction tx)) + { + MessageBox.Show("Only support to broadcast transaction."); + return; + } + tx.Witnesses = context.GetWitnesses(); + Service.NeoSystem.Blockchain.Tell(tx); + InformationBox.Show(tx.Hash.ToString(), Strings.RelaySuccessText, Strings.RelaySuccessTitle); + button4.Visible = false; + } + } +} diff --git a/src/Neo.GUI/GUI/SigningTxDialog.es-ES.resx b/src/Neo.GUI/GUI/SigningTxDialog.es-ES.resx new file mode 100644 index 0000000000..c440dbe4b2 --- /dev/null +++ b/src/Neo.GUI/GUI/SigningTxDialog.es-ES.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Firma + + + Entrada + + + Salida + + + Copiar + + + Cancelar + + + Emitir + + + Firma + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/SigningTxDialog.resx b/src/Neo.GUI/GUI/SigningTxDialog.resx new file mode 100644 index 0000000000..1f1af30e86 --- /dev/null +++ b/src/Neo.GUI/GUI/SigningTxDialog.resx @@ -0,0 +1,372 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top + + + + 190, 191 + + + 75, 23 + + + + 2 + + + Sign + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 5 + + + Top, Left, Right + + + 12, 12 + + + 430, 173 + + + 3 + + + Input + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 4 + + + Fill + + + 3, 19 + + + True + + + Vertical + + + 424, 151 + + + 1 + + + textBox1 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + Top, Bottom, Left, Right + + + 12, 220 + + + 430, 227 + + + 4 + + + Output + + + groupBox2 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + Fill + + + 3, 19 + + + True + + + Vertical + + + 424, 205 + + + 1 + + + textBox2 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox2 + + + 0 + + + Bottom, Right + + + 286, 453 + + + 75, 23 + + + 5 + + + Copy + + + button2 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + Bottom, Right + + + 367, 453 + + + 75, 23 + + + 6 + + + Close + + + button3 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 12, 453 + + + 75, 23 + + + 7 + + + Broadcast + + + False + + + button4 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 7, 17 + + + 454, 488 + + + 微软雅黑, 9pt + + + 3, 4, 3, 4 + + + CenterScreen + + + Signature + + + SigningTxDialog + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/src/Neo.GUI/GUI/SigningTxDialog.zh-Hans.resx b/src/Neo.GUI/GUI/SigningTxDialog.zh-Hans.resx new file mode 100644 index 0000000000..218f36f8e5 --- /dev/null +++ b/src/Neo.GUI/GUI/SigningTxDialog.zh-Hans.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 签名 + + + 输入 + + + 输出 + + + 复制 + + + 关闭 + + + 广播 + + + 签名 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/TextBoxWriter.cs b/src/Neo.GUI/GUI/TextBoxWriter.cs new file mode 100644 index 0000000000..bf1bfc9c40 --- /dev/null +++ b/src/Neo.GUI/GUI/TextBoxWriter.cs @@ -0,0 +1,40 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TextBoxWriter.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; +using System.IO; +using System.Text; +using System.Windows.Forms; + +namespace Neo.GUI +{ + internal class TextBoxWriter : TextWriter + { + private readonly TextBoxBase textBox; + + public override Encoding Encoding => Encoding.UTF8; + + public TextBoxWriter(TextBoxBase textBox) + { + this.textBox = textBox; + } + + public override void Write(char value) + { + textBox.Invoke(new Action(() => { textBox.Text += value; })); + } + + public override void Write(string value) + { + textBox.Invoke(new Action(textBox.AppendText), value); + } + } +} diff --git a/src/Neo.GUI/GUI/TransferDialog.Designer.cs b/src/Neo.GUI/GUI/TransferDialog.Designer.cs new file mode 100644 index 0000000000..2d19a83fce --- /dev/null +++ b/src/Neo.GUI/GUI/TransferDialog.Designer.cs @@ -0,0 +1,131 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class TransferDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TransferDialog)); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.txOutListBox1 = new Neo.GUI.TxOutListBox(); + this.button4 = new System.Windows.Forms.Button(); + this.button3 = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.comboBoxFrom = new System.Windows.Forms.ComboBox(); + this.labelFrom = new System.Windows.Forms.Label(); + this.groupBox3.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // groupBox3 + // + resources.ApplyResources(this.groupBox3, "groupBox3"); + this.groupBox3.Controls.Add(this.txOutListBox1); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.TabStop = false; + // + // txOutListBox1 + // + resources.ApplyResources(this.txOutListBox1, "txOutListBox1"); + this.txOutListBox1.Asset = null; + this.txOutListBox1.Name = "txOutListBox1"; + this.txOutListBox1.ReadOnly = false; + this.txOutListBox1.ScriptHash = null; + this.txOutListBox1.ItemsChanged += new System.EventHandler(this.txOutListBox1_ItemsChanged); + // + // button4 + // + resources.ApplyResources(this.button4, "button4"); + this.button4.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.button4.Name = "button4"; + this.button4.UseVisualStyleBackColor = true; + // + // button3 + // + resources.ApplyResources(this.button3, "button3"); + this.button3.DialogResult = System.Windows.Forms.DialogResult.OK; + this.button3.Name = "button3"; + this.button3.UseVisualStyleBackColor = true; + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.comboBoxFrom); + this.groupBox1.Controls.Add(this.labelFrom); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // comboBoxFrom + // + resources.ApplyResources(this.comboBoxFrom, "comboBoxFrom"); + this.comboBoxFrom.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxFrom.FormattingEnabled = true; + this.comboBoxFrom.Name = "comboBoxFrom"; + // + // labelFrom + // + resources.ApplyResources(this.labelFrom, "labelFrom"); + this.labelFrom.Name = "labelFrom"; + // + // TransferDialog + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.button4); + this.Controls.Add(this.button3); + this.Controls.Add(this.groupBox3); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.Name = "TransferDialog"; + this.ShowInTaskbar = false; + this.groupBox3.ResumeLayout(false); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + private System.Windows.Forms.GroupBox groupBox3; + private System.Windows.Forms.Button button4; + private System.Windows.Forms.Button button3; + private TxOutListBox txOutListBox1; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.ComboBox comboBoxFrom; + private System.Windows.Forms.Label labelFrom; + } +} diff --git a/src/Neo.GUI/GUI/TransferDialog.cs b/src/Neo.GUI/GUI/TransferDialog.cs new file mode 100644 index 0000000000..357dacb04a --- /dev/null +++ b/src/Neo.GUI/GUI/TransferDialog.cs @@ -0,0 +1,42 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TransferDialog.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.Network.P2P.Payloads; +using Neo.SmartContract; +using Neo.Wallets; +using System; +using System.Linq; +using System.Windows.Forms; +using static Neo.Program; + +namespace Neo.GUI +{ + public partial class TransferDialog : Form + { + public TransferDialog() + { + InitializeComponent(); + comboBoxFrom.Items.AddRange(Service.CurrentWallet.GetAccounts().Where(p => !p.WatchOnly).Select(p => p.Address).ToArray()); + } + + public Transaction GetTransaction() + { + TransferOutput[] outputs = txOutListBox1.Items.ToArray(); + UInt160 from = comboBoxFrom.SelectedItem is null ? null : ((string)comboBoxFrom.SelectedItem).ToScriptHash(Service.NeoSystem.Settings.AddressVersion); + return Service.CurrentWallet.MakeTransaction(Service.NeoSystem.StoreView, outputs, from); + } + + private void txOutListBox1_ItemsChanged(object sender, EventArgs e) + { + button3.Enabled = txOutListBox1.ItemCount > 0; + } + } +} diff --git a/src/Neo.GUI/GUI/TransferDialog.es-ES.resx b/src/Neo.GUI/GUI/TransferDialog.es-ES.resx new file mode 100644 index 0000000000..662fc871c4 --- /dev/null +++ b/src/Neo.GUI/GUI/TransferDialog.es-ES.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Lista de destinatarios + + + Cancelar + + + Aceptar + + + Transferir + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/TransferDialog.resx b/src/Neo.GUI/GUI/TransferDialog.resx new file mode 100644 index 0000000000..f902756230 --- /dev/null +++ b/src/Neo.GUI/GUI/TransferDialog.resx @@ -0,0 +1,369 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Left, Right + + + Top, Bottom, Left, Right + + + + Microsoft YaHei UI, 9pt + + + 6, 24 + + + 3, 4, 3, 4 + + + 551, 276 + + + + 0 + + + txOutListBox1 + + + Neo.UI.TxOutListBox, neo-gui, Version=2.10.7263.32482, Culture=neutral, PublicKeyToken=null + + + groupBox3 + + + 0 + + + 12, 13 + + + 3, 4, 3, 4 + + + 3, 4, 3, 4 + + + 563, 308 + + + 0 + + + Recipient List + + + groupBox3 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + Bottom, Right + + + NoControl + + + 500, 401 + + + 3, 4, 3, 4 + + + 75, 24 + + + 2 + + + Cancel + + + button4 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Bottom, Right + + + False + + + NoControl + + + 419, 401 + + + 3, 4, 3, 4 + + + 75, 24 + + + 1 + + + OK + + + button3 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + Top, Left, Right + + + Top, Left, Right + + + 78, 22 + + + 418, 0 + + + 479, 25 + + + 2 + + + comboBoxFrom + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + True + + + NoControl + + + 31, 25 + + + 41, 17 + + + 4 + + + From: + + + MiddleLeft + + + labelFrom + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 1 + + + 12, 328 + + + 563, 60 + + + 4 + + + Advanced + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 7, 17 + + + 587, 440 + + + Microsoft YaHei UI, 9pt + + + 3, 4, 3, 4 + + + CenterScreen + + + Transfer + + + TransferDialog + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/TransferDialog.zh-Hans.resx b/src/Neo.GUI/GUI/TransferDialog.zh-Hans.resx new file mode 100644 index 0000000000..33ddb97439 --- /dev/null +++ b/src/Neo.GUI/GUI/TransferDialog.zh-Hans.resx @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 收款人列表 + + + 取消 + + + 确定 + + + 高级 + + + + 44, 17 + + + 转自: + + + 转账 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/TxOutListBox.Designer.cs b/src/Neo.GUI/GUI/TxOutListBox.Designer.cs new file mode 100644 index 0000000000..ad9f54d846 --- /dev/null +++ b/src/Neo.GUI/GUI/TxOutListBox.Designer.cs @@ -0,0 +1,109 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class TxOutListBox + { + /// + /// 必需的设计器变量。 + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 清理所有正在使用的资源。 + /// + /// 如果应释放托管资源,为 true;否则为 false。 + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region 组件设计器生成的代码 + + /// + /// 设计器支持所需的方法 - 不要修改 + /// 使用代码编辑器修改此方法的内容。 + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TxOutListBox)); + this.listBox1 = new System.Windows.Forms.ListBox(); + this.panel1 = new System.Windows.Forms.Panel(); + this.button1 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.button3 = new System.Windows.Forms.Button(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // listBox1 + // + resources.ApplyResources(this.listBox1, "listBox1"); + this.listBox1.Name = "listBox1"; + this.listBox1.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended; + this.listBox1.SelectedIndexChanged += new System.EventHandler(this.listBox1_SelectedIndexChanged); + // + // panel1 + // + resources.ApplyResources(this.panel1, "panel1"); + this.panel1.Controls.Add(this.button1); + this.panel1.Controls.Add(this.button2); + this.panel1.Controls.Add(this.button3); + this.panel1.Name = "panel1"; + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.Image = global::Neo.Properties.Resources.add; + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // button2 + // + resources.ApplyResources(this.button2, "button2"); + this.button2.Image = global::Neo.Properties.Resources.remove; + this.button2.Name = "button2"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // button3 + // + resources.ApplyResources(this.button3, "button3"); + this.button3.Image = global::Neo.Properties.Resources.add2; + this.button3.Name = "button3"; + this.button3.UseVisualStyleBackColor = true; + this.button3.Click += new System.EventHandler(this.button3_Click); + // + // TxOutListBox + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.panel1); + this.Controls.Add(this.listBox1); + this.Name = "TxOutListBox"; + this.panel1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListBox listBox1; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.Button button3; + } +} diff --git a/src/Neo.GUI/GUI/TxOutListBox.cs b/src/Neo.GUI/GUI/TxOutListBox.cs new file mode 100644 index 0000000000..d2e314ecaf --- /dev/null +++ b/src/Neo.GUI/GUI/TxOutListBox.cs @@ -0,0 +1,103 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TxOutListBox.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.Wallets; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Windows.Forms; + +namespace Neo.GUI +{ + [DefaultEvent(nameof(ItemsChanged))] + internal partial class TxOutListBox : UserControl + { + public event EventHandler ItemsChanged; + + public AssetDescriptor Asset { get; set; } + + public int ItemCount => listBox1.Items.Count; + + public IEnumerable Items => listBox1.Items.OfType(); + + public bool ReadOnly + { + get + { + return !panel1.Enabled; + } + set + { + panel1.Enabled = !value; + } + } + + private UInt160 _script_hash = null; + public UInt160 ScriptHash + { + get + { + return _script_hash; + } + set + { + _script_hash = value; + button3.Enabled = value == null; + } + } + + public TxOutListBox() + { + InitializeComponent(); + } + + public void Clear() + { + if (listBox1.Items.Count > 0) + { + listBox1.Items.Clear(); + button2.Enabled = false; + ItemsChanged?.Invoke(this, EventArgs.Empty); + } + } + + private void listBox1_SelectedIndexChanged(object sender, EventArgs e) + { + button2.Enabled = listBox1.SelectedIndices.Count > 0; + } + + private void button1_Click(object sender, EventArgs e) + { + using PayToDialog dialog = new PayToDialog(asset: Asset, scriptHash: ScriptHash); + if (dialog.ShowDialog() != DialogResult.OK) return; + listBox1.Items.Add(dialog.GetOutput()); + ItemsChanged?.Invoke(this, EventArgs.Empty); + } + + private void button2_Click(object sender, EventArgs e) + { + while (listBox1.SelectedIndices.Count > 0) + { + listBox1.Items.RemoveAt(listBox1.SelectedIndices[0]); + } + ItemsChanged?.Invoke(this, EventArgs.Empty); + } + + private void button3_Click(object sender, EventArgs e) + { + using BulkPayDialog dialog = new BulkPayDialog(Asset); + if (dialog.ShowDialog() != DialogResult.OK) return; + listBox1.Items.AddRange(dialog.GetOutputs()); + ItemsChanged?.Invoke(this, EventArgs.Empty); + } + } +} diff --git a/src/Neo.GUI/GUI/TxOutListBox.resx b/src/Neo.GUI/GUI/TxOutListBox.resx new file mode 100644 index 0000000000..92bba21c58 --- /dev/null +++ b/src/Neo.GUI/GUI/TxOutListBox.resx @@ -0,0 +1,300 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Bottom, Left, Right + + + + True + + + False + + + 17 + + + + 0, 0 + + + 3, 4, 3, 4 + + + True + + + 349, 167 + + + 0 + + + listBox1 + + + System.Windows.Forms.ListBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Bottom, Left, Right + + + Bottom, Left + + + NoControl + + + 0, 0 + + + 3, 4, 3, 4 + + + 27, 27 + + + 0 + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + panel1 + + + 0 + + + Bottom, Left + + + False + + + NoControl + + + 33, 0 + + + 3, 4, 3, 4 + + + 27, 27 + + + 1 + + + button2 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + panel1 + + + 1 + + + Bottom, Left + + + NoControl + + + 66, 0 + + + 3, 4, 3, 4 + + + 27, 27 + + + 2 + + + button3 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + panel1 + + + 2 + + + 0, 175 + + + 349, 27 + + + 1 + + + panel1 + + + System.Windows.Forms.Panel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 7, 17 + + + 微软雅黑, 9pt + + + 3, 4, 3, 4 + + + 349, 202 + + + TxOutListBox + + + System.Windows.Forms.UserControl, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/TxOutListBoxItem.cs b/src/Neo.GUI/GUI/TxOutListBoxItem.cs new file mode 100644 index 0000000000..dfbb431ec2 --- /dev/null +++ b/src/Neo.GUI/GUI/TxOutListBoxItem.cs @@ -0,0 +1,25 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TxOutListBoxItem.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.Wallets; + +namespace Neo.GUI +{ + internal class TxOutListBoxItem : TransferOutput + { + public string AssetName; + + public override string ToString() + { + return $"{ScriptHash.ToAddress(Program.Service.NeoSystem.Settings.AddressVersion)}\t{Value}\t{AssetName}"; + } + } +} diff --git a/src/Neo.GUI/GUI/UpdateDialog.Designer.cs b/src/Neo.GUI/GUI/UpdateDialog.Designer.cs new file mode 100644 index 0000000000..6af087d42b --- /dev/null +++ b/src/Neo.GUI/GUI/UpdateDialog.Designer.cs @@ -0,0 +1,149 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class UpdateDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(UpdateDialog)); + this.label1 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.linkLabel1 = new System.Windows.Forms.LinkLabel(); + this.button1 = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.button2 = new System.Windows.Forms.Button(); + this.linkLabel2 = new System.Windows.Forms.LinkLabel(); + this.progressBar1 = new System.Windows.Forms.ProgressBar(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // textBox1 + // + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textBox1.Name = "textBox1"; + this.textBox1.ReadOnly = true; + // + // linkLabel1 + // + resources.ApplyResources(this.linkLabel1, "linkLabel1"); + this.linkLabel1.Name = "linkLabel1"; + this.linkLabel1.TabStop = true; + this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked); + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.textBox2); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // textBox2 + // + resources.ApplyResources(this.textBox2, "textBox2"); + this.textBox2.Name = "textBox2"; + this.textBox2.ReadOnly = true; + // + // button2 + // + resources.ApplyResources(this.button2, "button2"); + this.button2.Name = "button2"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // linkLabel2 + // + resources.ApplyResources(this.linkLabel2, "linkLabel2"); + this.linkLabel2.Name = "linkLabel2"; + this.linkLabel2.TabStop = true; + this.linkLabel2.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel2_LinkClicked); + // + // progressBar1 + // + resources.ApplyResources(this.progressBar1, "progressBar1"); + this.progressBar1.Name = "progressBar1"; + // + // UpdateDialog + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.button1; + this.Controls.Add(this.progressBar1); + this.Controls.Add(this.linkLabel2); + this.Controls.Add(this.button2); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.button1); + this.Controls.Add(this.linkLabel1); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "UpdateDialog"; + this.ShowInTaskbar = false; + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.LinkLabel linkLabel1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.LinkLabel linkLabel2; + private System.Windows.Forms.ProgressBar progressBar1; + } +} diff --git a/src/Neo.GUI/GUI/UpdateDialog.cs b/src/Neo.GUI/GUI/UpdateDialog.cs new file mode 100644 index 0000000000..215d19d855 --- /dev/null +++ b/src/Neo.GUI/GUI/UpdateDialog.cs @@ -0,0 +1,77 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UpdateDialog.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.Properties; +using System; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Net.Http; +using System.Windows.Forms; +using System.Xml.Linq; + +namespace Neo.GUI +{ + internal partial class UpdateDialog : Form + { + private readonly HttpClient http = new(); + private readonly string download_url; + private string download_path; + + public UpdateDialog(XDocument xdoc) + { + InitializeComponent(); + Version latest = Version.Parse(xdoc.Element("update").Attribute("latest").Value); + textBox1.Text = latest.ToString(); + XElement release = xdoc.Element("update").Elements("release").First(p => p.Attribute("version").Value == latest.ToString()); + textBox2.Text = release.Element("changes").Value.Replace("\n", Environment.NewLine); + download_url = release.Attribute("file").Value; + } + + private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + Process.Start("https://neo.org/"); + } + + private void linkLabel2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + Process.Start(download_url); + } + + private async void button2_Click(object sender, EventArgs e) + { + button1.Enabled = false; + button2.Enabled = false; + download_path = "update.zip"; + using (Stream responseStream = await http.GetStreamAsync(download_url)) + using (FileStream fileStream = new(download_path, FileMode.Create, FileAccess.Write, FileShare.None)) + { + await responseStream.CopyToAsync(fileStream); + } + DirectoryInfo di = new DirectoryInfo("update"); + if (di.Exists) di.Delete(true); + di.Create(); + ZipFile.ExtractToDirectory(download_path, di.Name); + FileSystemInfo[] fs = di.GetFileSystemInfos(); + if (fs.Length == 1 && fs[0] is DirectoryInfo directory) + { + directory.MoveTo("update2"); + di.Delete(); + Directory.Move("update2", di.Name); + } + File.WriteAllBytes("update.bat", Resources.UpdateBat); + Close(); + if (Program.MainForm != null) Program.MainForm.Close(); + Process.Start("update.bat"); + } + } +} diff --git a/src/Neo.GUI/GUI/UpdateDialog.es-ES.resx b/src/Neo.GUI/GUI/UpdateDialog.es-ES.resx new file mode 100644 index 0000000000..5f94e9b654 --- /dev/null +++ b/src/Neo.GUI/GUI/UpdateDialog.es-ES.resx @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 94, 17 + + + Última versión: + + + 112, 15 + + + 332, 16 + + + 73, 17 + + + Web oficial + + + Cancelar + + + Registro de cambios + + + Actualizar + + + 68, 17 + + + Descargar + + + Actualización disponible + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/UpdateDialog.resx b/src/Neo.GUI/GUI/UpdateDialog.resx new file mode 100644 index 0000000000..23e774988f --- /dev/null +++ b/src/Neo.GUI/GUI/UpdateDialog.resx @@ -0,0 +1,399 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + True + + + + 12, 15 + + + 102, 17 + + + 0 + + + Newest Version: + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 7 + + + + Top, Left, Right + + + 120, 15 + + + 324, 16 + + + 1 + + + textBox1 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 6 + + + Bottom, Left + + + True + + + 12, 335 + + + 79, 17 + + + 4 + + + Official Web + + + linkLabel1 + + + System.Windows.Forms.LinkLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 5 + + + Bottom, Right + + + 369, 332 + + + 75, 23 + + + 7 + + + Close + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 4 + + + Top, Bottom, Left, Right + + + Fill + + + 3, 19 + + + True + + + Both + + + 426, 234 + + + 0 + + + False + + + textBox2 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + 12, 41 + + + 432, 256 + + + 2 + + + Change logs + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + Bottom, Right + + + 288, 332 + + + 75, 23 + + + 6 + + + Update + + + button2 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + Bottom, Left + + + True + + + NoControl + + + 97, 335 + + + 67, 17 + + + 5 + + + Download + + + linkLabel2 + + + System.Windows.Forms.LinkLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 12, 303 + + + 432, 23 + + + 3 + + + progressBar1 + + + System.Windows.Forms.ProgressBar, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 7, 17 + + + 456, 367 + + + 微软雅黑, 9pt + + + 3, 4, 3, 4 + + + CenterScreen + + + Update Available + + + UpdateDialog + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/UpdateDialog.zh-Hans.resx b/src/Neo.GUI/GUI/UpdateDialog.zh-Hans.resx new file mode 100644 index 0000000000..6db88c3a6e --- /dev/null +++ b/src/Neo.GUI/GUI/UpdateDialog.zh-Hans.resx @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 59, 17 + + + 最新版本: + + + 77, 15 + + + 367, 16 + + + 32, 17 + + + 官网 + + + 关闭 + + + 更新日志 + + + 更新 + + + 50, 335 + + + 32, 17 + + + 下载 + + + 发现新版本 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ViewContractDialog.Designer.cs b/src/Neo.GUI/GUI/ViewContractDialog.Designer.cs new file mode 100644 index 0000000000..5adbe25451 --- /dev/null +++ b/src/Neo.GUI/GUI/ViewContractDialog.Designer.cs @@ -0,0 +1,143 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class ViewContractDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ViewContractDialog)); + this.label1 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.textBox4 = new System.Windows.Forms.TextBox(); + this.button1 = new System.Windows.Forms.Button(); + this.label3 = new System.Windows.Forms.Label(); + this.textBox3 = new System.Windows.Forms.TextBox(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // textBox1 + // + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.Name = "textBox1"; + this.textBox1.ReadOnly = true; + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // textBox2 + // + resources.ApplyResources(this.textBox2, "textBox2"); + this.textBox2.Name = "textBox2"; + this.textBox2.ReadOnly = true; + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.textBox4); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // textBox4 + // + resources.ApplyResources(this.textBox4, "textBox4"); + this.textBox4.Name = "textBox4"; + this.textBox4.ReadOnly = true; + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + // + // label3 + // + resources.ApplyResources(this.label3, "label3"); + this.label3.Name = "label3"; + // + // textBox3 + // + resources.ApplyResources(this.textBox3, "textBox3"); + this.textBox3.Name = "textBox3"; + this.textBox3.ReadOnly = true; + // + // ViewContractDialog + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.button1; + this.Controls.Add(this.textBox3); + this.Controls.Add(this.label3); + this.Controls.Add(this.button1); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.textBox2); + this.Controls.Add(this.label2); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.label1); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ViewContractDialog"; + this.ShowInTaskbar = false; + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.TextBox textBox4; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox textBox3; + } +} diff --git a/src/Neo.GUI/GUI/ViewContractDialog.cs b/src/Neo.GUI/GUI/ViewContractDialog.cs new file mode 100644 index 0000000000..00ea963328 --- /dev/null +++ b/src/Neo.GUI/GUI/ViewContractDialog.cs @@ -0,0 +1,30 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ViewContractDialog.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.SmartContract; +using Neo.Wallets; +using System.Linq; +using System.Windows.Forms; + +namespace Neo.GUI +{ + public partial class ViewContractDialog : Form + { + public ViewContractDialog(Contract contract) + { + InitializeComponent(); + textBox1.Text = contract.ScriptHash.ToAddress(Program.Service.NeoSystem.Settings.AddressVersion); + textBox2.Text = contract.ScriptHash.ToString(); + textBox3.Text = contract.ParameterList.Cast().ToArray().ToHexString(); + textBox4.Text = contract.Script.ToHexString(); + } + } +} diff --git a/src/Neo.GUI/GUI/ViewContractDialog.es-ES.resx b/src/Neo.GUI/GUI/ViewContractDialog.es-ES.resx new file mode 100644 index 0000000000..c792cfc6ab --- /dev/null +++ b/src/Neo.GUI/GUI/ViewContractDialog.es-ES.resx @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 72, 15 + + + 65, 17 + + + Dirección: + + + 143, 12 + + + 363, 23 + + + 39, 44 + + + 98, 17 + + + Hash del script: + + + 143, 41 + + + 363, 23 + + + 494, 273 + + + Código del script + + + 488, 251 + + + 431, 378 + + + Cancelar + + + 9, 73 + + + 128, 17 + + + Lista de parámetros: + + + 143, 70 + + + 363, 23 + + + 518, 413 + + + Ver contrato + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ViewContractDialog.resx b/src/Neo.GUI/GUI/ViewContractDialog.resx new file mode 100644 index 0000000000..97c2f61083 --- /dev/null +++ b/src/Neo.GUI/GUI/ViewContractDialog.resx @@ -0,0 +1,387 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + True + + + + 47, 15 + + + 59, 17 + + + 0 + + + Address: + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 7 + + + + Top, Left, Right + + + 112, 12 + + + 328, 23 + + + 1 + + + textBox1 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 6 + + + True + + + 29, 44 + + + 77, 17 + + + 2 + + + Script Hash: + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 5 + + + Top, Left, Right + + + 112, 41 + + + 328, 23 + + + 3 + + + textBox2 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 4 + + + Top, Bottom, Left, Right + + + Fill + + + 3, 19 + + + True + + + Vertical + + + 422, 251 + + + 0 + + + textBox4 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + 12, 99 + + + 428, 273 + + + 6 + + + Redeem Script + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + Bottom, Right + + + 365, 378 + + + 75, 23 + + + 7 + + + Close + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + True + + + 12, 73 + + + 94, 17 + + + 4 + + + Parameter List: + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Top, Left, Right + + + 112, 70 + + + 328, 23 + + + 5 + + + textBox3 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 7, 17 + + + 452, 413 + + + Microsoft YaHei UI, 9pt + + + 2, 2, 2, 2 + + + CenterScreen + + + View Contract + + + ViewContractDialog + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ViewContractDialog.zh-Hans.resx b/src/Neo.GUI/GUI/ViewContractDialog.zh-Hans.resx new file mode 100644 index 0000000000..9a870a5476 --- /dev/null +++ b/src/Neo.GUI/GUI/ViewContractDialog.zh-Hans.resx @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 36, 15 + + + 35, 17 + + + 地址: + + + 77, 12 + + + 363, 23 + + + 12, 44 + + + 59, 17 + + + 脚本散列: + + + 77, 41 + + + 363, 23 + + + 脚本 + + + 关闭 + + + 59, 17 + + + 形参列表: + + + 77, 70 + + + 363, 23 + + + 查看合约 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ViewPrivateKeyDialog.cs b/src/Neo.GUI/GUI/ViewPrivateKeyDialog.cs new file mode 100644 index 0000000000..7beaa56271 --- /dev/null +++ b/src/Neo.GUI/GUI/ViewPrivateKeyDialog.cs @@ -0,0 +1,29 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ViewPrivateKeyDialog.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.Wallets; +using System.Windows.Forms; + +namespace Neo.GUI +{ + internal partial class ViewPrivateKeyDialog : Form + { + public ViewPrivateKeyDialog(WalletAccount account) + { + InitializeComponent(); + KeyPair key = account.GetKey(); + textBox3.Text = account.Address; + textBox4.Text = key.PublicKey.EncodePoint(true).ToHexString(); + textBox1.Text = key.PrivateKey.ToHexString(); + textBox2.Text = key.Export(); + } + } +} diff --git a/src/Neo.GUI/GUI/ViewPrivateKeyDialog.designer.cs b/src/Neo.GUI/GUI/ViewPrivateKeyDialog.designer.cs new file mode 100644 index 0000000000..2ba3d9773f --- /dev/null +++ b/src/Neo.GUI/GUI/ViewPrivateKeyDialog.designer.cs @@ -0,0 +1,155 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class ViewPrivateKeyDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ViewPrivateKeyDialog)); + this.label1 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.button1 = new System.Windows.Forms.Button(); + this.textBox3 = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.textBox4 = new System.Windows.Forms.TextBox(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // textBox1 + // + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.Name = "textBox1"; + this.textBox1.ReadOnly = true; + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.textBox2); + this.groupBox1.Controls.Add(this.label4); + this.groupBox1.Controls.Add(this.label3); + this.groupBox1.Controls.Add(this.textBox1); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // textBox2 + // + resources.ApplyResources(this.textBox2, "textBox2"); + this.textBox2.Name = "textBox2"; + this.textBox2.ReadOnly = true; + // + // label4 + // + resources.ApplyResources(this.label4, "label4"); + this.label4.Name = "label4"; + // + // label3 + // + resources.ApplyResources(this.label3, "label3"); + this.label3.Name = "label3"; + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + // + // textBox3 + // + resources.ApplyResources(this.textBox3, "textBox3"); + this.textBox3.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textBox3.Name = "textBox3"; + this.textBox3.ReadOnly = true; + // + // label2 + // + resources.ApplyResources(this.label2, "label2"); + this.label2.Name = "label2"; + // + // textBox4 + // + resources.ApplyResources(this.textBox4, "textBox4"); + this.textBox4.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textBox4.Name = "textBox4"; + this.textBox4.ReadOnly = true; + // + // ViewPrivateKeyDialog + // + this.AcceptButton = this.button1; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.button1; + this.Controls.Add(this.textBox4); + this.Controls.Add(this.label2); + this.Controls.Add(this.textBox3); + this.Controls.Add(this.button1); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ViewPrivateKeyDialog"; + this.ShowInTaskbar = false; + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.TextBox textBox3; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox textBox4; + } +} diff --git a/src/Neo.GUI/GUI/ViewPrivateKeyDialog.es-ES.resx b/src/Neo.GUI/GUI/ViewPrivateKeyDialog.es-ES.resx new file mode 100644 index 0000000000..4c3e507b35 --- /dev/null +++ b/src/Neo.GUI/GUI/ViewPrivateKeyDialog.es-ES.resx @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 32, 11 + + + 65, 17 + + + Dirección: + + + Clave privada + + + Cerrar + + + 100, 11 + + + 470, 16 + + + 9, 36 + + + 88, 17 + + + Clave pública: + + + 100, 36 + + + 470, 16 + + + Ver clave pública + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ViewPrivateKeyDialog.resx b/src/Neo.GUI/GUI/ViewPrivateKeyDialog.resx new file mode 100644 index 0000000000..49368fb3f2 --- /dev/null +++ b/src/Neo.GUI/GUI/ViewPrivateKeyDialog.resx @@ -0,0 +1,408 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + True + + + + 29, 11 + + + 59, 17 + + + 0 + + + Address: + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 5 + + + + Top, Left, Right + + + 47, 22 + + + 494, 23 + + + 1 + + + textBox1 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 3 + + + Top, Left, Right + + + Top, Left, Right + + + 47, 51 + + + 494, 23 + + + 3 + + + textBox2 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + True + + + 8, 54 + + + 33, 17 + + + 2 + + + WIF: + + + label4 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 1 + + + True + + + 6, 25 + + + 35, 17 + + + 0 + + + HEX: + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 2 + + + 12, 73 + + + 558, 93 + + + 2 + + + Private Key + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 4 + + + Bottom, Right + + + 495, 172 + + + 75, 23 + + + 3 + + + close + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + Top, Left, Right + + + 94, 11 + + + 476, 16 + + + 4 + + + textBox3 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + True + + + 18, 36 + + + 70, 17 + + + 5 + + + Public Key: + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Top, Left, Right + + + 94, 36 + + + 476, 16 + + + 6 + + + textBox4 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 7, 17 + + + 582, 207 + + + 微软雅黑, 9pt + + + 3, 4, 3, 4 + + + CenterScreen + + + View Private Key + + + ViewPrivateKeyDialog + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/ViewPrivateKeyDialog.zh-Hans.resx b/src/Neo.GUI/GUI/ViewPrivateKeyDialog.zh-Hans.resx new file mode 100644 index 0000000000..aac178a792 --- /dev/null +++ b/src/Neo.GUI/GUI/ViewPrivateKeyDialog.zh-Hans.resx @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 18, 9 + + + 35, 17 + + + 地址: + + + 487, 23 + + + 487, 23 + + + 12, 53 + + + 540, 85 + + + 私钥 + + + 477, 153 + + + 关闭 + + + 59, 9 + + + 493, 16 + + + 18, 31 + + + 35, 17 + + + 公钥: + + + 59, 31 + + + 493, 16 + + + 564, 188 + + + 查看私钥 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/VotingDialog.Designer.cs b/src/Neo.GUI/GUI/VotingDialog.Designer.cs new file mode 100644 index 0000000000..0ce4b7ee30 --- /dev/null +++ b/src/Neo.GUI/GUI/VotingDialog.Designer.cs @@ -0,0 +1,111 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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.GUI +{ + partial class VotingDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(VotingDialog)); + this.label1 = new System.Windows.Forms.Label(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.button1 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // + // groupBox1 + // + resources.ApplyResources(this.groupBox1, "groupBox1"); + this.groupBox1.Controls.Add(this.textBox1); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.TabStop = false; + // + // textBox1 + // + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.AcceptsReturn = true; + this.textBox1.Name = "textBox1"; + // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.DialogResult = System.Windows.Forms.DialogResult.OK; + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + // + // button2 + // + resources.ApplyResources(this.button2, "button2"); + this.button2.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.button2.Name = "button2"; + this.button2.UseVisualStyleBackColor = true; + // + // VotingDialog + // + resources.ApplyResources(this, "$this"); + this.AcceptButton = this.button1; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.button2; + this.Controls.Add(this.button2); + this.Controls.Add(this.button1); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "VotingDialog"; + this.ShowInTaskbar = false; + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.TextBox textBox1; + } +} diff --git a/src/Neo.GUI/GUI/VotingDialog.cs b/src/Neo.GUI/GUI/VotingDialog.cs new file mode 100644 index 0000000000..d289cc48ca --- /dev/null +++ b/src/Neo.GUI/GUI/VotingDialog.cs @@ -0,0 +1,54 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// VotingDialog.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.Cryptography.ECC; +using Neo.IO; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.Wallets; +using System.Linq; +using System.Windows.Forms; + +namespace Neo.GUI +{ + internal partial class VotingDialog : Form + { + private readonly UInt160 script_hash; + + public byte[] GetScript() + { + ECPoint[] pubkeys = textBox1.Lines.Select(p => ECPoint.Parse(p, ECCurve.Secp256r1)).ToArray(); + using ScriptBuilder sb = new ScriptBuilder(); + sb.EmitDynamicCall(NativeContract.NEO.Hash, "vote", new ContractParameter + { + Type = ContractParameterType.Hash160, + Value = script_hash + }, new ContractParameter + { + Type = ContractParameterType.Array, + Value = pubkeys.Select(p => new ContractParameter + { + Type = ContractParameterType.PublicKey, + Value = p + }).ToArray() + }); + return sb.ToArray(); + } + + public VotingDialog(UInt160 script_hash) + { + InitializeComponent(); + this.script_hash = script_hash; + label1.Text = script_hash.ToAddress(Program.Service.NeoSystem.Settings.AddressVersion); + } + } +} diff --git a/src/Neo.GUI/GUI/VotingDialog.es-ES.resx b/src/Neo.GUI/GUI/VotingDialog.es-ES.resx new file mode 100644 index 0000000000..e2afed46e3 --- /dev/null +++ b/src/Neo.GUI/GUI/VotingDialog.es-ES.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Candidatos + + + Aceptar + + + Cancelar + + + Votación + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/VotingDialog.resx b/src/Neo.GUI/GUI/VotingDialog.resx new file mode 100644 index 0000000000..2416392148 --- /dev/null +++ b/src/Neo.GUI/GUI/VotingDialog.resx @@ -0,0 +1,300 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Left, Right + + + + 微软雅黑, 11pt + + + 12, 23 + + + 562, 39 + + + + 0 + + + label1 + + + MiddleCenter + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + Top, Bottom, Left, Right + + + Fill + + + Lucida Console, 9pt + + + 3, 19 + + + True + + + Vertical + + + 556, 368 + + + 0 + + + False + + + textBox1 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBox1 + + + 0 + + + 12, 65 + + + 562, 390 + + + 1 + + + Candidates + + + groupBox1 + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + Bottom, Right + + + 418, 461 + + + 75, 23 + + + 2 + + + OK + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Bottom, Right + + + 499, 461 + + + 75, 23 + + + 3 + + + Cancel + + + button2 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 7, 17 + + + 586, 496 + + + 微软雅黑, 9pt + + + 3, 4, 3, 4 + + + CenterScreen + + + Voting + + + VotingDialog + + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/VotingDialog.zh-Hans.resx b/src/Neo.GUI/GUI/VotingDialog.zh-Hans.resx new file mode 100644 index 0000000000..e41916cae4 --- /dev/null +++ b/src/Neo.GUI/GUI/VotingDialog.zh-Hans.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 候选人 + + + 确定 + + + 取消 + + + 投票 + + \ No newline at end of file diff --git a/src/Neo.GUI/GUI/Wrappers/HexConverter.cs b/src/Neo.GUI/GUI/Wrappers/HexConverter.cs new file mode 100644 index 0000000000..757bfd3b97 --- /dev/null +++ b/src/Neo.GUI/GUI/Wrappers/HexConverter.cs @@ -0,0 +1,49 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// HexConverter.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; +using System.ComponentModel; +using System.Globalization; + +namespace Neo.GUI.Wrappers +{ + internal class HexConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof(string)) + return true; + return false; + } + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + if (destinationType == typeof(string)) + return true; + return false; + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string s) + return s.HexToBytes(); + throw new NotSupportedException(); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + if (destinationType != typeof(string)) + throw new NotSupportedException(); + if (!(value is byte[] array)) return null; + return array.ToHexString(); + } + } +} diff --git a/src/Neo.GUI/GUI/Wrappers/ScriptEditor.cs b/src/Neo.GUI/GUI/Wrappers/ScriptEditor.cs new file mode 100644 index 0000000000..20bc894786 --- /dev/null +++ b/src/Neo.GUI/GUI/Wrappers/ScriptEditor.cs @@ -0,0 +1,36 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ScriptEditor.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; +using System.ComponentModel; +using System.IO; +using System.Windows.Forms; +using System.Windows.Forms.Design; + +namespace Neo.GUI.Wrappers +{ + internal class ScriptEditor : FileNameEditor + { + public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) + { + string path = (string)base.EditValue(context, provider, null); + if (path == null) return null; + return File.ReadAllBytes(path); + } + + protected override void InitializeDialog(OpenFileDialog openFileDialog) + { + base.InitializeDialog(openFileDialog); + openFileDialog.DefaultExt = "avm"; + openFileDialog.Filter = "NeoContract|*.avm"; + } + } +} diff --git a/src/Neo.GUI/GUI/Wrappers/SignerWrapper.cs b/src/Neo.GUI/GUI/Wrappers/SignerWrapper.cs new file mode 100644 index 0000000000..e022ae7746 --- /dev/null +++ b/src/Neo.GUI/GUI/Wrappers/SignerWrapper.cs @@ -0,0 +1,38 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// SignerWrapper.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.Cryptography.ECC; +using Neo.Network.P2P.Payloads; +using System.Collections.Generic; +using System.ComponentModel; + +namespace Neo.GUI.Wrappers +{ + internal class SignerWrapper + { + [TypeConverter(typeof(UIntBaseConverter))] + public UInt160 Account { get; set; } + public WitnessScope Scopes { get; set; } + public List AllowedContracts { get; set; } = new List(); + public List AllowedGroups { get; set; } = new List(); + + public Signer Unwrap() + { + return new Signer + { + Account = Account, + Scopes = Scopes, + AllowedContracts = AllowedContracts.ToArray(), + AllowedGroups = AllowedGroups.ToArray() + }; + } + } +} diff --git a/src/Neo.GUI/GUI/Wrappers/TransactionAttributeWrapper.cs b/src/Neo.GUI/GUI/Wrappers/TransactionAttributeWrapper.cs new file mode 100644 index 0000000000..a348eb98d1 --- /dev/null +++ b/src/Neo.GUI/GUI/Wrappers/TransactionAttributeWrapper.cs @@ -0,0 +1,30 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TransactionAttributeWrapper.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.IO; +using Neo.Network.P2P.Payloads; +using System.ComponentModel; + +namespace Neo.GUI.Wrappers +{ + internal class TransactionAttributeWrapper + { + public TransactionAttributeType Usage { get; set; } + [TypeConverter(typeof(HexConverter))] + public byte[] Data { get; set; } + + public TransactionAttribute Unwrap() + { + MemoryReader reader = new(Data); + return TransactionAttribute.DeserializeFrom(ref reader); + } + } +} diff --git a/src/Neo.GUI/GUI/Wrappers/TransactionWrapper.cs b/src/Neo.GUI/GUI/Wrappers/TransactionWrapper.cs new file mode 100644 index 0000000000..86e84e9c87 --- /dev/null +++ b/src/Neo.GUI/GUI/Wrappers/TransactionWrapper.cs @@ -0,0 +1,59 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TransactionWrapper.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.Network.P2P.Payloads; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing.Design; +using System.Linq; + +namespace Neo.GUI.Wrappers +{ + internal class TransactionWrapper + { + [Category("Basic")] + public byte Version { get; set; } + [Category("Basic")] + public uint Nonce { get; set; } + [Category("Basic")] + public List Signers { get; set; } + [Category("Basic")] + public long SystemFee { get; set; } + [Category("Basic")] + public long NetworkFee { get; set; } + [Category("Basic")] + public uint ValidUntilBlock { get; set; } + [Category("Basic")] + public List Attributes { get; set; } = new List(); + [Category("Basic")] + [Editor(typeof(ScriptEditor), typeof(UITypeEditor))] + [TypeConverter(typeof(HexConverter))] + public byte[] Script { get; set; } + [Category("Basic")] + public List Witnesses { get; set; } = new List(); + + public Transaction Unwrap() + { + return new Transaction + { + Version = Version, + Nonce = Nonce, + Signers = Signers.Select(p => p.Unwrap()).ToArray(), + SystemFee = SystemFee, + NetworkFee = NetworkFee, + ValidUntilBlock = ValidUntilBlock, + Attributes = Attributes.Select(p => p.Unwrap()).ToArray(), + Script = Script, + Witnesses = Witnesses.Select(p => p.Unwrap()).ToArray() + }; + } + } +} diff --git a/src/Neo.GUI/GUI/Wrappers/UIntBaseConverter.cs b/src/Neo.GUI/GUI/Wrappers/UIntBaseConverter.cs new file mode 100644 index 0000000000..2def7ea4ab --- /dev/null +++ b/src/Neo.GUI/GUI/Wrappers/UIntBaseConverter.cs @@ -0,0 +1,54 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UIntBaseConverter.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; +using System.ComponentModel; +using System.Globalization; + +namespace Neo.GUI.Wrappers +{ + internal class UIntBaseConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof(string)) + return true; + return false; + } + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + if (destinationType == typeof(string)) + return true; + return false; + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string s) + return context.PropertyDescriptor.PropertyType.GetMethod("Parse").Invoke(null, new[] { s }); + throw new NotSupportedException(); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + if (destinationType != typeof(string)) + throw new NotSupportedException(); + + return value switch + { + UInt160 i => i.ToString(), + UInt256 i => i.ToString(), + _ => null, + }; + } + } +} diff --git a/src/Neo.GUI/GUI/Wrappers/WitnessWrapper.cs b/src/Neo.GUI/GUI/Wrappers/WitnessWrapper.cs new file mode 100644 index 0000000000..1c78a34dd1 --- /dev/null +++ b/src/Neo.GUI/GUI/Wrappers/WitnessWrapper.cs @@ -0,0 +1,36 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// WitnessWrapper.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.Network.P2P.Payloads; +using System.ComponentModel; +using System.Drawing.Design; + +namespace Neo.GUI.Wrappers +{ + internal class WitnessWrapper + { + [Editor(typeof(ScriptEditor), typeof(UITypeEditor))] + [TypeConverter(typeof(HexConverter))] + public byte[] InvocationScript { get; set; } + [Editor(typeof(ScriptEditor), typeof(UITypeEditor))] + [TypeConverter(typeof(HexConverter))] + public byte[] VerificationScript { get; set; } + + public Witness Unwrap() + { + return new Witness + { + InvocationScript = InvocationScript, + VerificationScript = VerificationScript + }; + } + } +} diff --git a/src/Neo.GUI/IO/Actors/EventWrapper.cs b/src/Neo.GUI/IO/Actors/EventWrapper.cs new file mode 100644 index 0000000000..67c488dc28 --- /dev/null +++ b/src/Neo.GUI/IO/Actors/EventWrapper.cs @@ -0,0 +1,43 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// EventWrapper.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 Akka.Actor; +using System; + +namespace Neo.IO.Actors +{ + internal class EventWrapper : UntypedActor + { + private readonly Action callback; + + public EventWrapper(Action callback) + { + this.callback = callback; + Context.System.EventStream.Subscribe(Self, typeof(T)); + } + + protected override void OnReceive(object message) + { + if (message is T obj) callback(obj); + } + + protected override void PostStop() + { + Context.System.EventStream.Unsubscribe(Self); + base.PostStop(); + } + + public static Props Props(Action callback) + { + return Akka.Actor.Props.Create(() => new EventWrapper(callback)); + } + } +} diff --git a/src/Neo.GUI/Neo.GUI.csproj b/src/Neo.GUI/Neo.GUI.csproj new file mode 100644 index 0000000000..7b1a8bc981 --- /dev/null +++ b/src/Neo.GUI/Neo.GUI.csproj @@ -0,0 +1,64 @@ + + + + 2016-2023 The Neo Project + Neo.GUI + WinExe + net7.0-windows + true + Neo + true + Neo.GUI + neo.ico + + + + + + + + + + + + + DeveloperToolsForm.cs + + + DeveloperToolsForm.cs + + + True + True + Resources.resx + + + True + True + Strings.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + ResXFileCodeGenerator + Strings.Designer.cs + + + Strings.resx + + + Strings.resx + + + + + + + + + \ No newline at end of file diff --git a/src/Neo.GUI/Program.cs b/src/Neo.GUI/Program.cs new file mode 100644 index 0000000000..1317c4c793 --- /dev/null +++ b/src/Neo.GUI/Program.cs @@ -0,0 +1,96 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Program.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.CLI; +using Neo.GUI; +using Neo.SmartContract.Native; +using System; +using System.IO; +using System.Reflection; +using System.Windows.Forms; +using System.Xml.Linq; + +namespace Neo +{ + static class Program + { + public static MainService Service = new MainService(); + public static MainForm MainForm; + public static UInt160[] NEP5Watched = { NativeContract.NEO.Hash, NativeContract.GAS.Hash }; + + private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + using FileStream fs = new FileStream("error.log", FileMode.Create, FileAccess.Write, FileShare.None); + using StreamWriter w = new StreamWriter(fs); + if (e.ExceptionObject is Exception ex) + { + PrintErrorLogs(w, ex); + } + else + { + w.WriteLine(e.ExceptionObject.GetType()); + w.WriteLine(e.ExceptionObject); + } + } + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main(string[] args) + { + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + Application.SetHighDpiMode(HighDpiMode.SystemAware); + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + XDocument xdoc = null; + try + { + xdoc = XDocument.Load("https://raw.githubusercontent.com/neo-project/neo-gui/master/update.xml"); + } + catch { } + if (xdoc != null) + { + Version version = Assembly.GetExecutingAssembly().GetName().Version; + Version minimum = Version.Parse(xdoc.Element("update").Attribute("minimum").Value); + if (version < minimum) + { + using UpdateDialog dialog = new UpdateDialog(xdoc); + dialog.ShowDialog(); + return; + } + } + Service.OnStartWithCommandLine(args); + Application.Run(MainForm = new MainForm(xdoc)); + Service.Stop(); + } + + private static void PrintErrorLogs(StreamWriter writer, Exception ex) + { + writer.WriteLine(ex.GetType()); + writer.WriteLine(ex.Message); + writer.WriteLine(ex.StackTrace); + if (ex is AggregateException ex2) + { + foreach (Exception inner in ex2.InnerExceptions) + { + writer.WriteLine(); + PrintErrorLogs(writer, inner); + } + } + else if (ex.InnerException != null) + { + writer.WriteLine(); + PrintErrorLogs(writer, ex.InnerException); + } + } + } +} diff --git a/src/Neo.GUI/Properties/Resources.Designer.cs b/src/Neo.GUI/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..4cbcca4fd2 --- /dev/null +++ b/src/Neo.GUI/Properties/Resources.Designer.cs @@ -0,0 +1,133 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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. + +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace Neo.Properties { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Neo.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap add { + get { + object obj = ResourceManager.GetObject("add", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap add2 { + get { + object obj = ResourceManager.GetObject("add2", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap remark { + get { + object obj = ResourceManager.GetObject("remark", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap remove { + get { + object obj = ResourceManager.GetObject("remove", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap search { + get { + object obj = ResourceManager.GetObject("search", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// 查找 System.Byte[] 类型的本地化资源。 + /// + internal static byte[] UpdateBat { + get { + object obj = ResourceManager.GetObject("UpdateBat", resourceCulture); + return ((byte[])(obj)); + } + } + } +} diff --git a/src/Neo.GUI/Properties/Resources.resx b/src/Neo.GUI/Properties/Resources.resx new file mode 100644 index 0000000000..40ca55734d --- /dev/null +++ b/src/Neo.GUI/Properties/Resources.resx @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\add2.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\remark.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\remove.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\search.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\update.bat;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Neo.GUI/Properties/Strings.Designer.cs b/src/Neo.GUI/Properties/Strings.Designer.cs new file mode 100644 index 0000000000..ceafe6ac3a --- /dev/null +++ b/src/Neo.GUI/Properties/Strings.Designer.cs @@ -0,0 +1,542 @@ +// Copyright (C) 2016-2023 The Neo Project. +// +// The neo-gui is free software distributed under the MIT software +// license, see the accompanying file LICENSE in the main directory of +// the project 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. + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Neo.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Strings { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Strings() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Neo.Properties.Strings", typeof(Strings).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to About. + /// + internal static string About { + get { + return ResourceManager.GetString("About", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to NEO. + /// + internal static string AboutMessage { + get { + return ResourceManager.GetString("AboutMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Version:. + /// + internal static string AboutVersion { + get { + return ResourceManager.GetString("AboutVersion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to add smart contract, corresponding private key missing in this wallet.. + /// + internal static string AddContractFailedMessage { + get { + return ResourceManager.GetString("AddContractFailedMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Address. + /// + internal static string Address { + get { + return ResourceManager.GetString("Address", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cancel. + /// + internal static string Cancel { + get { + return ResourceManager.GetString("Cancel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change password successful.. + /// + internal static string ChangePasswordSuccessful { + get { + return ResourceManager.GetString("ChangePasswordSuccessful", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Confirm. + /// + internal static string Confirm { + get { + return ResourceManager.GetString("Confirm", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to will be consumed, confirm?. + /// + internal static string CostTips { + get { + return ResourceManager.GetString("CostTips", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cost Warning. + /// + internal static string CostTitle { + get { + return ResourceManager.GetString("CostTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Confirmation. + /// + internal static string DeleteAddressConfirmationCaption { + get { + return ResourceManager.GetString("DeleteAddressConfirmationCaption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Upon deletion, assets in these addresses will be permanently lost, are you sure to proceed?. + /// + internal static string DeleteAddressConfirmationMessage { + get { + return ResourceManager.GetString("DeleteAddressConfirmationMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Assets cannot be recovered once deleted, are you sure to delete the assets?. + /// + internal static string DeleteAssetConfirmationMessage { + get { + return ResourceManager.GetString("DeleteAssetConfirmationMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Confirmation. + /// + internal static string DeleteConfirmation { + get { + return ResourceManager.GetString("DeleteConfirmation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enter remark here, which will be recorded on the blockchain. + /// + internal static string EnterRemarkMessage { + get { + return ResourceManager.GetString("EnterRemarkMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Transaction Remark. + /// + internal static string EnterRemarkTitle { + get { + return ResourceManager.GetString("EnterRemarkTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Execution terminated in fault state.. + /// + internal static string ExecutionFailed { + get { + return ResourceManager.GetString("ExecutionFailed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Expired. + /// + internal static string ExpiredCertificate { + get { + return ResourceManager.GetString("ExpiredCertificate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed. + /// + internal static string Failed { + get { + return ResourceManager.GetString("Failed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to High Priority Transaction. + /// + internal static string HighPriority { + get { + return ResourceManager.GetString("HighPriority", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Import Watch-Only Address. + /// + internal static string ImportWatchOnlyAddress { + get { + return ResourceManager.GetString("ImportWatchOnlyAddress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Transaction initiated, but the signature is incomplete.. + /// + internal static string IncompletedSignatureMessage { + get { + return ResourceManager.GetString("IncompletedSignatureMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Incomplete signature. + /// + internal static string IncompletedSignatureTitle { + get { + return ResourceManager.GetString("IncompletedSignatureTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You have cancelled the certificate installation.. + /// + internal static string InstallCertificateCancel { + get { + return ResourceManager.GetString("InstallCertificateCancel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Install the certificate. + /// + internal static string InstallCertificateCaption { + get { + return ResourceManager.GetString("InstallCertificateCaption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to NEO must install Onchain root certificate to validate assets on the blockchain, install it now?. + /// + internal static string InstallCertificateText { + get { + return ResourceManager.GetString("InstallCertificateText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Insufficient funds, transaction cannot be initiated.. + /// + internal static string InsufficientFunds { + get { + return ResourceManager.GetString("InsufficientFunds", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid. + /// + internal static string InvalidCertificate { + get { + return ResourceManager.GetString("InvalidCertificate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Migrate Wallet. + /// + internal static string MigrateWalletCaption { + get { + return ResourceManager.GetString("MigrateWalletCaption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opening wallet files in older versions, update to newest format? + ///Note: updated files cannot be openned by clients in older versions!. + /// + internal static string MigrateWalletMessage { + get { + return ResourceManager.GetString("MigrateWalletMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Wallet file relocated. New wallet file has been saved at: . + /// + internal static string MigrateWalletSucceedMessage { + get { + return ResourceManager.GetString("MigrateWalletSucceedMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Password Incorrect. + /// + internal static string PasswordIncorrect { + get { + return ResourceManager.GetString("PasswordIncorrect", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Data broadcast success, the hash is shown as follows:. + /// + internal static string RelaySuccessText { + get { + return ResourceManager.GetString("RelaySuccessText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Broadcast Success. + /// + internal static string RelaySuccessTitle { + get { + return ResourceManager.GetString("RelaySuccessTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Raw:. + /// + internal static string RelayTitle { + get { + return ResourceManager.GetString("RelayTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Transaction sent, TXID:. + /// + internal static string SendTxSucceedMessage { + get { + return ResourceManager.GetString("SendTxSucceedMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Transaction successful. + /// + internal static string SendTxSucceedTitle { + get { + return ResourceManager.GetString("SendTxSucceedTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The private key that can sign the data is not found.. + /// + internal static string SigningFailedKeyNotFoundMessage { + get { + return ResourceManager.GetString("SigningFailedKeyNotFoundMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You must input JSON object pending signature data.. + /// + internal static string SigningFailedNoDataMessage { + get { + return ResourceManager.GetString("SigningFailedNoDataMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to System. + /// + internal static string SystemIssuer { + get { + return ResourceManager.GetString("SystemIssuer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Validation failed, the counterparty falsified the transaction content!. + /// + internal static string TradeFailedFakeDataMessage { + get { + return ResourceManager.GetString("TradeFailedFakeDataMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Validation failed, the counterparty generated illegal transaction content!. + /// + internal static string TradeFailedInvalidDataMessage { + get { + return ResourceManager.GetString("TradeFailedInvalidDataMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Validation failed, invalid transaction or unsynchronized blockchain, please try again when synchronized!. + /// + internal static string TradeFailedNoSyncMessage { + get { + return ResourceManager.GetString("TradeFailedNoSyncMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Need Signature. + /// + internal static string TradeNeedSignatureCaption { + get { + return ResourceManager.GetString("TradeNeedSignatureCaption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Transaction generated, please send the following information to the counterparty for signing:. + /// + internal static string TradeNeedSignatureMessage { + get { + return ResourceManager.GetString("TradeNeedSignatureMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Trade Request. + /// + internal static string TradeRequestCreatedCaption { + get { + return ResourceManager.GetString("TradeRequestCreatedCaption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Transaction request generated, please send it to the counterparty or merge it with the counterparty's request.. + /// + internal static string TradeRequestCreatedMessage { + get { + return ResourceManager.GetString("TradeRequestCreatedMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Trade Success. + /// + internal static string TradeSuccessCaption { + get { + return ResourceManager.GetString("TradeSuccessCaption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Transaction sent, this is the TXID:. + /// + internal static string TradeSuccessMessage { + get { + return ResourceManager.GetString("TradeSuccessMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to unconfirmed. + /// + internal static string Unconfirmed { + get { + return ResourceManager.GetString("Unconfirmed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to unknown issuer. + /// + internal static string UnknownIssuer { + get { + return ResourceManager.GetString("UnknownIssuer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Blockchain unsynchronized, transaction cannot be sent.. + /// + internal static string UnsynchronizedBlock { + get { + return ResourceManager.GetString("UnsynchronizedBlock", resourceCulture); + } + } + } +} diff --git a/src/Neo.GUI/Properties/Strings.es-Es.resx b/src/Neo.GUI/Properties/Strings.es-Es.resx new file mode 100644 index 0000000000..c3ab2fa426 --- /dev/null +++ b/src/Neo.GUI/Properties/Strings.es-Es.resx @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Acerca de + + + NEO + + + Versión: + + + Fallo al añadir el contrato inteligente. Falta la correspondiente clave privada en el monedero. + + + Dirección + + + Contraseña cambiada con éxito. + + + Confirmación + + + Una vez eliminados, los activos de estas direcciones se perderán permanentemente. ¿Deseas continuar? + + + Los activos no se pueden recuperar una vez eliminados. ¿Deseas eliminarlos? + + + Confirmación + + + Notas de la transacción que se grabará en la blockchain. + + + Notas de la transacción + + + La ejecución terminó con un estado de error. + + + Caducado + + + Falló + + + Importar dirección sólo lectura + + + Transacción iniciada aunque la firma está incompleta. + + + Firma incompleta + + + Instalación del certificado cancelada. + + + Instalar certificado + + + NEO debe instalar el certificado raíz de Onchain para validar activos en la blockchain. ¿Instalar ahora? + + + Fondos insuficientes, la transacción no se puede iniciar. + + + Inválido + + + Migrar monedero + + + Abriendo ficheros de monederos antiguos, actualizar al nuevo formato? +Aviso: los ficheros actualizados no podran ser abiertos por clientes de versiones antiguas. + + + Contraseña incorrecta + + + Datos emitidos con éxito. El hash se muestra como sigue: + + + Emisión realizada con éxito + + + Raw: + + + Transacción enviada, TXID: + + + Transacción realizada con éxito + + + Falta la clave privada para firmar los datos. + + + Debes introducir el objeto JSON de los datos pendientes de firmar. + + + System + + + ¡Falló la validación! El contratante falsificó el contenido de la transacción. + + + ¡Falló la validación! El contratante generó una transacción con contenido ilegal. + + + ¡Falló la validación! Transacción no válida o blockchain sin sincronizar. Inténtalo de nuevo después de sincronizar. + + + Firma necesaria. + + + Transacción generada. Por favor, envia la siguiente información al contratante para su firma: + + + Solicitud de transacción. + + + Solicitud de transacción generada. Por favor, enviala al contratante o incorporala a la solicitud del contratante. + + + Transacción realizada con éxito. + + + Transacción enviada, este es el TXID: + + + Sin confirmar. + + + Emisor desconocido. + + + Blockchain sin sincronizar, la transacción no puede ser enviada. + + \ No newline at end of file diff --git a/src/Neo.GUI/Properties/Strings.resx b/src/Neo.GUI/Properties/Strings.resx new file mode 100644 index 0000000000..95a3fced89 --- /dev/null +++ b/src/Neo.GUI/Properties/Strings.resx @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + About + + + NEO + + + Version: + + + Failed to add smart contract, corresponding private key missing in this wallet. + + + Address + + + Cancel + + + Change password successful. + + + Confirm + + + will be consumed, confirm? + + + Cost Warning + + + Confirmation + + + Upon deletion, assets in these addresses will be permanently lost, are you sure to proceed? + + + Assets cannot be recovered once deleted, are you sure to delete the assets? + + + Confirmation + + + Enter remark here, which will be recorded on the blockchain + + + Transaction Remark + + + Execution terminated in fault state. + + + Expired + + + Failed + + + High Priority Transaction + + + Import Watch-Only Address + + + Transaction initiated, but the signature is incomplete. + + + Incomplete signature + + + You have cancelled the certificate installation. + + + Install the certificate + + + NEO must install Onchain root certificate to validate assets on the blockchain, install it now? + + + Insufficient funds, transaction cannot be initiated. + + + Invalid + + + Migrate Wallet + + + Opening wallet files in older versions, update to newest format? +Note: updated files cannot be openned by clients in older versions! + + + Wallet file relocated. New wallet file has been saved at: + + + Password Incorrect + + + Data broadcast success, the hash is shown as follows: + + + Broadcast Success + + + Raw: + + + Transaction sent, TXID: + + + Transaction successful + + + The private key that can sign the data is not found. + + + You must input JSON object pending signature data. + + + System + + + Validation failed, the counterparty falsified the transaction content! + + + Validation failed, the counterparty generated illegal transaction content! + + + Validation failed, invalid transaction or unsynchronized blockchain, please try again when synchronized! + + + Need Signature + + + Transaction generated, please send the following information to the counterparty for signing: + + + Trade Request + + + Transaction request generated, please send it to the counterparty or merge it with the counterparty's request. + + + Trade Success + + + Transaction sent, this is the TXID: + + + unconfirmed + + + unknown issuer + + + Blockchain unsynchronized, transaction cannot be sent. + + \ No newline at end of file diff --git a/src/Neo.GUI/Properties/Strings.zh-Hans.resx b/src/Neo.GUI/Properties/Strings.zh-Hans.resx new file mode 100644 index 0000000000..678c4f324d --- /dev/null +++ b/src/Neo.GUI/Properties/Strings.zh-Hans.resx @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 关于 + + + NEO + + + 版本: + + + 无法添加智能合约,因为当前钱包中不包含签署该合约的私钥。 + + + 地址 + + + 取消 + + + 修改密码成功。 + + + 确认 + + + 费用即将被消耗,确认? + + + 消费提示 + + + 删除地址确认 + + + 删除地址后,这些地址中的资产将永久性地丢失,确认要继续吗? + + + 资产删除后将无法恢复,您确定要删除以下资产吗? + + + 删除确认 + + + 请输入备注信息,该信息将被记录在区块链上 + + + 交易备注 + + + 合约执行遇到错误并退出。 + + + 证书已过期 + + + 失败 + + + 优先交易 + + + 导入监视地址 + + + 交易构造完成,但没有足够的签名: + + + 签名不完整 + + + 您已取消了证书安装过程。 + + + 安装证书 + + + NEO需要安装Onchain的根证书才能对区块链上的资产进行认证,是否现在就安装证书? + + + 余额不足,无法创建交易。 + + + 证书错误 + + + 钱包文件升级 + + + 正在打开旧版本的钱包文件,是否尝试将文件升级为新版格式? +注意,升级后将无法用旧版本的客户端打开该文件! + + + 钱包文件迁移成功,新的钱包文件已经自动保存到以下位置: + + + 密码错误! + + + 数据广播成功,这是广播数据的散列值: + + + 广播成功 + + + 原始数据: + + + 交易已发送,这是交易编号(TXID): + + + 交易成功 + + + 没有找到可以签署该数据的私钥。 + + + 必须输入一段含有待签名数据的JSON对象。 + + + NEO系统 + + + 验证失败,对方篡改了交易内容! + + + 验证失败,对方构造了非法的交易内容! + + + 验证失败,交易无效或者区块链未同步完成,请同步后再试! + + + 签名不完整 + + + 交易构造完成,请将以下信息发送给对方进行签名: + + + 交易请求 + + + 交易请求已生成,请发送给对方,或与对方的请求合并: + + + 交易成功 + + + 交易已发送,这是交易编号(TXID): + + + 未确认 + + + 未知发行者 + + + 区块链未同步完成,无法发送该交易。 + + \ No newline at end of file diff --git a/src/Neo.GUI/Resources/add.png b/src/Neo.GUI/Resources/add.png new file mode 100644 index 0000000000..08816d6519 Binary files /dev/null and b/src/Neo.GUI/Resources/add.png differ diff --git a/src/Neo.GUI/Resources/add2.png b/src/Neo.GUI/Resources/add2.png new file mode 100644 index 0000000000..9f77afc279 Binary files /dev/null and b/src/Neo.GUI/Resources/add2.png differ diff --git a/src/Neo.GUI/Resources/remark.png b/src/Neo.GUI/Resources/remark.png new file mode 100644 index 0000000000..c26fe7be39 Binary files /dev/null and b/src/Neo.GUI/Resources/remark.png differ diff --git a/src/Neo.GUI/Resources/remove.png b/src/Neo.GUI/Resources/remove.png new file mode 100644 index 0000000000..a99083bd70 Binary files /dev/null and b/src/Neo.GUI/Resources/remove.png differ diff --git a/src/Neo.GUI/Resources/search.png b/src/Neo.GUI/Resources/search.png new file mode 100644 index 0000000000..fb951a1276 Binary files /dev/null and b/src/Neo.GUI/Resources/search.png differ diff --git a/src/Neo.GUI/Resources/update.bat b/src/Neo.GUI/Resources/update.bat new file mode 100644 index 0000000000..fff10101e1 --- /dev/null +++ b/src/Neo.GUI/Resources/update.bat @@ -0,0 +1,13 @@ +@echo off +set "taskname=neo-gui.exe" +echo waiting... +:wait +ping 127.0.1 -n 3 >nul +tasklist | find "%taskname%" /i >nul 2>nul +if "%errorlevel%" NEQ "1" goto wait +echo updating... +copy /Y update\* * +rmdir /S /Q update +del /F /Q update.zip +start %taskname% +del /F /Q update.bat diff --git a/src/Neo.GUI/neo.ico b/src/Neo.GUI/neo.ico new file mode 100644 index 0000000000..141d11d686 Binary files /dev/null and b/src/Neo.GUI/neo.ico differ diff --git a/src/Neo.IO/ByteArrayComparer.cs b/src/Neo.IO/ByteArrayComparer.cs new file mode 100644 index 0000000000..f9d44e24d6 --- /dev/null +++ b/src/Neo.IO/ByteArrayComparer.cs @@ -0,0 +1,54 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ByteArrayComparer.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; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Neo.IO +{ + internal class ByteArrayComparer : IComparer + { + public static readonly ByteArrayComparer Default = new(1); + public static readonly ByteArrayComparer Reverse = new(-1); + + private readonly int _direction; + + private ByteArrayComparer(int direction) + { + _direction = direction; + } + + public int Compare(byte[]? x, byte[]? y) + { + if (x == y) return 0; + if (x is null && y is not null) + return _direction > 0 ? -y.Length : y.Length; + if (y is null && x is not null) + return _direction > 0 ? x.Length : -x.Length; + return _direction > 0 ? + CompareInternal(x!, y!) : + -CompareInternal(x!, y!); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int CompareInternal(byte[] x, byte[] y) + { + var length = Math.Min(x.Length, y.Length); + for (var i = 0; i < length; i++) + { + var r = x[i].CompareTo(y[i]); + if (r != 0) return r; + } + return x.Length.CompareTo(y.Length); + } + } +} diff --git a/src/neo/IO/ByteArrayEqualityComparer.cs b/src/Neo.IO/ByteArrayEqualityComparer.cs similarity index 73% rename from src/neo/IO/ByteArrayEqualityComparer.cs rename to src/Neo.IO/ByteArrayEqualityComparer.cs index b069d87228..2b6f01491c 100644 --- a/src/neo/IO/ByteArrayEqualityComparer.cs +++ b/src/Neo.IO/ByteArrayEqualityComparer.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ByteArrayEqualityComparer.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. @@ -16,11 +17,11 @@ internal class ByteArrayEqualityComparer : IEqualityComparer { public static readonly ByteArrayEqualityComparer Default = new(); - public unsafe bool Equals(byte[] x, byte[] y) + public unsafe bool Equals(byte[]? x, byte[]? y) { if (ReferenceEquals(x, y)) return true; if (x is null || y is null) return false; - int len = x.Length; + var len = x.Length; if (len != y.Length) return false; if (len == 0) return true; fixed (byte* xp = x, yp = y) @@ -47,8 +48,8 @@ public int GetHashCode(byte[] obj) { unchecked { - int hash = 17; - foreach (byte element in obj) + var hash = 17; + foreach (var element in obj) hash = hash * 31 + element; return hash; } diff --git a/src/neo/IO/ISerializable.cs b/src/Neo.IO/ISerializable.cs similarity index 65% rename from src/neo/IO/ISerializable.cs rename to src/Neo.IO/ISerializable.cs index 1f361630db..3d90009a09 100644 --- a/src/neo/IO/ISerializable.cs +++ b/src/Neo.IO/ISerializable.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ISerializable.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. @@ -29,9 +30,9 @@ public interface ISerializable void Serialize(BinaryWriter writer); /// - /// Deserializes the object using the specified . + /// Deserializes the object using the specified . /// - /// The for reading data. - void Deserialize(BinaryReader reader); + /// The for reading data. + void Deserialize(ref MemoryReader reader); } } diff --git a/src/Neo.IO/MemoryReader.cs b/src/Neo.IO/MemoryReader.cs new file mode 100644 index 0000000000..df8afbb3d3 --- /dev/null +++ b/src/Neo.IO/MemoryReader.cs @@ -0,0 +1,240 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MemoryReader.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; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; + +namespace Neo.IO +{ + public ref struct MemoryReader + { + private readonly ReadOnlyMemory _memory; + private readonly ReadOnlySpan _span; + private int _pos = 0; + + public readonly int Position => _pos; + + public MemoryReader(ReadOnlyMemory memory) + { + _memory = memory; + _span = memory.Span; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private readonly void EnsurePosition(int move) + { + if (_pos + move > _span.Length) throw new FormatException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly byte Peek() + { + EnsurePosition(1); + return _span[_pos]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ReadBoolean() + { + return ReadByte() switch + { + 0 => false, + 1 => true, + _ => throw new FormatException() + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public sbyte ReadSByte() + { + EnsurePosition(1); + var b = _span[_pos++]; + return unchecked((sbyte)b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte ReadByte() + { + EnsurePosition(1); + return _span[_pos++]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public short ReadInt16() + { + EnsurePosition(sizeof(short)); + var result = BinaryPrimitives.ReadInt16LittleEndian(_span[_pos..]); + _pos += sizeof(short); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public short ReadInt16BigEndian() + { + EnsurePosition(sizeof(short)); + var result = BinaryPrimitives.ReadInt16BigEndian(_span[_pos..]); + _pos += sizeof(short); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ushort ReadUInt16() + { + EnsurePosition(sizeof(ushort)); + var result = BinaryPrimitives.ReadUInt16LittleEndian(_span[_pos..]); + _pos += sizeof(ushort); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ushort ReadUInt16BigEndian() + { + EnsurePosition(sizeof(ushort)); + var result = BinaryPrimitives.ReadUInt16BigEndian(_span[_pos..]); + _pos += sizeof(ushort); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadInt32() + { + EnsurePosition(sizeof(int)); + var result = BinaryPrimitives.ReadInt32LittleEndian(_span[_pos..]); + _pos += sizeof(int); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadInt32BigEndian() + { + EnsurePosition(sizeof(int)); + var result = BinaryPrimitives.ReadInt32BigEndian(_span[_pos..]); + _pos += sizeof(int); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public uint ReadUInt32() + { + EnsurePosition(sizeof(uint)); + var result = BinaryPrimitives.ReadUInt32LittleEndian(_span[_pos..]); + _pos += sizeof(uint); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public uint ReadUInt32BigEndian() + { + EnsurePosition(sizeof(uint)); + var result = BinaryPrimitives.ReadUInt32BigEndian(_span[_pos..]); + _pos += sizeof(uint); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long ReadInt64() + { + EnsurePosition(sizeof(long)); + var result = BinaryPrimitives.ReadInt64LittleEndian(_span[_pos..]); + _pos += sizeof(long); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long ReadInt64BigEndian() + { + EnsurePosition(sizeof(long)); + var result = BinaryPrimitives.ReadInt64BigEndian(_span[_pos..]); + _pos += sizeof(long); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong ReadUInt64() + { + EnsurePosition(sizeof(ulong)); + var result = BinaryPrimitives.ReadUInt64LittleEndian(_span[_pos..]); + _pos += sizeof(ulong); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong ReadUInt64BigEndian() + { + EnsurePosition(sizeof(ulong)); + var result = BinaryPrimitives.ReadUInt64BigEndian(_span[_pos..]); + _pos += sizeof(ulong); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong ReadVarInt(ulong max = ulong.MaxValue) + { + var b = ReadByte(); + var value = b switch + { + 0xfd => ReadUInt16(), + 0xfe => ReadUInt32(), + 0xff => ReadUInt64(), + _ => b + }; + if (value > max) throw new FormatException(); + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public string ReadFixedString(int length) + { + EnsurePosition(length); + var end = _pos + length; + var i = _pos; + while (i < end && _span[i] != 0) i++; + var data = _span[_pos..i]; + for (; i < end; i++) + if (_span[i] != 0) + throw new FormatException(); + _pos = end; + return Utility.StrictUTF8.GetString(data); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public string ReadVarString(int max = 0x1000000) + { + var length = (int)ReadVarInt((ulong)max); + EnsurePosition(length); + var data = _span.Slice(_pos, length); + _pos += length; + return Utility.StrictUTF8.GetString(data); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlyMemory ReadMemory(int count) + { + EnsurePosition(count); + var result = _memory.Slice(_pos, count); + _pos += count; + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlyMemory ReadVarMemory(int max = 0x1000000) => + ReadMemory((int)ReadVarInt((ulong)max)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlyMemory ReadToEnd() + { + var result = _memory[_pos..]; + _pos = _memory.Length; + return result; + } + } +} diff --git a/src/Neo.IO/Neo.IO.csproj b/src/Neo.IO/Neo.IO.csproj new file mode 100644 index 0000000000..05bc305a67 --- /dev/null +++ b/src/Neo.IO/Neo.IO.csproj @@ -0,0 +1,20 @@ + + + + netstandard2.1;net7.0 + true + enable + NEO;Blockchain;IO + + + + + + + + + + + + + diff --git a/src/neo/IO/Json/JArray.cs b/src/Neo.Json/JArray.cs similarity index 61% rename from src/neo/IO/Json/JArray.cs rename to src/Neo.Json/JArray.cs index eee381eda4..903f29941b 100644 --- a/src/neo/IO/Json/JArray.cs +++ b/src/Neo.Json/JArray.cs @@ -1,32 +1,31 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// JArray.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.Collections; -using System.Collections.Generic; -using System.Linq; using System.Text.Json; -namespace Neo.IO.Json +namespace Neo.Json { /// /// Represents a JSON array. /// - public class JArray : JObject, IList + public class JArray : JContainer, IList { - private readonly List items = new(); + private readonly List items = new(); /// /// Initializes a new instance of the class. /// /// The initial items in the array. - public JArray(params JObject[] items) : this((IEnumerable)items) + public JArray(params JToken?[] items) : this((IEnumerable)items) { } @@ -34,12 +33,12 @@ public JArray(params JObject[] items) : this((IEnumerable)items) /// Initializes a new instance of the class. /// /// The initial items in the array. - public JArray(IEnumerable items) + public JArray(IEnumerable items) { this.items.AddRange(items); } - public override JObject this[int index] + public override JToken? this[int index] { get { @@ -51,13 +50,7 @@ public override JObject this[int index] } } - public int Count - { - get - { - return items.Count; - } - } + public override IReadOnlyList Children => items; public bool IsReadOnly { @@ -67,7 +60,7 @@ public bool IsReadOnly } } - public void Add(JObject item) + public void Add(JToken? item) { items.Add(item); } @@ -77,24 +70,17 @@ public override string AsString() return string.Join(",", items.Select(p => p?.AsString())); } - public void Clear() + public override void Clear() { items.Clear(); } - public bool Contains(JObject item) + public bool Contains(JToken? item) { return items.Contains(item); } - public void CopyTo(JObject[] array, int arrayIndex) - { - items.CopyTo(array, arrayIndex); - } - - public override JArray GetArray() => this; - - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { return items.GetEnumerator(); } @@ -104,17 +90,17 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } - public int IndexOf(JObject item) + public int IndexOf(JToken? item) { return items.IndexOf(item); } - public void Insert(int index, JObject item) + public void Insert(int index, JToken? item) { items.Insert(index, item); } - public bool Remove(JObject item) + public bool Remove(JToken? item) { return items.Remove(item); } @@ -127,7 +113,7 @@ public void RemoveAt(int index) internal override void Write(Utf8JsonWriter writer) { writer.WriteStartArray(); - foreach (JObject item in items) + foreach (JToken? item in items) { if (item is null) writer.WriteNullValue(); @@ -137,19 +123,19 @@ internal override void Write(Utf8JsonWriter writer) writer.WriteEndArray(); } - public override JObject Clone() + public override JToken Clone() { var cloned = new JArray(); - foreach (JObject item in items) + foreach (JToken? item in items) { - cloned.Add(item.Clone()); + cloned.Add(item?.Clone()); } return cloned; } - public static implicit operator JArray(JObject[] value) + public static implicit operator JArray(JToken?[] value) { return new JArray(value); } diff --git a/src/neo/IO/Json/JBoolean.cs b/src/Neo.Json/JBoolean.cs similarity index 52% rename from src/neo/IO/Json/JBoolean.cs rename to src/Neo.Json/JBoolean.cs index acdd13649e..05cb36a89d 100644 --- a/src/neo/IO/Json/JBoolean.cs +++ b/src/Neo.Json/JBoolean.cs @@ -1,31 +1,32 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// JBoolean.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.Text.Json; -namespace Neo.IO.Json +namespace Neo.Json { /// /// Represents a JSON boolean value. /// - public class JBoolean : JObject + public class JBoolean : JToken { /// - /// Gets the value of the JSON object. + /// Gets the value of the JSON token. /// public bool Value { get; } /// /// Initializes a new instance of the class with the specified value. /// - /// The value of the JSON object. + /// The value of the JSON token. public JBoolean(bool value = false) { this.Value = value; @@ -37,7 +38,7 @@ public override bool AsBoolean() } /// - /// Converts the current JSON object to a floating point number. + /// Converts the current JSON token to a floating point number. /// /// The number 1 if value is ; otherwise, 0. public override double AsNumber() @@ -62,7 +63,7 @@ internal override void Write(Utf8JsonWriter writer) writer.WriteBooleanValue(Value); } - public override JObject Clone() + public override JToken Clone() { return this; } @@ -71,5 +72,33 @@ public static implicit operator JBoolean(bool value) { return new JBoolean(value); } + + public static bool operator ==(JBoolean left, JBoolean right) + { + return left.Value.Equals(right.Value); + } + + public static bool operator !=(JBoolean left, JBoolean right) + { + return !left.Value.Equals(right.Value); + } + + public override bool Equals(object? obj) + { + if (ReferenceEquals(this, obj)) + { + return true; + } + if (obj is JBoolean other) + { + return this.Value.Equals(other.Value); + } + return false; + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } } } diff --git a/src/Neo.Json/JContainer.cs b/src/Neo.Json/JContainer.cs new file mode 100644 index 0000000000..5cc1bc7263 --- /dev/null +++ b/src/Neo.Json/JContainer.cs @@ -0,0 +1,29 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JContainer.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.Json; + +public abstract class JContainer : JToken +{ + public override JToken? this[int index] => Children[index]; + + public abstract IReadOnlyList Children { get; } + + public int Count => Children.Count; + + public abstract void Clear(); + + public void CopyTo(JToken?[] array, int arrayIndex) + { + for (int i = 0; i < Count && i + arrayIndex < array.Length; i++) + array[i + arrayIndex] = Children[i]; + } +} diff --git a/src/Neo.Json/JNumber.cs b/src/Neo.Json/JNumber.cs new file mode 100644 index 0000000000..479303cfb5 --- /dev/null +++ b/src/Neo.Json/JNumber.cs @@ -0,0 +1,167 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JNumber.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.Globalization; +using System.Text.Json; + +namespace Neo.Json +{ + /// + /// Represents a JSON number. + /// + public class JNumber : JToken + { + /// + /// Represents the largest safe integer in JSON. + /// + public static readonly long MAX_SAFE_INTEGER = (long)Math.Pow(2, 53) - 1; + + /// + /// Represents the smallest safe integer in JSON. + /// + public static readonly long MIN_SAFE_INTEGER = -MAX_SAFE_INTEGER; + + /// + /// Gets the value of the JSON token. + /// + public double Value { get; } + + /// + /// Initializes a new instance of the class with the specified value. + /// + /// The value of the JSON token. + public JNumber(double value = 0) + { + if (!double.IsFinite(value)) throw new FormatException(); + this.Value = value; + } + + /// + /// Converts the current JSON token to a boolean value. + /// + /// if value is not zero; otherwise, . + public override bool AsBoolean() + { + return Value != 0; + } + + public override double AsNumber() + { + return Value; + } + + public override string AsString() + { + return Value.ToString(CultureInfo.InvariantCulture); + } + + public override double GetNumber() => Value; + + public override string ToString() + { + return AsString(); + } + + public override T AsEnum(T defaultValue = default, bool ignoreCase = false) + { + Type enumType = typeof(T); + object value; + try + { + value = Convert.ChangeType(Value, enumType.GetEnumUnderlyingType()); + } + catch (OverflowException) + { + return defaultValue; + } + object result = Enum.ToObject(enumType, value); + return Enum.IsDefined(enumType, result) ? (T)result : defaultValue; + } + + public override T GetEnum(bool ignoreCase = false) + { + Type enumType = typeof(T); + object value; + try + { + value = Convert.ChangeType(Value, enumType.GetEnumUnderlyingType()); + } + catch (OverflowException) + { + throw new InvalidCastException(); + } + object result = Enum.ToObject(enumType, value); + if (!Enum.IsDefined(enumType, result)) + throw new InvalidCastException(); + return (T)result; + } + + internal override void Write(Utf8JsonWriter writer) + { + writer.WriteNumberValue(Value); + } + + public override JToken Clone() + { + return this; + } + + public static implicit operator JNumber(double value) + { + return new JNumber(value); + } + + public static implicit operator JNumber(long value) + { + return new JNumber(value); + } + + public static bool operator ==(JNumber left, JNumber? right) + { + if (right is null) return false; + return ReferenceEquals(left, right) || left.Value.Equals(right.Value); + } + + public static bool operator !=(JNumber left, JNumber right) + { + return !(left == right); + } + + public override bool Equals(object? obj) + { + if (obj is null) return false; + if (ReferenceEquals(this, obj)) return true; + + var other = obj switch + { + JNumber jNumber => jNumber, + uint u => new JNumber(u), + int i => new JNumber(i), + ulong ul => new JNumber(ul), + long l => new JNumber(l), + byte b => new JNumber(b), + sbyte sb => new JNumber(sb), + short s => new JNumber(s), + ushort us => new JNumber(us), + decimal d => new JNumber((double)d), + float f => new JNumber(f), + double d => new JNumber(d), + _ => throw new ArgumentOutOfRangeException(nameof(obj), obj, null) + }; + return other == this; + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + } +} diff --git a/src/Neo.Json/JObject.cs b/src/Neo.Json/JObject.cs new file mode 100644 index 0000000000..c5666e9112 --- /dev/null +++ b/src/Neo.Json/JObject.cs @@ -0,0 +1,91 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JObject.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.Text.Json; + +namespace Neo.Json +{ + /// + /// Represents a JSON object. + /// + public class JObject : JContainer + { + private readonly OrderedDictionary properties = new(); + + /// + /// Gets or sets the properties of the JSON object. + /// + public IDictionary Properties => properties; + + /// + /// Gets or sets the properties of the JSON object. + /// + /// The name of the property to get or set. + /// The property with the specified name. + public override JToken? this[string name] + { + get + { + if (Properties.TryGetValue(name, out JToken? value)) + return value; + return null; + } + set + { + Properties[name] = value; + } + } + + public override IReadOnlyList Children => properties.Values; + + /// + /// Determines whether the JSON object contains a property with the specified name. + /// + /// The property name to locate in the JSON object. + /// if the JSON object contains a property with the name; otherwise, . + public bool ContainsProperty(string key) + { + return Properties.ContainsKey(key); + } + + public override void Clear() => properties.Clear(); + + internal override void Write(Utf8JsonWriter writer) + { + writer.WriteStartObject(); + foreach (var (key, value) in Properties) + { + writer.WritePropertyName(key); + if (value is null) + writer.WriteNullValue(); + else + value.Write(writer); + } + writer.WriteEndObject(); + } + + /// + /// Creates a copy of the current JSON object. + /// + /// A copy of the current JSON object. + public override JToken Clone() + { + var cloned = new JObject(); + + foreach (var (key, value) in Properties) + { + cloned[key] = value != null ? value.Clone() : Null; + } + + return cloned; + } + } +} diff --git a/src/neo/IO/Json/JPathToken.cs b/src/Neo.Json/JPathToken.cs similarity index 70% rename from src/neo/IO/Json/JPathToken.cs rename to src/Neo.Json/JPathToken.cs index 8599cd3020..40054a1fb0 100644 --- a/src/neo/IO/Json/JPathToken.cs +++ b/src/Neo.Json/JPathToken.cs @@ -1,23 +1,20 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// JPathToken.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; -using System.Collections.Generic; -using System.Linq; - -namespace Neo.IO.Json +namespace Neo.Json { sealed class JPathToken { public JPathTokenType Type { get; private set; } - public string Content { get; private set; } + public string? Content { get; private set; } public static IEnumerable Parse(string expr) { @@ -114,24 +111,25 @@ private static string ParseNumber(string expr, int start) private static JPathToken DequeueToken(Queue tokens) { - if (!tokens.TryDequeue(out JPathToken token)) + if (!tokens.TryDequeue(out JPathToken? token)) throw new FormatException(); return token; } - public static void ProcessJsonPath(ref JObject[] objects, Queue tokens) + public static void ProcessJsonPath(ref JToken?[] objects, Queue tokens) { int maxDepth = 6; + int maxObjects = 1024; while (tokens.Count > 0) { JPathToken token = DequeueToken(tokens); switch (token.Type) { case JPathTokenType.Dot: - ProcessDot(ref objects, ref maxDepth, tokens); + ProcessDot(ref objects, ref maxDepth, maxObjects, tokens); break; case JPathTokenType.LeftBracket: - ProcessBracket(ref objects, ref maxDepth, tokens); + ProcessBracket(ref objects, ref maxDepth, maxObjects, tokens); break; default: throw new FormatException(); @@ -139,26 +137,26 @@ public static void ProcessJsonPath(ref JObject[] objects, Queue toke } } - private static void ProcessDot(ref JObject[] objects, ref int maxDepth, Queue tokens) + private static void ProcessDot(ref JToken?[] objects, ref int maxDepth, int maxObjects, Queue tokens) { JPathToken token = DequeueToken(tokens); switch (token.Type) { case JPathTokenType.Asterisk: - Descent(ref objects, ref maxDepth); + Descent(ref objects, ref maxDepth, maxObjects); break; case JPathTokenType.Dot: - ProcessRecursiveDescent(ref objects, ref maxDepth, tokens); + ProcessRecursiveDescent(ref objects, ref maxDepth, maxObjects, tokens); break; case JPathTokenType.Identifier: - Descent(ref objects, ref maxDepth, token.Content); + Descent(ref objects, ref maxDepth, maxObjects, token.Content!); break; default: throw new FormatException(); } } - private static void ProcessBracket(ref JObject[] objects, ref int maxDepth, Queue tokens) + private static void ProcessBracket(ref JToken?[] objects, ref int maxDepth, int maxObjects, Queue tokens) { JPathToken token = DequeueToken(tokens); switch (token.Type) @@ -166,23 +164,23 @@ private static void ProcessBracket(ref JObject[] objects, ref int maxDepth, Queu case JPathTokenType.Asterisk: if (DequeueToken(tokens).Type != JPathTokenType.RightBracket) throw new FormatException(); - Descent(ref objects, ref maxDepth); + Descent(ref objects, ref maxDepth, maxObjects); break; case JPathTokenType.Colon: - ProcessSlice(ref objects, ref maxDepth, tokens, 0); + ProcessSlice(ref objects, ref maxDepth, maxObjects, tokens, 0); break; case JPathTokenType.Number: JPathToken next = DequeueToken(tokens); switch (next.Type) { case JPathTokenType.Colon: - ProcessSlice(ref objects, ref maxDepth, tokens, int.Parse(token.Content)); + ProcessSlice(ref objects, ref maxDepth, maxObjects, tokens, int.Parse(token.Content!)); break; case JPathTokenType.Comma: - ProcessUnion(ref objects, ref maxDepth, tokens, token); + ProcessUnion(ref objects, ref maxDepth, maxObjects, tokens, token); break; case JPathTokenType.RightBracket: - Descent(ref objects, ref maxDepth, int.Parse(token.Content)); + Descent(ref objects, ref maxDepth, maxObjects, int.Parse(token.Content!)); break; default: throw new FormatException(); @@ -193,10 +191,10 @@ private static void ProcessBracket(ref JObject[] objects, ref int maxDepth, Queu switch (next.Type) { case JPathTokenType.Comma: - ProcessUnion(ref objects, ref maxDepth, tokens, token); + ProcessUnion(ref objects, ref maxDepth, maxObjects, tokens, token); break; case JPathTokenType.RightBracket: - Descent(ref objects, ref maxDepth, JObject.Parse($"\"{token.Content.Trim('\'')}\"").GetString()); + Descent(ref objects, ref maxDepth, maxObjects, JToken.Parse($"\"{token.Content!.Trim('\'')}\"")!.GetString()); break; default: throw new FormatException(); @@ -207,20 +205,21 @@ private static void ProcessBracket(ref JObject[] objects, ref int maxDepth, Queu } } - private static void ProcessRecursiveDescent(ref JObject[] objects, ref int maxDepth, Queue tokens) + private static void ProcessRecursiveDescent(ref JToken?[] objects, ref int maxDepth, int maxObjects, Queue tokens) { - List results = new(); + List results = new(); JPathToken token = DequeueToken(tokens); if (token.Type != JPathTokenType.Identifier) throw new FormatException(); while (objects.Length > 0) { - results.AddRange(objects.Where(p => p is not null).SelectMany(p => p.Properties).Where(p => p.Key == token.Content).Select(p => p.Value)); - Descent(ref objects, ref maxDepth); + results.AddRange(objects.OfType().SelectMany(p => p.Properties).Where(p => p.Key == token.Content).Select(p => p.Value)); + Descent(ref objects, ref maxDepth, maxObjects); + if (results.Count > maxObjects) throw new InvalidOperationException(nameof(maxObjects)); } objects = results.ToArray(); } - private static void ProcessSlice(ref JObject[] objects, ref int maxDepth, Queue tokens, int start) + private static void ProcessSlice(ref JToken?[] objects, ref int maxDepth, int maxObjects, Queue tokens, int start) { JPathToken token = DequeueToken(tokens); switch (token.Type) @@ -228,17 +227,17 @@ private static void ProcessSlice(ref JObject[] objects, ref int maxDepth, Queue< case JPathTokenType.Number: if (DequeueToken(tokens).Type != JPathTokenType.RightBracket) throw new FormatException(); - DescentRange(ref objects, ref maxDepth, start, int.Parse(token.Content)); + DescentRange(ref objects, ref maxDepth, maxObjects, start, int.Parse(token.Content!)); break; case JPathTokenType.RightBracket: - DescentRange(ref objects, ref maxDepth, start, 0); + DescentRange(ref objects, ref maxDepth, maxObjects, start, 0); break; default: throw new FormatException(); } } - private static void ProcessUnion(ref JObject[] objects, ref int maxDepth, Queue tokens, JPathToken first) + private static void ProcessUnion(ref JToken?[] objects, ref int maxDepth, int maxObjects, Queue tokens, JPathToken first) { List items = new() { first }; while (true) @@ -255,26 +254,27 @@ private static void ProcessUnion(ref JObject[] objects, ref int maxDepth, Queue< switch (first.Type) { case JPathTokenType.Number: - Descent(ref objects, ref maxDepth, items.Select(p => int.Parse(p.Content)).ToArray()); + Descent(ref objects, ref maxDepth, maxObjects, items.Select(p => int.Parse(p.Content!)).ToArray()); break; case JPathTokenType.String: - Descent(ref objects, ref maxDepth, items.Select(p => JObject.Parse($"\"{p.Content.Trim('\'')}\"").GetString()).ToArray()); + Descent(ref objects, ref maxDepth, maxObjects, items.Select(p => JToken.Parse($"\"{p.Content!.Trim('\'')}\"")!.GetString()).ToArray()); break; default: throw new FormatException(); } } - private static void Descent(ref JObject[] objects, ref int maxDepth) + private static void Descent(ref JToken?[] objects, ref int maxDepth, int maxObjects) { if (maxDepth <= 0) throw new InvalidOperationException(); --maxDepth; - objects = objects.Where(p => p is not null).SelectMany(p => p is JArray array ? array : p.Properties.Values).ToArray(); + objects = objects.OfType().SelectMany(p => p.Children).ToArray(); + if (objects.Length > maxObjects) throw new InvalidOperationException(nameof(maxObjects)); } - private static void Descent(ref JObject[] objects, ref int maxDepth, params string[] names) + private static void Descent(ref JToken?[] objects, ref int maxDepth, int maxObjects, params string[] names) { - static IEnumerable GetProperties(JObject obj, string[] names) + static IEnumerable GetProperties(JObject obj, string[] names) { foreach (string name in names) if (obj.ContainsProperty(name)) @@ -282,12 +282,13 @@ static IEnumerable GetProperties(JObject obj, string[] names) } if (maxDepth <= 0) throw new InvalidOperationException(); --maxDepth; - objects = objects.SelectMany(p => GetProperties(p, names)).ToArray(); + objects = objects.OfType().SelectMany(p => GetProperties(p, names)).ToArray(); + if (objects.Length > maxObjects) throw new InvalidOperationException(nameof(maxObjects)); } - private static void Descent(ref JObject[] objects, ref int maxDepth, params int[] indexes) + private static void Descent(ref JToken?[] objects, ref int maxDepth, int maxObjects, params int[] indexes) { - static IEnumerable GetElements(JArray array, int[] indexes) + static IEnumerable GetElements(JArray array, int[] indexes) { foreach (int index in indexes) { @@ -299,9 +300,10 @@ static IEnumerable GetElements(JArray array, int[] indexes) if (maxDepth <= 0) throw new InvalidOperationException(); --maxDepth; objects = objects.OfType().SelectMany(p => GetElements(p, indexes)).ToArray(); + if (objects.Length > maxObjects) throw new InvalidOperationException(nameof(maxObjects)); } - private static void DescentRange(ref JObject[] objects, ref int maxDepth, int start, int end) + private static void DescentRange(ref JToken?[] objects, ref int maxDepth, int maxObjects, int start, int end) { if (maxDepth <= 0) throw new InvalidOperationException(); --maxDepth; @@ -313,6 +315,7 @@ private static void DescentRange(ref JObject[] objects, ref int maxDepth, int st int count = iEnd - iStart; return p.Skip(iStart).Take(count); }).ToArray(); + if (objects.Length > maxObjects) throw new InvalidOperationException(nameof(maxObjects)); } } } diff --git a/src/Neo.Json/JPathTokenType.cs b/src/Neo.Json/JPathTokenType.cs new file mode 100644 index 0000000000..ea25a3609d --- /dev/null +++ b/src/Neo.Json/JPathTokenType.cs @@ -0,0 +1,26 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JPathTokenType.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.Json; + +enum JPathTokenType : byte +{ + Root, + Dot, + LeftBracket, + RightBracket, + Asterisk, + Comma, + Colon, + Identifier, + String, + Number +} diff --git a/src/Neo.Json/JString.cs b/src/Neo.Json/JString.cs new file mode 100644 index 0000000000..c9466c26a6 --- /dev/null +++ b/src/Neo.Json/JString.cs @@ -0,0 +1,128 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JString.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.Globalization; +using System.Text.Json; + +namespace Neo.Json +{ + /// + /// Represents a JSON string. + /// + public class JString : JToken + { + /// + /// Gets the value of the JSON token. + /// + public string Value { get; } + + /// + /// Initializes a new instance of the class with the specified value. + /// + /// The value of the JSON token. + public JString(string value) + { + this.Value = value ?? throw new ArgumentNullException(nameof(value)); + } + + /// + /// Converts the current JSON token to a boolean value. + /// + /// if value is not empty; otherwise, . + public override bool AsBoolean() + { + return !string.IsNullOrEmpty(Value); + } + + public override double AsNumber() + { + if (string.IsNullOrEmpty(Value)) return 0; + return double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double result) ? result : double.NaN; + } + + public override string AsString() + { + return Value; + } + + public override string GetString() => Value; + + public override T AsEnum(T defaultValue = default, bool ignoreCase = false) + { + try + { + return Enum.Parse(Value, ignoreCase); + } + catch + { + return defaultValue; + } + } + + public override T GetEnum(bool ignoreCase = false) + { + T result = Enum.Parse(Value, ignoreCase); + if (!Enum.IsDefined(typeof(T), result)) throw new InvalidCastException(); + return result; + } + + internal override void Write(Utf8JsonWriter writer) + { + writer.WriteStringValue(Value); + } + + public override JToken Clone() + { + return this; + } + + public static implicit operator JString(Enum value) + { + return new JString(value.ToString()); + } + + public static implicit operator JString?(string? value) + { + return value == null ? null : new JString(value); + } + + public static bool operator ==(JString left, JString? right) + { + if (right is null) return false; + return ReferenceEquals(left, right) || left.Value.Equals(right.Value); + } + + public static bool operator !=(JString left, JString right) + { + return !(left == right); + } + + public override bool Equals(object? obj) + { + if (obj is null) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj is JString other) + { + return this == other; + } + if (obj is string str) + { + return this.Value == str; + } + return false; + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + } +} diff --git a/src/Neo.Json/JToken.cs b/src/Neo.Json/JToken.cs new file mode 100644 index 0000000000..bb6a510a69 --- /dev/null +++ b/src/Neo.Json/JToken.cs @@ -0,0 +1,305 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JToken.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.Text.Json; +using static Neo.Json.Utility; + +namespace Neo.Json; + +/// +/// Represents an abstract JSON token. +/// +public abstract class JToken +{ + /// + /// Represents a token. + /// + public const JToken? Null = null; + + /// + /// Gets or sets the child token at the specified index. + /// + /// The zero-based index of the child token to get or set. + /// The child token at the specified index. + public virtual JToken? this[int index] + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + /// + /// Gets or sets the properties of the JSON object. + /// + /// The key of the property to get or set. + /// The property with the specified name. + public virtual JToken? this[string key] + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + /// + /// Converts the current JSON token to a boolean value. + /// + /// The converted value. + public virtual bool AsBoolean() + { + return true; + } + + /// + /// Converts the current JSON token to an . + /// + /// The type of the . + /// If the current JSON token cannot be converted to type , then the default value is returned. + /// Indicates whether case should be ignored during conversion. + /// The converted value. + public virtual T AsEnum(T defaultValue = default, bool ignoreCase = false) where T : unmanaged, Enum + { + return defaultValue; + } + + /// + /// Converts the current JSON token to a floating point number. + /// + /// The converted value. + public virtual double AsNumber() + { + return double.NaN; + } + + /// + /// Converts the current JSON token to a . + /// + /// The converted value. + public virtual string AsString() + { + return ToString(); + } + + /// + /// Converts the current JSON token to a boolean value. + /// + /// The converted value. + /// The JSON token is not a . + public virtual bool GetBoolean() => throw new InvalidCastException(); + + public virtual T GetEnum(bool ignoreCase = false) where T : unmanaged, Enum => throw new InvalidCastException(); + + /// + /// Converts the current JSON token to a 32-bit signed integer. + /// + /// The converted value. + /// The JSON token is not a . + /// The JSON token cannot be converted to an integer. + /// The JSON token cannot be converted to a 32-bit signed integer. + public int GetInt32() + { + double d = GetNumber(); + if (d % 1 != 0) throw new InvalidCastException(); + return checked((int)d); + } + + /// + /// Converts the current JSON token to a floating point number. + /// + /// The converted value. + /// The JSON token is not a . + public virtual double GetNumber() => throw new InvalidCastException(); + + /// + /// Converts the current JSON token to a . + /// + /// The converted value. + /// The JSON token is not a . + public virtual string GetString() => throw new InvalidCastException(); + + /// + /// Parses a JSON token from a byte array. + /// + /// The byte array that contains the JSON token. + /// The maximum nesting depth when parsing the JSON token. + /// The parsed JSON token. + public static JToken? Parse(ReadOnlySpan value, int max_nest = 64) + { + Utf8JsonReader reader = new(value, new JsonReaderOptions + { + AllowTrailingCommas = false, + CommentHandling = JsonCommentHandling.Skip, + MaxDepth = max_nest + }); + try + { + JToken? json = Read(ref reader); + if (reader.Read()) throw new FormatException(); + return json; + } + catch (JsonException ex) + { + throw new FormatException(ex.Message, ex); + } + } + + /// + /// Parses a JSON token from a . + /// + /// The that contains the JSON token. + /// The maximum nesting depth when parsing the JSON token. + /// The parsed JSON token. + public static JToken? Parse(string value, int max_nest = 64) + { + return Parse(StrictUTF8.GetBytes(value), max_nest); + } + + private static JToken? Read(ref Utf8JsonReader reader, bool skipReading = false) + { + if (!skipReading && !reader.Read()) throw new FormatException(); + return reader.TokenType switch + { + JsonTokenType.False => false, + JsonTokenType.Null => Null, + JsonTokenType.Number => reader.GetDouble(), + JsonTokenType.StartArray => ReadArray(ref reader), + JsonTokenType.StartObject => ReadObject(ref reader), + JsonTokenType.String => ReadString(ref reader), + JsonTokenType.True => true, + _ => throw new FormatException(), + }; + } + + private static JArray ReadArray(ref Utf8JsonReader reader) + { + JArray array = new(); + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonTokenType.EndArray: + return array; + default: + array.Add(Read(ref reader, skipReading: true)); + break; + } + } + throw new FormatException(); + } + + private static JObject ReadObject(ref Utf8JsonReader reader) + { + JObject obj = new(); + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonTokenType.EndObject: + return obj; + case JsonTokenType.PropertyName: + string name = ReadString(ref reader); + if (obj.Properties.ContainsKey(name)) throw new FormatException(); + JToken? value = Read(ref reader); + obj.Properties.Add(name, value); + break; + default: + throw new FormatException(); + } + } + throw new FormatException(); + } + + private static string ReadString(ref Utf8JsonReader reader) + { + try + { + return reader.GetString()!; + } + catch (InvalidOperationException ex) + { + throw new FormatException(ex.Message, ex); + } + } + + /// + /// Encode the current JSON token into a byte array. + /// + /// Indicates whether indentation is required. + /// The encoded JSON token. + public byte[] ToByteArray(bool indented) + { + using MemoryStream ms = new(); + using Utf8JsonWriter writer = new(ms, new JsonWriterOptions + { + Indented = indented, + SkipValidation = true + }); + Write(writer); + writer.Flush(); + return ms.ToArray(); + } + + /// + /// Encode the current JSON token into a . + /// + /// The encoded JSON token. + public override string ToString() + { + return ToString(false); + } + + /// + /// Encode the current JSON token into a . + /// + /// Indicates whether indentation is required. + /// The encoded JSON token. + public string ToString(bool indented) + { + return StrictUTF8.GetString(ToByteArray(indented)); + } + + internal abstract void Write(Utf8JsonWriter writer); + + public abstract JToken Clone(); + + public JArray JsonPath(string expr) + { + JToken?[] objects = { this }; + if (expr.Length == 0) return objects; + Queue tokens = new(JPathToken.Parse(expr)); + JPathToken first = tokens.Dequeue(); + if (first.Type != JPathTokenType.Root) throw new FormatException(); + JPathToken.ProcessJsonPath(ref objects, tokens); + return objects; + } + + public static implicit operator JToken(Enum value) + { + return (JString)value; + } + + public static implicit operator JToken(JToken?[] value) + { + return (JArray)value; + } + + public static implicit operator JToken(bool value) + { + return (JBoolean)value; + } + + public static implicit operator JToken(double value) + { + return (JNumber)value; + } + + public static implicit operator JToken?(string? value) + { + return (JString?)value; + } +} diff --git a/src/Neo.Json/Neo.Json.csproj b/src/Neo.Json/Neo.Json.csproj new file mode 100644 index 0000000000..d2ce18e430 --- /dev/null +++ b/src/Neo.Json/Neo.Json.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.1;net7.0 + enable + enable + NEO;JSON + + + + + + + diff --git a/src/Neo.Json/OrderedDictionary.KeyCollection.cs b/src/Neo.Json/OrderedDictionary.KeyCollection.cs new file mode 100644 index 0000000000..13fbd62e85 --- /dev/null +++ b/src/Neo.Json/OrderedDictionary.KeyCollection.cs @@ -0,0 +1,51 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// OrderedDictionary.KeyCollection.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.Collections; + +namespace Neo.Json; + +partial class OrderedDictionary +{ + class KeyCollection : ICollection, IReadOnlyList + { + private readonly InternalCollection internalCollection; + + public KeyCollection(InternalCollection internalCollection) + { + this.internalCollection = internalCollection; + } + + public TKey this[int index] => internalCollection[index].Key; + + public int Count => internalCollection.Count; + + public bool IsReadOnly => true; + + public void Add(TKey item) => throw new NotSupportedException(); + + public void Clear() => throw new NotSupportedException(); + + public bool Contains(TKey item) => internalCollection.Contains(item); + + public void CopyTo(TKey[] array, int arrayIndex) + { + for (int i = 0; i < internalCollection.Count && i + arrayIndex < array.Length; i++) + array[i + arrayIndex] = internalCollection[i].Key; + } + + public IEnumerator GetEnumerator() => internalCollection.Select(p => p.Key).GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public bool Remove(TKey item) => throw new NotSupportedException(); + } +} diff --git a/src/Neo.Json/OrderedDictionary.ValueCollection.cs b/src/Neo.Json/OrderedDictionary.ValueCollection.cs new file mode 100644 index 0000000000..f7bb0c4359 --- /dev/null +++ b/src/Neo.Json/OrderedDictionary.ValueCollection.cs @@ -0,0 +1,51 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// OrderedDictionary.ValueCollection.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.Collections; + +namespace Neo.Json; + +partial class OrderedDictionary +{ + class ValueCollection : ICollection, IReadOnlyList + { + private readonly InternalCollection internalCollection; + + public ValueCollection(InternalCollection internalCollection) + { + this.internalCollection = internalCollection; + } + + public TValue this[int index] => internalCollection[index].Value; + + public int Count => internalCollection.Count; + + public bool IsReadOnly => true; + + public void Add(TValue item) => throw new NotSupportedException(); + + public void Clear() => throw new NotSupportedException(); + + public bool Contains(TValue item) => item is null ? internalCollection.Any(p => p is null) : internalCollection.Any(p => item.Equals(p.Value)); + + public void CopyTo(TValue[] array, int arrayIndex) + { + for (int i = 0; i < internalCollection.Count && i + arrayIndex < array.Length; i++) + array[i + arrayIndex] = internalCollection[i].Value; + } + + public IEnumerator GetEnumerator() => internalCollection.Select(p => p.Value).GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public bool Remove(TValue item) => throw new NotSupportedException(); + } +} diff --git a/src/Neo.Json/OrderedDictionary.cs b/src/Neo.Json/OrderedDictionary.cs new file mode 100644 index 0000000000..0913bda44e --- /dev/null +++ b/src/Neo.Json/OrderedDictionary.cs @@ -0,0 +1,144 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// OrderedDictionary.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.Collections; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; + +namespace Neo.Json +{ + partial class OrderedDictionary : IDictionary where TKey : notnull + { + private class TItem + { + public TKey Key; + public TValue Value; + + public TItem(TKey key, TValue value) + { + Key = key; + Value = value; + } + } + + private class InternalCollection : KeyedCollection + { + protected override TKey GetKeyForItem(TItem item) + { + return item.Key; + } + } + + private readonly InternalCollection collection = new(); + + public int Count => collection.Count; + public bool IsReadOnly => false; + public IReadOnlyList Keys { get; } + public IReadOnlyList Values { get; } + ICollection IDictionary.Keys => (KeyCollection)Keys; + ICollection IDictionary.Values => (ValueCollection)Values; + + public OrderedDictionary() + { + Keys = new KeyCollection(collection); + Values = new ValueCollection(collection); + } + + public TValue this[TKey key] + { + get + { + return collection[key].Value; + } + set + { + if (collection.TryGetValue(key, out var entry)) + entry.Value = value; + else + Add(key, value); + } + } + + public TValue this[int index] + { + get + { + return collection[index].Value; + } + } + + public void Add(TKey key, TValue value) + { + collection.Add(new TItem(key, value)); + } + + public bool ContainsKey(TKey key) + { + return collection.Contains(key); + } + + public bool Remove(TKey key) + { + return collection.Remove(key); + } + +#pragma warning disable CS8767 + + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) + { + if (collection.TryGetValue(key, out var entry)) + { + value = entry.Value; + return true; + } + value = default; + return false; + } + +#pragma warning restore CS8767 + + void ICollection>.Add(KeyValuePair item) + { + Add(item.Key, item.Value); + } + + public void Clear() + { + collection.Clear(); + } + + bool ICollection>.Contains(KeyValuePair item) + { + return collection.Contains(item.Key); + } + + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + for (int i = 0; i < collection.Count; i++) + array[i + arrayIndex] = new KeyValuePair(collection[i].Key, collection[i].Value); + } + + bool ICollection>.Remove(KeyValuePair item) + { + return collection.Remove(item.Key); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + return collection.Select(p => new KeyValuePair(p.Key, p.Value)).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return collection.Select(p => new KeyValuePair(p.Key, p.Value)).GetEnumerator(); + } + } +} diff --git a/src/Neo.Json/Properties/AssemblyInfo.cs b/src/Neo.Json/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..9c3afc5d2e --- /dev/null +++ b/src/Neo.Json/Properties/AssemblyInfo.cs @@ -0,0 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// AssemblyInfo.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.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Neo.Json.UnitTests")] diff --git a/src/Neo.Json/Utility.cs b/src/Neo.Json/Utility.cs new file mode 100644 index 0000000000..6fff7211ca --- /dev/null +++ b/src/Neo.Json/Utility.cs @@ -0,0 +1,26 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Utility.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.Text; + +namespace Neo.Json; + +static class Utility +{ + public static Encoding StrictUTF8 { get; } + + static Utility() + { + StrictUTF8 = (Encoding)Encoding.UTF8.Clone(); + StrictUTF8.DecoderFallback = DecoderFallback.ExceptionFallback; + StrictUTF8.EncoderFallback = EncoderFallback.ExceptionFallback; + } +} diff --git a/src/Neo.VM/BadScriptException.cs b/src/Neo.VM/BadScriptException.cs new file mode 100644 index 0000000000..1e7b250291 --- /dev/null +++ b/src/Neo.VM/BadScriptException.cs @@ -0,0 +1,32 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// BadScriptException.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; + +namespace Neo.VM +{ + /// + /// Represents the exception thrown when the bad script is parsed. + /// + public class BadScriptException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public BadScriptException() { } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The message that describes the error. + public BadScriptException(string message) : base(message) { } + } +} diff --git a/src/Neo.VM/CatchableException.cs b/src/Neo.VM/CatchableException.cs new file mode 100644 index 0000000000..b4fd3f1655 --- /dev/null +++ b/src/Neo.VM/CatchableException.cs @@ -0,0 +1,22 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// CatchableException.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; + +namespace Neo.VM +{ + public class CatchableException : Exception + { + public CatchableException(string message) : base(message) + { + } + } +} diff --git a/src/neo/IO/Caching/OrderedDictionary.cs b/src/Neo.VM/Collections/OrderedDictionary.cs similarity index 79% rename from src/neo/IO/Caching/OrderedDictionary.cs rename to src/Neo.VM/Collections/OrderedDictionary.cs index 7f384d1380..92d8e8f3ad 100644 --- a/src/neo/IO/Caching/OrderedDictionary.cs +++ b/src/Neo.VM/Collections/OrderedDictionary.cs @@ -1,26 +1,35 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// OrderedDictionary.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.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.Linq; -namespace Neo.IO.Caching +namespace Neo.VM.Collections { internal class OrderedDictionary : IDictionary + where TKey : notnull { private class TItem { - public TKey Key; + public readonly TKey Key; public TValue Value; + + public TItem(TKey key, TValue value) + { + this.Key = key; + this.Value = value; + } } private class InternalCollection : KeyedCollection @@ -53,21 +62,9 @@ public TValue this[TKey key] } } - public TValue this[int index] - { - get - { - return collection[index].Value; - } - } - public void Add(TKey key, TValue value) { - collection.Add(new TItem - { - Key = key, - Value = value - }); + collection.Add(new TItem(key, value)); } public bool ContainsKey(TKey key) @@ -80,7 +77,10 @@ public bool Remove(TKey key) return collection.Remove(key); } - public bool TryGetValue(TKey key, out TValue value) + // supress warning of value parameter nullability mismatch +#pragma warning disable CS8767 + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) +#pragma warning restore CS8767 { if (collection.TryGetValue(key, out var entry)) { diff --git a/src/Neo.VM/Cryptography/BitOperations.cs b/src/Neo.VM/Cryptography/BitOperations.cs new file mode 100644 index 0000000000..c947b4de98 --- /dev/null +++ b/src/Neo.VM/Cryptography/BitOperations.cs @@ -0,0 +1,28 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// BitOperations.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.Runtime.CompilerServices; + +namespace Neo.VM.Cryptography +{ +#if !NET5_0_OR_GREATER + static class BitOperations + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint RotateLeft(uint value, int offset) + => (value << offset) | (value >> (32 - offset)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong RotateLeft(ulong value, int offset) + => (value << offset) | (value >> (64 - offset)); + } +#endif +} diff --git a/src/neo/Cryptography/Murmur32.cs b/src/Neo.VM/Cryptography/Murmur32.cs similarity index 88% rename from src/neo/Cryptography/Murmur32.cs rename to src/Neo.VM/Cryptography/Murmur32.cs index ee1f7051bb..cfb6f8ccd1 100644 --- a/src/neo/Cryptography/Murmur32.cs +++ b/src/Neo.VM/Cryptography/Murmur32.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Murmur32.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. @@ -13,12 +14,12 @@ using System.Numerics; using System.Security.Cryptography; -namespace Neo.Cryptography +namespace Neo.VM.Cryptography { /// /// Computes the murmur hash for the input data. /// - public sealed class Murmur32 : HashAlgorithm + sealed class Murmur32 : HashAlgorithm { private const uint c1 = 0xcc9e2d51; private const uint c2 = 0x1b873593; diff --git a/src/Neo.VM/Debugger.cs b/src/Neo.VM/Debugger.cs new file mode 100644 index 0000000000..a19ae7d64e --- /dev/null +++ b/src/Neo.VM/Debugger.cs @@ -0,0 +1,138 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Debugger.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.Collections.Generic; + +namespace Neo.VM +{ + /// + /// A simple debugger for . + /// + public class Debugger + { + private readonly ExecutionEngine engine; + private readonly Dictionary> break_points = new(); + + /// + /// Create a debugger on the specified . + /// + /// The to attach the debugger. + public Debugger(ExecutionEngine engine) + { + this.engine = engine; + } + + /// + /// Add a breakpoint at the specified position of the specified script. The VM will break the execution when it reaches the breakpoint. + /// + /// The script to add the breakpoint. + /// The position of the breakpoint in the script. + public void AddBreakPoint(Script script, uint position) + { + if (!break_points.TryGetValue(script, out HashSet? hashset)) + { + hashset = new HashSet(); + break_points.Add(script, hashset); + } + hashset.Add(position); + } + + /// + /// Start or continue execution of the VM. + /// + /// Returns the state of the VM after the execution. + public VMState Execute() + { + if (engine.State == VMState.BREAK) + engine.State = VMState.NONE; + while (engine.State == VMState.NONE) + ExecuteAndCheckBreakPoints(); + return engine.State; + } + + private void ExecuteAndCheckBreakPoints() + { + engine.ExecuteNext(); + if (engine.State == VMState.NONE && engine.InvocationStack.Count > 0 && break_points.Count > 0) + { + if (break_points.TryGetValue(engine.CurrentContext!.Script, out HashSet? hashset) && hashset.Contains((uint)engine.CurrentContext.InstructionPointer)) + engine.State = VMState.BREAK; + } + } + + /// + /// Removes the breakpoint at the specified position in the specified script. + /// + /// The script to remove the breakpoint. + /// The position of the breakpoint in the script. + /// + /// if the breakpoint is successfully found and removed; + /// otherwise, . + /// + public bool RemoveBreakPoint(Script script, uint position) + { + if (!break_points.TryGetValue(script, out HashSet? hashset)) return false; + if (!hashset.Remove(position)) return false; + if (hashset.Count == 0) break_points.Remove(script); + return true; + } + + /// + /// Execute the next instruction. If the instruction involves a call to a method, it steps into the method and breaks the execution on the first instruction of that method. + /// + /// The VM state after the instruction is executed. + public VMState StepInto() + { + if (engine.State == VMState.HALT || engine.State == VMState.FAULT) + return engine.State; + engine.ExecuteNext(); + if (engine.State == VMState.NONE) + engine.State = VMState.BREAK; + return engine.State; + } + + /// + /// Execute until the currently executed method is returned. + /// + /// The VM state after the currently executed method is returned. + public VMState StepOut() + { + if (engine.State == VMState.BREAK) + engine.State = VMState.NONE; + int c = engine.InvocationStack.Count; + while (engine.State == VMState.NONE && engine.InvocationStack.Count >= c) + ExecuteAndCheckBreakPoints(); + if (engine.State == VMState.NONE) + engine.State = VMState.BREAK; + return engine.State; + } + + /// + /// Execute the next instruction. If the instruction involves a call to a method, it does not step into the method (it steps over it instead). + /// + /// The VM state after the instruction is executed. + public VMState StepOver() + { + if (engine.State == VMState.HALT || engine.State == VMState.FAULT) + return engine.State; + engine.State = VMState.NONE; + int c = engine.InvocationStack.Count; + do + { + ExecuteAndCheckBreakPoints(); + } + while (engine.State == VMState.NONE && engine.InvocationStack.Count > c); + if (engine.State == VMState.NONE) + engine.State = VMState.BREAK; + return engine.State; + } + } +} diff --git a/src/Neo.VM/EvaluationStack.cs b/src/Neo.VM/EvaluationStack.cs new file mode 100644 index 0000000000..34517b2197 --- /dev/null +++ b/src/Neo.VM/EvaluationStack.cs @@ -0,0 +1,168 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// EvaluationStack.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.VM.Types; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ + /// + /// Represents the evaluation stack in the VM. + /// + public sealed class EvaluationStack : IReadOnlyList + { + private readonly List innerList = new(); + private readonly ReferenceCounter referenceCounter; + + internal EvaluationStack(ReferenceCounter referenceCounter) + { + this.referenceCounter = referenceCounter; + } + + /// + /// Gets the number of items on the stack. + /// + public int Count => innerList.Count; + + internal void Clear() + { + foreach (StackItem item in innerList) + referenceCounter.RemoveStackReference(item); + innerList.Clear(); + } + + internal void CopyTo(EvaluationStack stack, int count = -1) + { + if (count < -1 || count > innerList.Count) + throw new ArgumentOutOfRangeException(nameof(count)); + if (count == 0) return; + if (count == -1 || count == innerList.Count) + stack.innerList.AddRange(innerList); + else + stack.innerList.AddRange(innerList.Skip(innerList.Count - count)); + } + + public IEnumerator GetEnumerator() + { + return innerList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return innerList.GetEnumerator(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void Insert(int index, StackItem item) + { + if (index > innerList.Count) throw new InvalidOperationException($"Insert out of bounds: {index}/{innerList.Count}"); + innerList.Insert(innerList.Count - index, item); + referenceCounter.AddStackReference(item); + } + + internal void MoveTo(EvaluationStack stack, int count = -1) + { + if (count == 0) return; + CopyTo(stack, count); + if (count == -1 || count == innerList.Count) + innerList.Clear(); + else + innerList.RemoveRange(innerList.Count - count, count); + } + + /// + /// Returns the item at the specified index from the top of the stack without removing it. + /// + /// The index of the object from the top of the stack. + /// The item at the specified index. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public StackItem Peek(int index = 0) + { + if (index >= innerList.Count) throw new InvalidOperationException($"Peek out of bounds: {index}/{innerList.Count}"); + if (index < 0) + { + index += innerList.Count; + if (index < 0) throw new InvalidOperationException($"Peek out of bounds: {index}/{innerList.Count}"); + } + return innerList[innerList.Count - index - 1]; + } + + StackItem IReadOnlyList.this[int index] => Peek(index); + + /// + /// Pushes an item onto the top of the stack. + /// + /// The item to be pushed. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Push(StackItem item) + { + innerList.Add(item); + referenceCounter.AddStackReference(item); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void Reverse(int n) + { + if (n < 0 || n > innerList.Count) + throw new ArgumentOutOfRangeException(nameof(n)); + if (n <= 1) return; + innerList.Reverse(innerList.Count - n, n); + } + + /// + /// Removes and returns the item at the top of the stack. + /// + /// The item removed from the top of the stack. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public StackItem Pop() + { + return Remove(0); + } + + /// + /// Removes and returns the item at the top of the stack and convert it to the specified type. + /// + /// The type to convert to. + /// The item removed from the top of the stack. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Pop() where T : StackItem + { + return Remove(0); + } + + internal T Remove(int index) where T : StackItem + { + if (index >= innerList.Count) + throw new ArgumentOutOfRangeException(nameof(index)); + if (index < 0) + { + index += innerList.Count; + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index)); + } + index = innerList.Count - index - 1; + if (innerList[index] is not T item) + throw new InvalidCastException($"The item can't be casted to type {typeof(T)}"); + innerList.RemoveAt(index); + referenceCounter.RemoveStackReference(item); + return item; + } + + public override string ToString() + { + return $"[{string.Join(", ", innerList.Select(p => $"{p.Type}({p})"))}]"; + } + } +} diff --git a/src/Neo.VM/ExceptionHandlingContext.cs b/src/Neo.VM/ExceptionHandlingContext.cs new file mode 100644 index 0000000000..a0c48c0b97 --- /dev/null +++ b/src/Neo.VM/ExceptionHandlingContext.cs @@ -0,0 +1,58 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ExceptionHandlingContext.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 +{ + /// + /// Represents the context used for exception handling. + /// + [DebuggerDisplay("State={State}, CatchPointer={CatchPointer}, FinallyPointer={FinallyPointer}, EndPointer={EndPointer}")] + public sealed class ExceptionHandlingContext + { + /// + /// The position of the block. + /// + public int CatchPointer { get; } + + /// + /// The position of the block. + /// + public int FinallyPointer { get; } + + /// + /// The end position of the -- block. + /// + public int EndPointer { get; internal set; } = -1; + + /// + /// Indicates whether the block is included in the context. + /// + public bool HasCatch => CatchPointer >= 0; + + /// + /// Indicates whether the block is included in the context. + /// + public bool HasFinally => FinallyPointer >= 0; + + /// + /// Indicates the state of the context. + /// + public ExceptionHandlingState State { get; internal set; } = ExceptionHandlingState.Try; + + internal ExceptionHandlingContext(int catchPointer, int finallyPointer) + { + this.CatchPointer = catchPointer; + this.FinallyPointer = finallyPointer; + } + } +} diff --git a/src/Neo.VM/ExceptionHandlingState.cs b/src/Neo.VM/ExceptionHandlingState.cs new file mode 100644 index 0000000000..119307a29d --- /dev/null +++ b/src/Neo.VM/ExceptionHandlingState.cs @@ -0,0 +1,34 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ExceptionHandlingState.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 +{ + /// + /// Indicates the state of the . + /// + public enum ExceptionHandlingState : byte + { + /// + /// Indicates that the block is being executed. + /// + Try, + + /// + /// Indicates that the block is being executed. + /// + Catch, + + /// + /// Indicates that the block is being executed. + /// + Finally + } +} diff --git a/src/Neo.VM/ExecutionContext.SharedStates.cs b/src/Neo.VM/ExecutionContext.SharedStates.cs new file mode 100644 index 0000000000..9903d50adf --- /dev/null +++ b/src/Neo.VM/ExecutionContext.SharedStates.cs @@ -0,0 +1,34 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ExecutionContext.SharedStates.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; +using System.Collections.Generic; + +namespace Neo.VM +{ + partial class ExecutionContext + { + private class SharedStates + { + public readonly Script Script; + public readonly EvaluationStack EvaluationStack; + public Slot? StaticFields; + public readonly Dictionary States; + + public SharedStates(Script script, ReferenceCounter referenceCounter) + { + this.Script = script; + this.EvaluationStack = new EvaluationStack(referenceCounter); + this.States = new Dictionary(); + } + } + } +} diff --git a/src/Neo.VM/ExecutionContext.cs b/src/Neo.VM/ExecutionContext.cs new file mode 100644 index 0000000000..5f3a18e077 --- /dev/null +++ b/src/Neo.VM/ExecutionContext.cs @@ -0,0 +1,170 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ExecutionContext.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; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ + /// + /// Represents a frame in the VM execution stack. + /// + [DebuggerDisplay("InstructionPointer={InstructionPointer}")] + public sealed partial class ExecutionContext + { + private readonly SharedStates shared_states; + private int instructionPointer; + + /// + /// Indicates the number of values that the context should return when it is unloaded. + /// + public int RVCount { get; } + + /// + /// The script to run in this context. + /// + public Script Script => shared_states.Script; + + /// + /// The evaluation stack for this context. + /// + public EvaluationStack EvaluationStack => shared_states.EvaluationStack; + + /// + /// The slot used to store the static fields. + /// + public Slot? StaticFields + { + get => shared_states.StaticFields; + internal set => shared_states.StaticFields = value; + } + + /// + /// The slot used to store the local variables of the current method. + /// + public Slot? LocalVariables { get; internal set; } + + /// + /// The slot used to store the arguments of the current method. + /// + public Slot? Arguments { get; internal set; } + + /// + /// The stack containing nested . + /// + public Stack? TryStack { get; internal set; } + + /// + /// The pointer indicating the current instruction. + /// + public int InstructionPointer + { + get + { + return instructionPointer; + } + internal set + { + if (value < 0 || value > Script.Length) + throw new ArgumentOutOfRangeException(nameof(value)); + instructionPointer = value; + } + } + + /// + /// Returns the current . + /// + public Instruction? CurrentInstruction + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return GetInstruction(InstructionPointer); + } + } + + /// + /// Returns the next . + /// + public Instruction? NextInstruction + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + Instruction? current = CurrentInstruction; + if (current is null) return null; + return GetInstruction(InstructionPointer + current.Size); + } + } + + internal ExecutionContext(Script script, int rvcount, ReferenceCounter referenceCounter) + : this(new SharedStates(script, referenceCounter), rvcount, 0) + { + } + + private ExecutionContext(SharedStates shared_states, int rvcount, int initialPosition) + { + if (rvcount < -1 || rvcount > ushort.MaxValue) + throw new ArgumentOutOfRangeException(nameof(rvcount)); + this.shared_states = shared_states; + this.RVCount = rvcount; + this.InstructionPointer = initialPosition; + } + + /// + /// Clones the context so that they share the same script, stack, and static fields. + /// + /// The cloned context. + public ExecutionContext Clone() + { + return Clone(InstructionPointer); + } + + /// + /// Clones the context so that they share the same script, stack, and static fields. + /// + /// The instruction pointer of the new context. + /// The cloned context. + public ExecutionContext Clone(int initialPosition) + { + return new ExecutionContext(shared_states, 0, initialPosition); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Instruction? GetInstruction(int ip) => ip >= Script.Length ? null : Script.GetInstruction(ip); + + /// + /// Gets custom data of the specified type. If the data does not exist, create a new one. + /// + /// The type of data to be obtained. + /// A delegate used to create the entry. If factory is null, new() will be used. + /// The custom data of the specified type. + public T GetState(Func? factory = null) where T : class, new() + { + if (!shared_states.States.TryGetValue(typeof(T), out object? value)) + { + value = factory is null ? new T() : factory(); + shared_states.States[typeof(T)] = value; + } + return (T)value; + } + + internal bool MoveNext() + { + Instruction? current = CurrentInstruction; + if (current is null) return false; + InstructionPointer += current.Size; + return InstructionPointer < Script.Length; + } + } +} diff --git a/src/Neo.VM/ExecutionEngine.cs b/src/Neo.VM/ExecutionEngine.cs new file mode 100644 index 0000000000..de268d1030 --- /dev/null +++ b/src/Neo.VM/ExecutionEngine.cs @@ -0,0 +1,296 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ExecutionEngine.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.VM.Types; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ + /// + /// Represents the VM used to execute the script. + /// + public class ExecutionEngine : IDisposable + { + private VMState state = VMState.BREAK; + internal bool isJumping = false; + + public JumpTable JumpTable { get; } + + /// + /// Restrictions on the VM. + /// + public ExecutionEngineLimits Limits { get; } + + /// + /// Used for reference counting of objects in the VM. + /// + public ReferenceCounter ReferenceCounter { get; } + + /// + /// The invocation stack of the VM. + /// + public Stack InvocationStack { get; } = new Stack(); + + /// + /// The top frame of the invocation stack. + /// + public ExecutionContext? CurrentContext { get; private set; } + + /// + /// The bottom frame of the invocation stack. + /// + public ExecutionContext? EntryContext { get; private set; } + + /// + /// The stack to store the return values. + /// + public EvaluationStack ResultStack { get; } + + /// + /// The VM object representing the uncaught exception. + /// + public StackItem? UncaughtException { get; internal set; } + + /// + /// The current state of the VM. + /// + public VMState State + { + get + { + return state; + } + protected internal set + { + if (state != value) + { + state = value; + OnStateChanged(); + } + } + } + + /// + /// Initializes a new instance of the class. + /// + public ExecutionEngine(JumpTable? jumpTable = null) : this(jumpTable, new ReferenceCounter(), ExecutionEngineLimits.Default) + { + } + + /// + /// Initializes a new instance of the class with the specified and . + /// + /// The jump table to be used. + /// The reference counter to be used. + /// Restrictions on the VM. + protected ExecutionEngine(JumpTable? jumpTable, ReferenceCounter referenceCounter, ExecutionEngineLimits limits) + { + this.JumpTable = jumpTable ?? JumpTable.Default; + this.Limits = limits; + this.ReferenceCounter = referenceCounter; + this.ResultStack = new EvaluationStack(referenceCounter); + } + + public virtual void Dispose() + { + InvocationStack.Clear(); + } + + /// + /// Start execution of the VM. + /// + /// + public virtual VMState Execute() + { + if (State == VMState.BREAK) + State = VMState.NONE; + while (State != VMState.HALT && State != VMState.FAULT) + ExecuteNext(); + return State; + } + + /// + /// Execute the next instruction. + /// + protected internal void ExecuteNext() + { + if (InvocationStack.Count == 0) + { + State = VMState.HALT; + } + else + { + try + { + ExecutionContext context = CurrentContext!; + Instruction instruction = context.CurrentInstruction ?? Instruction.RET; + PreExecuteInstruction(instruction); + try + { + JumpTable[instruction.OpCode](this, instruction); + } + catch (CatchableException ex) when (Limits.CatchEngineExceptions) + { + JumpTable.ExecuteThrow(this, ex.Message); + } + PostExecuteInstruction(instruction); + if (!isJumping) context.MoveNext(); + isJumping = false; + } + catch (Exception e) + { + OnFault(e); + } + } + } + + /// + /// Loads the specified context into the invocation stack. + /// + /// The context to load. + internal virtual void LoadContext(ExecutionContext context) + { + if (InvocationStack.Count >= Limits.MaxInvocationStackSize) + throw new InvalidOperationException($"MaxInvocationStackSize exceed: {InvocationStack.Count}"); + InvocationStack.Push(context); + if (EntryContext is null) EntryContext = context; + CurrentContext = context; + } + + /// + /// Called when a context is unloaded. + /// + /// The context being unloaded. + internal virtual void UnloadContext(ExecutionContext context) + { + if (InvocationStack.Count == 0) + { + CurrentContext = null; + EntryContext = null; + } + else + { + CurrentContext = InvocationStack.Peek(); + } + if (context.StaticFields != null && context.StaticFields != CurrentContext?.StaticFields) + { + context.StaticFields.ClearReferences(); + } + context.LocalVariables?.ClearReferences(); + context.Arguments?.ClearReferences(); + } + + /// + /// Create a new context with the specified script without loading. + /// + /// The script used to create the context. + /// The number of values that the context should return when it is unloaded. + /// The pointer indicating the current instruction. + /// The created context. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected ExecutionContext CreateContext(Script script, int rvcount, int initialPosition) + { + return new ExecutionContext(script, rvcount, ReferenceCounter) + { + InstructionPointer = initialPosition + }; + } + + /// + /// Create a new context with the specified script and load it. + /// + /// The script used to create the context. + /// The number of values that the context should return when it is unloaded. + /// The pointer indicating the current instruction. + /// The created context. + public ExecutionContext LoadScript(Script script, int rvcount = -1, int initialPosition = 0) + { + ExecutionContext context = CreateContext(script, rvcount, initialPosition); + LoadContext(context); + return context; + } + + /// + /// Called when an exception that cannot be caught by the VM is thrown. + /// + /// The exception that caused the state. + protected virtual void OnFault(Exception ex) + { + State = VMState.FAULT; + } + + /// + /// Called when the state of the VM changed. + /// + protected virtual void OnStateChanged() + { + } + + /// + /// Returns the item at the specified index from the top of the current stack without removing it. + /// + /// The index of the object from the top of the stack. + /// The item at the specified index. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public StackItem Peek(int index = 0) + { + return CurrentContext!.EvaluationStack.Peek(index); + } + + /// + /// Removes and returns the item at the top of the current stack. + /// + /// The item removed from the top of the stack. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public StackItem Pop() + { + return CurrentContext!.EvaluationStack.Pop(); + } + + /// + /// Removes and returns the item at the top of the current stack and convert it to the specified type. + /// + /// The type to convert to. + /// The item removed from the top of the stack. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Pop() where T : StackItem + { + return CurrentContext!.EvaluationStack.Pop(); + } + + /// + /// Called after an instruction is executed. + /// + protected virtual void PostExecuteInstruction(Instruction instruction) + { + if (ReferenceCounter.Count < Limits.MaxStackSize) return; + if (ReferenceCounter.CheckZeroReferred() > Limits.MaxStackSize) + throw new InvalidOperationException($"MaxStackSize exceed: {ReferenceCounter.Count}"); + } + + /// + /// Called before an instruction is executed. + /// + protected virtual void PreExecuteInstruction(Instruction instruction) { } + + /// + /// Pushes an item onto the top of the current stack. + /// + /// The item to be pushed. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Push(StackItem item) + { + CurrentContext!.EvaluationStack.Push(item); + } + } +} diff --git a/src/Neo.VM/ExecutionEngineLimits.cs b/src/Neo.VM/ExecutionEngineLimits.cs new file mode 100644 index 0000000000..6670572fb4 --- /dev/null +++ b/src/Neo.VM/ExecutionEngineLimits.cs @@ -0,0 +1,88 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ExecutionEngineLimits.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; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ + /// + /// Represents the restrictions on the VM. + /// + public sealed record ExecutionEngineLimits + { + /// + /// The default strategy. + /// + public static readonly ExecutionEngineLimits Default = new(); + + /// + /// The maximum number of bits that and can shift. + /// + public int MaxShift { get; init; } = 256; + + /// + /// The maximum number of items that can be contained in the VM's evaluation stacks and slots. + /// + public uint MaxStackSize { get; init; } = 2 * 1024; + + /// + /// The maximum size of an item in the VM. + /// + public uint MaxItemSize { get; init; } = ushort.MaxValue * 2; + + /// + /// The largest comparable size. If a or exceeds this size, comparison operations on it cannot be performed in the VM. + /// + public uint MaxComparableSize { get; init; } = 65536; + + /// + /// The maximum number of frames in the invocation stack of the VM. + /// + public uint MaxInvocationStackSize { get; init; } = 1024; + + /// + /// The maximum nesting depth of -- blocks. + /// + public uint MaxTryNestingDepth { get; init; } = 16; + + /// + /// Allow to catch the ExecutionEngine Exceptions + /// + public bool CatchEngineExceptions { get; init; } = true; + + /// + /// Assert that the size of the item meets the limit. + /// + /// The size to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AssertMaxItemSize(int size) + { + if (size < 0 || size > MaxItemSize) + { + throw new InvalidOperationException($"MaxItemSize exceed: {size}"); + } + } + + /// + /// Assert that the number of bits shifted meets the limit. + /// + /// The number of bits shifted. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AssertShift(int shift) + { + if (shift > MaxShift || shift < 0) + { + throw new InvalidOperationException($"Invalid shift value: {shift}"); + } + } + } +} diff --git a/src/Neo.VM/GlobalSuppressions.cs b/src/Neo.VM/GlobalSuppressions.cs new file mode 100644 index 0000000000..acf6e71c9c --- /dev/null +++ b/src/Neo.VM/GlobalSuppressions.cs @@ -0,0 +1,19 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// GlobalSuppressions.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. + +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Usage", "CA1816")] diff --git a/src/Neo.VM/Instruction.cs b/src/Neo.VM/Instruction.cs new file mode 100644 index 0000000000..b92e9e3bec --- /dev/null +++ b/src/Neo.VM/Instruction.cs @@ -0,0 +1,235 @@ +// 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 System; +using System.Buffers.Binary; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Neo.VM +{ + /// + /// Represents instructions in the VM script. + /// + [DebuggerDisplay("OpCode={OpCode}")] + public class Instruction + { + /// + /// Represents the instruction with . + /// + public static Instruction RET { get; } = new Instruction(OpCode.RET); + + /// + /// The of the instruction. + /// + public readonly OpCode OpCode; + + /// + /// The operand of the instruction. + /// + public readonly ReadOnlyMemory Operand; + + private static readonly int[] OperandSizePrefixTable = new int[256]; + private static readonly int[] OperandSizeTable = new int[256]; + + /// + /// Gets the size of the instruction. + /// + public int Size + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + int prefixSize = OperandSizePrefixTable[(int)OpCode]; + return prefixSize > 0 + ? 1 + prefixSize + Operand.Length + : 1 + OperandSizeTable[(int)OpCode]; + } + } + + /// + /// Gets the first operand as . + /// + public short TokenI16 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return BinaryPrimitives.ReadInt16LittleEndian(Operand.Span); + } + } + + /// + /// Gets the first operand as . + /// + public int TokenI32 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return BinaryPrimitives.ReadInt32LittleEndian(Operand.Span); + } + } + + /// + /// Gets the second operand as . + /// + public int TokenI32_1 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return BinaryPrimitives.ReadInt32LittleEndian(Operand.Span[4..]); + } + } + + /// + /// Gets the first operand as . + /// + public sbyte TokenI8 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return (sbyte)Operand.Span[0]; + } + } + + /// + /// Gets the second operand as . + /// + public sbyte TokenI8_1 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return (sbyte)Operand.Span[1]; + } + } + + /// + /// Gets the operand as . + /// + public string TokenString + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return Encoding.ASCII.GetString(Operand.Span); + } + } + + /// + /// Gets the first operand as . + /// + public ushort TokenU16 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return BinaryPrimitives.ReadUInt16LittleEndian(Operand.Span); + } + } + + /// + /// Gets the first operand as . + /// + public uint TokenU32 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return BinaryPrimitives.ReadUInt32LittleEndian(Operand.Span); + } + } + + /// + /// Gets the first operand as . + /// + public byte TokenU8 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return Operand.Span[0]; + } + } + + /// + /// Gets the second operand as . + /// + public byte TokenU8_1 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return Operand.Span[1]; + } + } + + static Instruction() + { + foreach (FieldInfo field in typeof(OpCode).GetFields(BindingFlags.Public | BindingFlags.Static)) + { + OperandSizeAttribute? attribute = field.GetCustomAttribute(); + if (attribute == null) continue; + int index = (int)(OpCode)field.GetValue(null)!; + OperandSizePrefixTable[index] = attribute.SizePrefix; + OperandSizeTable[index] = attribute.Size; + } + } + + private Instruction(OpCode opcode) + { + this.OpCode = opcode; + if (!Enum.IsDefined(typeof(OpCode), opcode)) throw new BadScriptException(); + } + + internal Instruction(ReadOnlyMemory script, int ip) : this((OpCode)script.Span[ip++]) + { + ReadOnlySpan span = script.Span; + int operandSizePrefix = OperandSizePrefixTable[(int)OpCode]; + int operandSize = 0; + switch (operandSizePrefix) + { + case 0: + operandSize = OperandSizeTable[(int)OpCode]; + break; + case 1: + if (ip >= span.Length) + throw new BadScriptException($"Instruction out of bounds. InstructionPointer: {ip}"); + operandSize = span[ip]; + break; + case 2: + if (ip + 1 >= span.Length) + throw new BadScriptException($"Instruction out of bounds. InstructionPointer: {ip}"); + operandSize = BinaryPrimitives.ReadUInt16LittleEndian(span[ip..]); + break; + case 4: + if (ip + 3 >= span.Length) + throw new BadScriptException($"Instruction out of bounds. InstructionPointer: {ip}"); + operandSize = BinaryPrimitives.ReadInt32LittleEndian(span[ip..]); + if (operandSize < 0) + throw new BadScriptException($"Instruction out of bounds. InstructionPointer: {ip}, operandSize: {operandSize}"); + break; + } + ip += operandSizePrefix; + if (operandSize > 0) + { + if (ip + operandSize > script.Length) + throw new BadScriptException($"Instrucion out of bounds. InstructionPointer: {ip}, operandSize: {operandSize}, length: {script.Length}"); + Operand = script.Slice(ip, operandSize); + } + } + } +} diff --git a/src/Neo.VM/JumpTable/JumpTable.Bitwisee.cs b/src/Neo.VM/JumpTable/JumpTable.Bitwisee.cs new file mode 100644 index 0000000000..65a8e490d1 --- /dev/null +++ b/src/Neo.VM/JumpTable/JumpTable.Bitwisee.cs @@ -0,0 +1,65 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JumpTable.Bitwisee.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.Runtime.CompilerServices; + +namespace Neo.VM +{ + public partial class JumpTable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Invert(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop().GetInteger(); + engine.Push(~x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void And(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + engine.Push(x1 & x2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Or(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + engine.Push(x1 | x2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void XOr(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + engine.Push(x1 ^ x2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Equal(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop(); + var x1 = engine.Pop(); + engine.Push(x1.Equals(x2, engine.Limits)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void NotEqual(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop(); + var x1 = engine.Pop(); + engine.Push(!x1.Equals(x2, engine.Limits)); + } + } +} diff --git a/src/Neo.VM/JumpTable/JumpTable.Compound.cs b/src/Neo.VM/JumpTable/JumpTable.Compound.cs new file mode 100644 index 0000000000..916caf02b8 --- /dev/null +++ b/src/Neo.VM/JumpTable/JumpTable.Compound.cs @@ -0,0 +1,390 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JumpTable.Compound.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.VM.Types; +using System; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using VMArray = Neo.VM.Types.Array; + +namespace Neo.VM +{ + public partial class JumpTable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PackMap(ExecutionEngine engine, Instruction instruction) + { + var size = (int)engine.Pop().GetInteger(); + if (size < 0 || size * 2 > engine.CurrentContext!.EvaluationStack.Count) + throw new InvalidOperationException($"The value {size} is out of range."); + Map map = new(engine.ReferenceCounter); + for (var i = 0; i < size; i++) + { + var key = engine.Pop(); + var value = engine.Pop(); + map[key] = value; + } + engine.Push(map); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PackStruct(ExecutionEngine engine, Instruction instruction) + { + var size = (int)engine.Pop().GetInteger(); + if (size < 0 || size > engine.CurrentContext!.EvaluationStack.Count) + throw new InvalidOperationException($"The value {size} is out of range."); + Struct @struct = new(engine.ReferenceCounter); + for (var i = 0; i < size; i++) + { + var item = engine.Pop(); + @struct.Add(item); + } + engine.Push(@struct); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Pack(ExecutionEngine engine, Instruction instruction) + { + var size = (int)engine.Pop().GetInteger(); + if (size < 0 || size > engine.CurrentContext!.EvaluationStack.Count) + throw new InvalidOperationException($"The value {size} is out of range."); + VMArray array = new(engine.ReferenceCounter); + for (var i = 0; i < size; i++) + { + var item = engine.Pop(); + array.Add(item); + } + engine.Push(array); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Unpack(ExecutionEngine engine, Instruction instruction) + { + var compound = engine.Pop(); + switch (compound) + { + case Map map: + foreach (var (key, value) in map.Reverse()) + { + engine.Push(value); + engine.Push(key); + } + break; + case VMArray array: + for (var i = array.Count - 1; i >= 0; i--) + { + engine.Push(array[i]); + } + break; + default: + throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {compound.Type}"); + } + engine.Push(compound.Count); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void NewArray0(ExecutionEngine engine, Instruction instruction) + { + engine.Push(new VMArray(engine.ReferenceCounter)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void NewArray(ExecutionEngine engine, Instruction instruction) + { + var n = (int)engine.Pop().GetInteger(); + if (n < 0 || n > engine.Limits.MaxStackSize) + throw new InvalidOperationException($"MaxStackSize exceed: {n}"); + + engine.Push(new VMArray(engine.ReferenceCounter, Enumerable.Repeat(StackItem.Null, n))); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void NewArray_T(ExecutionEngine engine, Instruction instruction) + { + var n = (int)engine.Pop().GetInteger(); + if (n < 0 || n > engine.Limits.MaxStackSize) + throw new InvalidOperationException($"MaxStackSize exceed: {n}"); + + var type = (StackItemType)instruction.TokenU8; + if (!Enum.IsDefined(typeof(StackItemType), type)) + throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {instruction.TokenU8}"); + + var item = instruction.TokenU8 switch + { + (byte)StackItemType.Boolean => StackItem.False, + (byte)StackItemType.Integer => Integer.Zero, + (byte)StackItemType.ByteString => ByteString.Empty, + _ => StackItem.Null + }; + + engine.Push(new VMArray(engine.ReferenceCounter, Enumerable.Repeat(item, n))); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void NewStruct0(ExecutionEngine engine, Instruction instruction) + { + engine.Push(new Struct(engine.ReferenceCounter)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void NewStruct(ExecutionEngine engine, Instruction instruction) + { + var n = (int)engine.Pop().GetInteger(); + if (n < 0 || n > engine.Limits.MaxStackSize) + throw new InvalidOperationException($"MaxStackSize exceed: {n}"); + Struct result = new(engine.ReferenceCounter); + for (var i = 0; i < n; i++) + result.Add(StackItem.Null); + engine.Push(result); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void NewMap(ExecutionEngine engine, Instruction instruction) + { + engine.Push(new Map(engine.ReferenceCounter)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Size(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop(); + switch (x) + { + case CompoundType compound: + engine.Push(compound.Count); + break; + case PrimitiveType primitive: + engine.Push(primitive.Size); + break; + case Types.Buffer buffer: + engine.Push(buffer.Size); + break; + default: + throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {x.Type}"); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void HasKey(ExecutionEngine engine, Instruction instruction) + { + var key = engine.Pop(); + var x = engine.Pop(); + switch (x) + { + case VMArray array: + { + var index = (int)key.GetInteger(); + if (index < 0) + throw new InvalidOperationException($"The negative value {index} is invalid for OpCode.{instruction.OpCode}."); + engine.Push(index < array.Count); + break; + } + case Map map: + { + engine.Push(map.ContainsKey(key)); + break; + } + case Types.Buffer buffer: + { + var index = (int)key.GetInteger(); + if (index < 0) + throw new InvalidOperationException($"The negative value {index} is invalid for OpCode.{instruction.OpCode}."); + engine.Push(index < buffer.Size); + break; + } + case ByteString array: + { + var index = (int)key.GetInteger(); + if (index < 0) + throw new InvalidOperationException($"The negative value {index} is invalid for OpCode.{instruction.OpCode}."); + engine.Push(index < array.Size); + break; + } + default: + throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {x.Type}"); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Keys(ExecutionEngine engine, Instruction instruction) + { + var map = engine.Pop(); + engine.Push(new VMArray(engine.ReferenceCounter, map.Keys)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Values(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop(); + var values = x switch + { + VMArray array => array, + Map map => map.Values, + _ => throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {x.Type}"), + }; + VMArray newArray = new(engine.ReferenceCounter); + foreach (var item in values) + if (item is Struct s) + newArray.Add(s.Clone(engine.Limits)); + else + newArray.Add(item); + engine.Push(newArray); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PickItem(ExecutionEngine engine, Instruction instruction) + { + var key = engine.Pop(); + var x = engine.Pop(); + switch (x) + { + case VMArray array: + { + var index = (int)key.GetInteger(); + if (index < 0 || index >= array.Count) + throw new CatchableException($"The value {index} is out of range."); + engine.Push(array[index]); + break; + } + case Map map: + { + if (!map.TryGetValue(key, out var value)) + throw new CatchableException($"Key not found in {nameof(Map)}"); + engine.Push(value); + break; + } + case PrimitiveType primitive: + { + var byteArray = primitive.GetSpan(); + var index = (int)key.GetInteger(); + if (index < 0 || index >= byteArray.Length) + throw new CatchableException($"The value {index} is out of range."); + engine.Push((BigInteger)byteArray[index]); + break; + } + case Types.Buffer buffer: + { + var index = (int)key.GetInteger(); + if (index < 0 || index >= buffer.Size) + throw new CatchableException($"The value {index} is out of range."); + engine.Push((BigInteger)buffer.InnerBuffer.Span[index]); + break; + } + default: + throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {x.Type}"); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Append(ExecutionEngine engine, Instruction instruction) + { + var newItem = engine.Pop(); + var array = engine.Pop(); + if (newItem is Struct s) newItem = s.Clone(engine.Limits); + array.Add(newItem); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void SetItem(ExecutionEngine engine, Instruction instruction) + { + var value = engine.Pop(); + if (value is Struct s) value = s.Clone(engine.Limits); + var key = engine.Pop(); + var x = engine.Pop(); + switch (x) + { + case VMArray array: + { + var index = (int)key.GetInteger(); + if (index < 0 || index >= array.Count) + throw new CatchableException($"The value {index} is out of range."); + array[index] = value; + break; + } + case Map map: + { + map[key] = value; + break; + } + case Types.Buffer buffer: + { + var index = (int)key.GetInteger(); + if (index < 0 || index >= buffer.Size) + throw new CatchableException($"The value {index} is out of range."); + if (value is not PrimitiveType p) + throw new InvalidOperationException($"Value must be a primitive type in {instruction.OpCode}"); + var b = (int)p.GetInteger(); + if (b < sbyte.MinValue || b > byte.MaxValue) + throw new InvalidOperationException($"Overflow in {instruction.OpCode}, {b} is not a byte type."); + buffer.InnerBuffer.Span[index] = (byte)b; + break; + } + default: + throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {x.Type}"); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void ReverseItems(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop(); + switch (x) + { + case VMArray array: + array.Reverse(); + break; + case Types.Buffer buffer: + buffer.InnerBuffer.Span.Reverse(); + break; + default: + throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {x.Type}"); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Remove(ExecutionEngine engine, Instruction instruction) + { + var key = engine.Pop(); + var x = engine.Pop(); + switch (x) + { + case VMArray array: + var index = (int)key.GetInteger(); + if (index < 0 || index >= array.Count) + throw new InvalidOperationException($"The value {index} is out of range."); + array.RemoveAt(index); + break; + case Map map: + map.Remove(key); + break; + default: + throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {x.Type}"); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void ClearItems(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop(); + x.Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PopItem(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop(); + var index = x.Count - 1; + engine.Push(x[index]); + x.RemoveAt(index); + } + } +} diff --git a/src/Neo.VM/JumpTable/JumpTable.Control.cs b/src/Neo.VM/JumpTable/JumpTable.Control.cs new file mode 100644 index 0000000000..2415f1e614 --- /dev/null +++ b/src/Neo.VM/JumpTable/JumpTable.Control.cs @@ -0,0 +1,396 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JumpTable.Control.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.VM.Types; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ + public partial class JumpTable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Nop(ExecutionEngine engine, Instruction instruction) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Jmp(ExecutionEngine engine, Instruction instruction) + { + ExecuteJumpOffset(engine, instruction.TokenI8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Jmp_L(ExecutionEngine engine, Instruction instruction) + { + ExecuteJumpOffset(engine, instruction.TokenI32); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpIf(ExecutionEngine engine, Instruction instruction) + { + if (engine.Pop().GetBoolean()) + ExecuteJumpOffset(engine, instruction.TokenI8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpIf_L(ExecutionEngine engine, Instruction instruction) + { + if (engine.Pop().GetBoolean()) + ExecuteJumpOffset(engine, instruction.TokenI32); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpIfNot(ExecutionEngine engine, Instruction instruction) + { + if (!engine.Pop().GetBoolean()) + ExecuteJumpOffset(engine, instruction.TokenI8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpIfNot_L(ExecutionEngine engine, Instruction instruction) + { + if (!engine.Pop().GetBoolean()) + ExecuteJumpOffset(engine, instruction.TokenI32); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpEq(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 == x2) + ExecuteJumpOffset(engine, instruction.TokenI8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpEq_L(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 == x2) + ExecuteJumpOffset(engine, instruction.TokenI32); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpNe(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 != x2) + ExecuteJumpOffset(engine, instruction.TokenI8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpNe_L(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 != x2) + ExecuteJumpOffset(engine, instruction.TokenI32); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpGt(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 > x2) + ExecuteJumpOffset(engine, instruction.TokenI8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpGt_L(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 > x2) + ExecuteJumpOffset(engine, instruction.TokenI32); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpGe(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 >= x2) + ExecuteJumpOffset(engine, instruction.TokenI8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpGe_L(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 >= x2) + ExecuteJumpOffset(engine, instruction.TokenI32); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpLt(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 < x2) + ExecuteJumpOffset(engine, instruction.TokenI8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpLt_L(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 < x2) + ExecuteJumpOffset(engine, instruction.TokenI32); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpLe(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 <= x2) + ExecuteJumpOffset(engine, instruction.TokenI8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JMPLE_L(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 <= x2) + ExecuteJumpOffset(engine, instruction.TokenI32); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Call(ExecutionEngine engine, Instruction instruction) + { + ExecuteCall(engine, checked(engine.CurrentContext!.InstructionPointer + instruction.TokenI8)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Call_L(ExecutionEngine engine, Instruction instruction) + { + ExecuteCall(engine, checked(engine.CurrentContext!.InstructionPointer + instruction.TokenI32)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void CallA(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop(); + if (x.Script != engine.CurrentContext!.Script) + throw new InvalidOperationException("Pointers can't be shared between scripts"); + ExecuteCall(engine, x.Position); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void CallT(ExecutionEngine engine, Instruction instruction) + { + throw new InvalidOperationException($"Token not found: {instruction.TokenU16}"); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Abort(ExecutionEngine engine, Instruction instruction) + { + throw new Exception($"{OpCode.ABORT} is executed."); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Assert(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop().GetBoolean(); + if (!x) + throw new Exception($"{OpCode.ASSERT} is executed with false result."); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Throw(ExecutionEngine engine, Instruction instruction) + { + ExecuteThrow(engine, engine.Pop()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Try(ExecutionEngine engine, Instruction instruction) + { + int catchOffset = instruction.TokenI8; + int finallyOffset = instruction.TokenI8_1; + ExecuteTry(engine, catchOffset, finallyOffset); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Try_L(ExecutionEngine engine, Instruction instruction) + { + var catchOffset = instruction.TokenI32; + var finallyOffset = instruction.TokenI32_1; + ExecuteTry(engine, catchOffset, finallyOffset); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void EndTry(ExecutionEngine engine, Instruction instruction) + { + var endOffset = instruction.TokenI8; + ExecuteEndTry(engine, endOffset); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void EndTry_L(ExecutionEngine engine, Instruction instruction) + { + var endOffset = instruction.TokenI32; + ExecuteEndTry(engine, endOffset); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void EndFinally(ExecutionEngine engine, Instruction instruction) + { + if (engine.CurrentContext!.TryStack is null) + throw new InvalidOperationException($"The corresponding TRY block cannot be found."); + if (!engine.CurrentContext.TryStack.TryPop(out var currentTry)) + throw new InvalidOperationException($"The corresponding TRY block cannot be found."); + + if (engine.UncaughtException is null) + engine.CurrentContext.InstructionPointer = currentTry.EndPointer; + else + ExecuteThrow(engine, engine.UncaughtException); + + engine.isJumping = true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Ret(ExecutionEngine engine, Instruction instruction) + { + var context_pop = engine.InvocationStack.Pop(); + var stack_eval = engine.InvocationStack.Count == 0 ? engine.ResultStack : engine.InvocationStack.Peek().EvaluationStack; + if (context_pop.EvaluationStack != stack_eval) + { + if (context_pop.RVCount >= 0 && context_pop.EvaluationStack.Count != context_pop.RVCount) + throw new InvalidOperationException("RVCount doesn't match with EvaluationStack"); + context_pop.EvaluationStack.CopyTo(stack_eval); + } + if (engine.InvocationStack.Count == 0) + engine.State = VMState.HALT; + engine.UnloadContext(context_pop); + engine.isJumping = true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Syscall(ExecutionEngine engine, Instruction instruction) + { + throw new InvalidOperationException($"Syscall not found: {instruction.TokenU32}"); + } + + #region Execute methods + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void ExecuteCall(ExecutionEngine engine, int position) + { + engine.LoadContext(engine.CurrentContext!.Clone(position)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void ExecuteEndTry(ExecutionEngine engine, int endOffset) + { + if (engine.CurrentContext!.TryStack is null) + throw new InvalidOperationException($"The corresponding TRY block cannot be found."); + if (!engine.CurrentContext.TryStack.TryPeek(out var currentTry)) + throw new InvalidOperationException($"The corresponding TRY block cannot be found."); + if (currentTry.State == ExceptionHandlingState.Finally) + throw new InvalidOperationException($"The opcode {OpCode.ENDTRY} can't be executed in a FINALLY block."); + + var endPointer = checked(engine.CurrentContext.InstructionPointer + endOffset); + if (currentTry.HasFinally) + { + currentTry.State = ExceptionHandlingState.Finally; + currentTry.EndPointer = endPointer; + engine.CurrentContext.InstructionPointer = currentTry.FinallyPointer; + } + else + { + engine.CurrentContext.TryStack.Pop(); + engine.CurrentContext.InstructionPointer = endPointer; + } + engine.isJumping = true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void ExecuteJump(ExecutionEngine engine, int position) + { + if (position < 0 || position >= engine.CurrentContext!.Script.Length) + throw new ArgumentOutOfRangeException($"Jump out of range for position: {position}"); + engine.CurrentContext.InstructionPointer = position; + engine.isJumping = true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void ExecuteJumpOffset(ExecutionEngine engine, int offset) + { + ExecuteJump(engine, checked(engine.CurrentContext!.InstructionPointer + offset)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void ExecuteTry(ExecutionEngine engine, int catchOffset, int finallyOffset) + { + if (catchOffset == 0 && finallyOffset == 0) + throw new InvalidOperationException($"catchOffset and finallyOffset can't be 0 in a TRY block"); + if (engine.CurrentContext!.TryStack is null) + engine.CurrentContext.TryStack = new Stack(); + else if (engine.CurrentContext.TryStack.Count >= engine.Limits.MaxTryNestingDepth) + throw new InvalidOperationException("MaxTryNestingDepth exceed."); + var catchPointer = catchOffset == 0 ? -1 : checked(engine.CurrentContext.InstructionPointer + catchOffset); + var finallyPointer = finallyOffset == 0 ? -1 : checked(engine.CurrentContext.InstructionPointer + finallyOffset); + engine.CurrentContext.TryStack.Push(new ExceptionHandlingContext(catchPointer, finallyPointer)); + } + + public virtual void ExecuteThrow(ExecutionEngine engine, StackItem? ex) + { + engine.UncaughtException = ex; + + var pop = 0; + foreach (var executionContext in engine.InvocationStack) + { + if (executionContext.TryStack != null) + { + while (executionContext.TryStack.TryPeek(out var tryContext)) + { + if (tryContext.State == ExceptionHandlingState.Finally || (tryContext.State == ExceptionHandlingState.Catch && !tryContext.HasFinally)) + { + executionContext.TryStack.Pop(); + continue; + } + for (var i = 0; i < pop; i++) + { + engine.UnloadContext(engine.InvocationStack.Pop()); + } + if (tryContext.State == ExceptionHandlingState.Try && tryContext.HasCatch) + { + tryContext.State = ExceptionHandlingState.Catch; + engine.Push(engine.UncaughtException!); + executionContext.InstructionPointer = tryContext.CatchPointer; + engine.UncaughtException = null; + } + else + { + tryContext.State = ExceptionHandlingState.Finally; + executionContext.InstructionPointer = tryContext.FinallyPointer; + } + engine.isJumping = true; + return; + } + } + ++pop; + } + + throw new VMUnhandledException(engine.UncaughtException!); + } + + #endregion + } +} diff --git a/src/Neo.VM/JumpTable/JumpTable.Numeric.cs b/src/Neo.VM/JumpTable/JumpTable.Numeric.cs new file mode 100644 index 0000000000..60586cb77e --- /dev/null +++ b/src/Neo.VM/JumpTable/JumpTable.Numeric.cs @@ -0,0 +1,265 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JumpTable.Numeric.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.Numerics; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ + public partial class JumpTable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Sign(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop().GetInteger(); + engine.Push(x.Sign); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Abs(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop().GetInteger(); + engine.Push(BigInteger.Abs(x)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Negate(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop().GetInteger(); + engine.Push(-x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Inc(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop().GetInteger(); + engine.Push(x + 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Dec(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop().GetInteger(); + engine.Push(x - 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Add(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + engine.Push(x1 + x2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Sub(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + engine.Push(x1 - x2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Mul(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + engine.Push(x1 * x2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Div(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + engine.Push(x1 / x2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Mod(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + engine.Push(x1 % x2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Pow(ExecutionEngine engine, Instruction instruction) + { + var exponent = (int)engine.Pop().GetInteger(); + engine.Limits.AssertShift(exponent); + var value = engine.Pop().GetInteger(); + engine.Push(BigInteger.Pow(value, exponent)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Sqrt(ExecutionEngine engine, Instruction instruction) + { + engine.Push(engine.Pop().GetInteger().Sqrt()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void ModMul(ExecutionEngine engine, Instruction instruction) + { + var modulus = engine.Pop().GetInteger(); + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + engine.Push(x1 * x2 % modulus); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void ModPow(ExecutionEngine engine, Instruction instruction) + { + var modulus = engine.Pop().GetInteger(); + var exponent = engine.Pop().GetInteger(); + var value = engine.Pop().GetInteger(); + var result = exponent == -1 + ? value.ModInverse(modulus) + : BigInteger.ModPow(value, exponent, modulus); + engine.Push(result); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Shl(ExecutionEngine engine, Instruction instruction) + { + var shift = (int)engine.Pop().GetInteger(); + engine.Limits.AssertShift(shift); + if (shift == 0) return; + var x = engine.Pop().GetInteger(); + engine.Push(x << shift); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Shr(ExecutionEngine engine, Instruction instruction) + { + var shift = (int)engine.Pop().GetInteger(); + engine.Limits.AssertShift(shift); + if (shift == 0) return; + var x = engine.Pop().GetInteger(); + engine.Push(x >> shift); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Not(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop().GetBoolean(); + engine.Push(!x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void BoolAnd(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetBoolean(); + var x1 = engine.Pop().GetBoolean(); + engine.Push(x1 && x2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void BoolOr(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetBoolean(); + var x1 = engine.Pop().GetBoolean(); + engine.Push(x1 || x2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Nz(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop().GetInteger(); + engine.Push(!x.IsZero); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void NumEqual(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + engine.Push(x1 == x2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void NumNotEqual(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + engine.Push(x1 != x2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Lt(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop(); + var x1 = engine.Pop(); + if (x1.IsNull || x2.IsNull) + engine.Push(false); + else + engine.Push(x1.GetInteger() < x2.GetInteger()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Le(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop(); + var x1 = engine.Pop(); + if (x1.IsNull || x2.IsNull) + engine.Push(false); + else + engine.Push(x1.GetInteger() <= x2.GetInteger()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Gt(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop(); + var x1 = engine.Pop(); + if (x1.IsNull || x2.IsNull) + engine.Push(false); + else + engine.Push(x1.GetInteger() > x2.GetInteger()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Ge(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop(); + var x1 = engine.Pop(); + if (x1.IsNull || x2.IsNull) + engine.Push(false); + else + engine.Push(x1.GetInteger() >= x2.GetInteger()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Min(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + engine.Push(BigInteger.Min(x1, x2)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Max(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + engine.Push(BigInteger.Max(x1, x2)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Within(ExecutionEngine engine, Instruction instruction) + { + var b = engine.Pop().GetInteger(); + var a = engine.Pop().GetInteger(); + var x = engine.Pop().GetInteger(); + engine.Push(a <= x && x < b); + } + } +} diff --git a/src/Neo.VM/JumpTable/JumpTable.Push.cs b/src/Neo.VM/JumpTable/JumpTable.Push.cs new file mode 100644 index 0000000000..066330f5bb --- /dev/null +++ b/src/Neo.VM/JumpTable/JumpTable.Push.cs @@ -0,0 +1,213 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JumpTable.Push.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.VM.Types; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ + public partial class JumpTable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushInt8(ExecutionEngine engine, Instruction instruction) + { + engine.Push(new BigInteger(instruction.Operand.Span)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushInt16(ExecutionEngine engine, Instruction instruction) + { + engine.Push(new BigInteger(instruction.Operand.Span)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushInt32(ExecutionEngine engine, Instruction instruction) + { + engine.Push(new BigInteger(instruction.Operand.Span)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushInt64(ExecutionEngine engine, Instruction instruction) + { + engine.Push(new BigInteger(instruction.Operand.Span)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushInt128(ExecutionEngine engine, Instruction instruction) + { + engine.Push(new BigInteger(instruction.Operand.Span)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushInt256(ExecutionEngine engine, Instruction instruction) + { + engine.Push(new BigInteger(instruction.Operand.Span)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushT(ExecutionEngine engine, Instruction instruction) + { + engine.Push(StackItem.True); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushF(ExecutionEngine engine, Instruction instruction) + { + engine.Push(StackItem.False); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushA(ExecutionEngine engine, Instruction instruction) + { + var position = checked(engine.CurrentContext!.InstructionPointer + instruction.TokenI32); + if (position < 0 || position > engine.CurrentContext.Script.Length) + throw new InvalidOperationException($"Bad pointer address(Instruction instruction) {position}"); + engine.Push(new Pointer(engine.CurrentContext.Script, position)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushNull(ExecutionEngine engine, Instruction instruction) + { + engine.Push(StackItem.Null); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushData1(ExecutionEngine engine, Instruction instruction) + { + engine.Limits.AssertMaxItemSize(instruction.Operand.Length); + engine.Push(instruction.Operand); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushData2(ExecutionEngine engine, Instruction instruction) + { + engine.Limits.AssertMaxItemSize(instruction.Operand.Length); + engine.Push(instruction.Operand); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushData4(ExecutionEngine engine, Instruction instruction) + { + engine.Limits.AssertMaxItemSize(instruction.Operand.Length); + engine.Push(instruction.Operand); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushM1(ExecutionEngine engine, Instruction instruction) + { + engine.Push(-1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Push0(ExecutionEngine engine, Instruction instruction) + { + engine.Push(0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Push1(ExecutionEngine engine, Instruction instruction) + { + engine.Push(1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Push2(ExecutionEngine engine, Instruction instruction) + { + engine.Push(2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Push3(ExecutionEngine engine, Instruction instruction) + { + engine.Push(3); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Push4(ExecutionEngine engine, Instruction instruction) + { + engine.Push(4); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Push5(ExecutionEngine engine, Instruction instruction) + { + engine.Push(5); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Push6(ExecutionEngine engine, Instruction instruction) + { + engine.Push(6); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Push7(ExecutionEngine engine, Instruction instruction) + { + engine.Push(7); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Push8(ExecutionEngine engine, Instruction instruction) + { + engine.Push(8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Push9(ExecutionEngine engine, Instruction instruction) + { + engine.Push(9); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Push10(ExecutionEngine engine, Instruction instruction) + { + engine.Push(10); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Push11(ExecutionEngine engine, Instruction instruction) + { + engine.Push(11); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Push12(ExecutionEngine engine, Instruction instruction) + { + engine.Push(12); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Push13(ExecutionEngine engine, Instruction instruction) + { + engine.Push(13); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Push14(ExecutionEngine engine, Instruction instruction) + { + engine.Push(14); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Push15(ExecutionEngine engine, Instruction instruction) + { + engine.Push(15); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Push16(ExecutionEngine engine, Instruction instruction) + { + engine.Push(16); + } + } +} diff --git a/src/Neo.VM/JumpTable/JumpTable.Slot.cs b/src/Neo.VM/JumpTable/JumpTable.Slot.cs new file mode 100644 index 0000000000..1305701e98 --- /dev/null +++ b/src/Neo.VM/JumpTable/JumpTable.Slot.cs @@ -0,0 +1,363 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JumpTable.Slot.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.VM; +using Neo.VM.Types; +using System; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ + public partial class JumpTable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void InitSSlot(ExecutionEngine engine, Instruction instruction) + { + if (engine.CurrentContext!.StaticFields != null) + throw new InvalidOperationException($"{instruction.OpCode} cannot be executed twice."); + if (instruction.TokenU8 == 0) + throw new InvalidOperationException($"The operand {instruction.TokenU8} is invalid for OpCode.{instruction.OpCode}."); + engine.CurrentContext.StaticFields = new Slot(instruction.TokenU8, engine.ReferenceCounter); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void InitSlot(ExecutionEngine engine, Instruction instruction) + { + if (engine.CurrentContext!.LocalVariables != null || engine.CurrentContext.Arguments != null) + throw new InvalidOperationException($"{instruction.OpCode} cannot be executed twice."); + if (instruction.TokenU16 == 0) + throw new InvalidOperationException($"The operand {instruction.TokenU16} is invalid for OpCode.{instruction.OpCode}."); + if (instruction.TokenU8 > 0) + { + engine.CurrentContext.LocalVariables = new Slot(instruction.TokenU8, engine.ReferenceCounter); + } + if (instruction.TokenU8_1 > 0) + { + var items = new StackItem[instruction.TokenU8_1]; + for (var i = 0; i < instruction.TokenU8_1; i++) + { + items[i] = engine.Pop(); + } + engine.CurrentContext.Arguments = new Slot(items, engine.ReferenceCounter); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdSFld0(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.StaticFields, 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdSFld1(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.StaticFields, 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdSFld2(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.StaticFields, 2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdSFld3(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.StaticFields, 3); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdSFld4(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.StaticFields, 4); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdSFld5(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.StaticFields, 5); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdSFld6(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.StaticFields, 6); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdSFld(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.StaticFields, instruction.TokenU8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StSFld0(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.StaticFields, 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StSFld1(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.StaticFields, 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StSFld2(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.StaticFields, 2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StSFld3(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.StaticFields, 3); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StSFld4(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.StaticFields, 4); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StSFld5(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.StaticFields, 5); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StSFld6(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.StaticFields, 6); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StSFld(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.StaticFields, instruction.TokenU8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdLoc0(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.LocalVariables, 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdLoc1(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.LocalVariables, 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdLoc2(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.LocalVariables, 2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdLoc3(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.LocalVariables, 3); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdLoc4(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.LocalVariables, 4); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdLoc5(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.LocalVariables, 5); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdLoc6(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.LocalVariables, 6); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdLoc(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.LocalVariables, instruction.TokenU8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StLoc0(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.LocalVariables, 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StLoc1(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.LocalVariables, 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StLoc2(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.LocalVariables, 2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StLoc3(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.LocalVariables, 3); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StLoc4(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.LocalVariables, 4); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StLoc5(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.LocalVariables, 5); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StLoc6(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.LocalVariables, 6); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StLoc(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.LocalVariables, instruction.TokenU8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdArg0(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.Arguments, 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdArg1(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.Arguments, 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdArg2(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.Arguments, 2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdArg3(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.Arguments, 3); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdArg4(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.Arguments, 4); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdArg5(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.Arguments, 5); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdArg6(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.Arguments, 6); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void LdArg(ExecutionEngine engine, Instruction instruction) + { + ExecuteLoadFromSlot(engine, engine.CurrentContext!.Arguments, instruction.TokenU8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StArg0(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.Arguments, 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StArg1(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.Arguments, 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StArg2(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.Arguments, 2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StArg3(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.Arguments, 3); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StArg4(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.Arguments, 4); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StArg5(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.Arguments, 5); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StArg6(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.Arguments, 6); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void StArg(ExecutionEngine engine, Instruction instruction) + { + ExecuteStoreToSlot(engine, engine.CurrentContext!.Arguments, instruction.TokenU8); + } + + #region Execute methods + + public virtual void ExecuteStoreToSlot(ExecutionEngine engine, Slot? slot, int index) + { + if (slot is null) + throw new InvalidOperationException("Slot has not been initialized."); + if (index < 0 || index >= slot.Count) + throw new InvalidOperationException($"Index out of range when storing to slot: {index}"); + slot[index] = engine.Pop(); + } + + public virtual void ExecuteLoadFromSlot(ExecutionEngine engine, Slot? slot, int index) + { + if (slot is null) + throw new InvalidOperationException("Slot has not been initialized."); + if (index < 0 || index >= slot.Count) + throw new InvalidOperationException($"Index out of range when loading from slot: {index}"); + engine.Push(slot[index]); + } + + #endregion + } +} diff --git a/src/Neo.VM/JumpTable/JumpTable.Splice.cs b/src/Neo.VM/JumpTable/JumpTable.Splice.cs new file mode 100644 index 0000000000..95bad5314f --- /dev/null +++ b/src/Neo.VM/JumpTable/JumpTable.Splice.cs @@ -0,0 +1,106 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JumpTable.Splice.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; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ + public partial class JumpTable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void NewBuffer(ExecutionEngine engine, Instruction instruction) + { + int length = (int)engine.Pop().GetInteger(); + engine.Limits.AssertMaxItemSize(length); + engine.Push(new Types.Buffer(length)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Memcpy(ExecutionEngine engine, Instruction instruction) + { + int count = (int)engine.Pop().GetInteger(); + if (count < 0) + throw new InvalidOperationException($"The value {count} is out of range."); + int si = (int)engine.Pop().GetInteger(); + if (si < 0) + throw new InvalidOperationException($"The value {si} is out of range."); + ReadOnlySpan src = engine.Pop().GetSpan(); + if (checked(si + count) > src.Length) + throw new InvalidOperationException($"The value {count} is out of range."); + int di = (int)engine.Pop().GetInteger(); + if (di < 0) + throw new InvalidOperationException($"The value {di} is out of range."); + Types.Buffer dst = engine.Pop(); + if (checked(di + count) > dst.Size) + throw new InvalidOperationException($"The value {count} is out of range."); + src.Slice(si, count).CopyTo(dst.InnerBuffer.Span[di..]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Cat(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetSpan(); + var x1 = engine.Pop().GetSpan(); + int length = x1.Length + x2.Length; + engine.Limits.AssertMaxItemSize(length); + Types.Buffer result = new(length, false); + x1.CopyTo(result.InnerBuffer.Span); + x2.CopyTo(result.InnerBuffer.Span[x1.Length..]); + engine.Push(result); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void SubStr(ExecutionEngine engine, Instruction instruction) + { + int count = (int)engine.Pop().GetInteger(); + if (count < 0) + throw new InvalidOperationException($"The value {count} is out of range."); + int index = (int)engine.Pop().GetInteger(); + if (index < 0) + throw new InvalidOperationException($"The value {index} is out of range."); + var x = engine.Pop().GetSpan(); + if (index + count > x.Length) + throw new InvalidOperationException($"The value {count} is out of range."); + Types.Buffer result = new(count, false); + x.Slice(index, count).CopyTo(result.InnerBuffer.Span); + engine.Push(result); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Left(ExecutionEngine engine, Instruction instruction) + { + int count = (int)engine.Pop().GetInteger(); + if (count < 0) + throw new InvalidOperationException($"The value {count} is out of range."); + var x = engine.Pop().GetSpan(); + if (count > x.Length) + throw new InvalidOperationException($"The value {count} is out of range."); + Types.Buffer result = new(count, false); + x[..count].CopyTo(result.InnerBuffer.Span); + engine.Push(result); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Right(ExecutionEngine engine, Instruction instruction) + { + int count = (int)engine.Pop().GetInteger(); + if (count < 0) + throw new InvalidOperationException($"The value {count} is out of range."); + var x = engine.Pop().GetSpan(); + if (count > x.Length) + throw new InvalidOperationException($"The value {count} is out of range."); + Types.Buffer result = new(count, false); + x[^count..^0].CopyTo(result.InnerBuffer.Span); + engine.Push(result); + } + } +} diff --git a/src/Neo.VM/JumpTable/JumpTable.Stack.cs b/src/Neo.VM/JumpTable/JumpTable.Stack.cs new file mode 100644 index 0000000000..0f623c26ce --- /dev/null +++ b/src/Neo.VM/JumpTable/JumpTable.Stack.cs @@ -0,0 +1,124 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JumpTable.Stack.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.VM.Types; +using System; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ + public partial class JumpTable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Depth(ExecutionEngine engine, Instruction instruction) + { + engine.Push(engine.CurrentContext!.EvaluationStack.Count); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Drop(ExecutionEngine engine, Instruction instruction) + { + engine.Pop(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Nip(ExecutionEngine engine, Instruction instruction) + { + engine.CurrentContext!.EvaluationStack.Remove(1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void XDrop(ExecutionEngine engine, Instruction instruction) + { + var n = (int)engine.Pop().GetInteger(); + if (n < 0) + throw new InvalidOperationException($"The negative value {n} is invalid for OpCode.{instruction.OpCode}."); + engine.CurrentContext!.EvaluationStack.Remove(n); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Clear(ExecutionEngine engine, Instruction instruction) + { + engine.CurrentContext!.EvaluationStack.Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Dup(ExecutionEngine engine, Instruction instruction) + { + engine.Push(engine.Peek()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Over(ExecutionEngine engine, Instruction instruction) + { + engine.Push(engine.Peek(1)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Pick(ExecutionEngine engine, Instruction instruction) + { + var n = (int)engine.Pop().GetInteger(); + if (n < 0) + throw new InvalidOperationException($"The negative value {n} is invalid for OpCode.{instruction.OpCode}."); + engine.Push(engine.Peek(n)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Tuck(ExecutionEngine engine, Instruction instruction) + { + engine.CurrentContext!.EvaluationStack.Insert(2, engine.Peek()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Swap(ExecutionEngine engine, Instruction instruction) + { + var x = engine.CurrentContext!.EvaluationStack.Remove(1); + engine.Push(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Rot(ExecutionEngine engine, Instruction instruction) + { + var x = engine.CurrentContext!.EvaluationStack.Remove(2); + engine.Push(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Roll(ExecutionEngine engine, Instruction instruction) + { + var n = (int)engine.Pop().GetInteger(); + if (n < 0) + throw new InvalidOperationException($"The negative value {n} is invalid for OpCode.{instruction.OpCode}."); + if (n == 0) return; + var x = engine.CurrentContext!.EvaluationStack.Remove(n); + engine.Push(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Reverse3(ExecutionEngine engine, Instruction instruction) + { + engine.CurrentContext!.EvaluationStack.Reverse(3); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Reverse4(ExecutionEngine engine, Instruction instruction) + { + engine.CurrentContext!.EvaluationStack.Reverse(4); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void ReverseN(ExecutionEngine engine, Instruction instruction) + { + var n = (int)engine.Pop().GetInteger(); + engine.CurrentContext!.EvaluationStack.Reverse(n); + } + } +} diff --git a/src/Neo.VM/JumpTable/JumpTable.Types.cs b/src/Neo.VM/JumpTable/JumpTable.Types.cs new file mode 100644 index 0000000000..6c3b1cbb61 --- /dev/null +++ b/src/Neo.VM/JumpTable/JumpTable.Types.cs @@ -0,0 +1,60 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JumpTable.Types.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.VM.Types; +using System; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ + public partial class JumpTable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void IsNull(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop(); + engine.Push(x.IsNull); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void IsType(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop(); + var type = (StackItemType)instruction.TokenU8; + if (type == StackItemType.Any || !Enum.IsDefined(typeof(StackItemType), type)) + throw new InvalidOperationException($"Invalid type: {type}"); + engine.Push(x.Type == type); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Convert(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop(); + engine.Push(x.ConvertTo((StackItemType)instruction.TokenU8)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void AbortMsg(ExecutionEngine engine, Instruction instruction) + { + var msg = engine.Pop().GetString(); + throw new Exception($"{OpCode.ABORTMSG} is executed. Reason: {msg}"); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void AssertMsg(ExecutionEngine engine, Instruction instruction) + { + var msg = engine.Pop().GetString(); + var x = engine.Pop().GetBoolean(); + if (!x) + throw new Exception($"{OpCode.ASSERTMSG} is executed with false result. Reason: {msg}"); + } + } +} diff --git a/src/Neo.VM/JumpTable/JumpTable.cs b/src/Neo.VM/JumpTable/JumpTable.cs new file mode 100644 index 0000000000..0c387e7c9b --- /dev/null +++ b/src/Neo.VM/JumpTable/JumpTable.cs @@ -0,0 +1,71 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JumpTable.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; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ + public partial class JumpTable + { + /// + /// Default JumpTable + /// + public static readonly JumpTable Default = new(); + + public delegate void DelAction(ExecutionEngine engine, Instruction instruction); + protected readonly DelAction[] Table = new DelAction[byte.MaxValue]; + + public DelAction this[OpCode opCode] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Table[(byte)opCode]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set { Table[(byte)opCode] = value; } + } + + /// + /// Jump table constructor + /// + /// Throw an exception if the opcode was already set + public JumpTable() + { + // Fill defined + + foreach (var mi in GetType().GetMethods()) + { + if (Enum.TryParse(mi.Name, true, out var opCode)) + { + if (Table[(byte)opCode] is not null) + { + throw new InvalidOperationException($"Opcode {opCode} is already defined."); + } + + Table[(byte)opCode] = (DelAction)mi.CreateDelegate(typeof(DelAction), this); + } + } + + // Fill with undefined + + for (var x = 0; x < Table.Length; x++) + { + if (Table[x] is not null) continue; + + Table[x] = InvalidOpcode; + } + } + + public virtual void InvalidOpcode(ExecutionEngine engine, Instruction instruction) + { + throw new InvalidOperationException($"Opcode {instruction.OpCode} is undefined."); + } + } +} diff --git a/src/Neo.VM/Neo.VM.csproj b/src/Neo.VM/Neo.VM.csproj new file mode 100644 index 0000000000..66f4682c64 --- /dev/null +++ b/src/Neo.VM/Neo.VM.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.1;net7.0 + true + enable + + + + + + + + diff --git a/src/Neo.VM/OpCode.cs b/src/Neo.VM/OpCode.cs new file mode 100644 index 0000000000..dd5f1574ea --- /dev/null +++ b/src/Neo.VM/OpCode.cs @@ -0,0 +1,906 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// 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. + +using Neo.VM.Types; + +namespace Neo.VM +{ + /// + /// Represents the opcode of an . + /// + public enum OpCode : byte + { + #region Constants + + /// + /// Pushes a 1-byte signed integer onto the stack. + /// + [OperandSize(Size = 1)] + PUSHINT8 = 0x00, + /// + /// Pushes a 2-bytes signed integer onto the stack. + /// + [OperandSize(Size = 2)] + PUSHINT16 = 0x01, + /// + /// Pushes a 4-bytes signed integer onto the stack. + /// + [OperandSize(Size = 4)] + PUSHINT32 = 0x02, + /// + /// Pushes a 8-bytes signed integer onto the stack. + /// + [OperandSize(Size = 8)] + PUSHINT64 = 0x03, + /// + /// Pushes a 16-bytes signed integer onto the stack. + /// + [OperandSize(Size = 16)] + PUSHINT128 = 0x04, + /// + /// Pushes a 32-bytes signed integer onto the stack. + /// + [OperandSize(Size = 32)] + PUSHINT256 = 0x05, + /// + /// Pushes the boolean value onto the stack. + /// + PUSHT = 0x08, + /// + /// Pushes the boolean value onto the stack. + /// + PUSHF = 0x09, + /// + /// Converts the 4-bytes offset to an , and pushes it onto the stack. + /// + [OperandSize(Size = 4)] + PUSHA = 0x0A, + /// + /// The item is pushed onto the stack. + /// + PUSHNULL = 0x0B, + /// + /// The next byte contains the number of bytes to be pushed onto the stack. + /// + [OperandSize(SizePrefix = 1)] + PUSHDATA1 = 0x0C, + /// + /// The next two bytes contain the number of bytes to be pushed onto the stack. + /// + [OperandSize(SizePrefix = 2)] + PUSHDATA2 = 0x0D, + /// + /// The next four bytes contain the number of bytes to be pushed onto the stack. + /// + [OperandSize(SizePrefix = 4)] + PUSHDATA4 = 0x0E, + /// + /// The number -1 is pushed onto the stack. + /// + PUSHM1 = 0x0F, + /// + /// The number 0 is pushed onto the stack. + /// + PUSH0 = 0x10, + /// + /// The number 1 is pushed onto the stack. + /// + PUSH1 = 0x11, + /// + /// The number 2 is pushed onto the stack. + /// + PUSH2 = 0x12, + /// + /// The number 3 is pushed onto the stack. + /// + PUSH3 = 0x13, + /// + /// The number 4 is pushed onto the stack. + /// + PUSH4 = 0x14, + /// + /// The number 5 is pushed onto the stack. + /// + PUSH5 = 0x15, + /// + /// The number 6 is pushed onto the stack. + /// + PUSH6 = 0x16, + /// + /// The number 7 is pushed onto the stack. + /// + PUSH7 = 0x17, + /// + /// The number 8 is pushed onto the stack. + /// + PUSH8 = 0x18, + /// + /// The number 9 is pushed onto the stack. + /// + PUSH9 = 0x19, + /// + /// The number 10 is pushed onto the stack. + /// + PUSH10 = 0x1A, + /// + /// The number 11 is pushed onto the stack. + /// + PUSH11 = 0x1B, + /// + /// The number 12 is pushed onto the stack. + /// + PUSH12 = 0x1C, + /// + /// The number 13 is pushed onto the stack. + /// + PUSH13 = 0x1D, + /// + /// The number 14 is pushed onto the stack. + /// + PUSH14 = 0x1E, + /// + /// The number 15 is pushed onto the stack. + /// + PUSH15 = 0x1F, + /// + /// The number 16 is pushed onto the stack. + /// + PUSH16 = 0x20, + + #endregion + + #region Flow control + + /// + /// The operation does nothing. It is intended to fill in space if opcodes are patched. + /// + NOP = 0x21, + /// + /// Unconditionally transfers control to a target instruction. The target instruction is represented as a 1-byte signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 1)] + JMP = 0x22, + /// + /// Unconditionally transfers control to a target instruction. The target instruction is represented as a 4-bytes signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 4)] + JMP_L = 0x23, + /// + /// Transfers control to a target instruction if the value is , not , or non-zero. The target instruction is represented as a 1-byte signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 1)] + JMPIF = 0x24, + /// + /// Transfers control to a target instruction if the value is , not , or non-zero. The target instruction is represented as a 4-bytes signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 4)] + JMPIF_L = 0x25, + /// + /// Transfers control to a target instruction if the value is , a reference, or zero. The target instruction is represented as a 1-byte signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 1)] + JMPIFNOT = 0x26, + /// + /// Transfers control to a target instruction if the value is , a reference, or zero. The target instruction is represented as a 4-bytes signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 4)] + JMPIFNOT_L = 0x27, + /// + /// Transfers control to a target instruction if two values are equal. The target instruction is represented as a 1-byte signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 1)] + JMPEQ = 0x28, + /// + /// Transfers control to a target instruction if two values are equal. The target instruction is represented as a 4-bytes signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 4)] + JMPEQ_L = 0x29, + /// + /// Transfers control to a target instruction when two values are not equal. The target instruction is represented as a 1-byte signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 1)] + JMPNE = 0x2A, + /// + /// Transfers control to a target instruction when two values are not equal. The target instruction is represented as a 4-bytes signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 4)] + JMPNE_L = 0x2B, + /// + /// Transfers control to a target instruction if the first value is greater than the second value. The target instruction is represented as a 1-byte signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 1)] + JMPGT = 0x2C, + /// + /// Transfers control to a target instruction if the first value is greater than the second value. The target instruction is represented as a 4-bytes signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 4)] + JMPGT_L = 0x2D, + /// + /// Transfers control to a target instruction if the first value is greater than or equal to the second value. The target instruction is represented as a 1-byte signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 1)] + JMPGE = 0x2E, + /// + /// Transfers control to a target instruction if the first value is greater than or equal to the second value. The target instruction is represented as a 4-bytes signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 4)] + JMPGE_L = 0x2F, + /// + /// Transfers control to a target instruction if the first value is less than the second value. The target instruction is represented as a 1-byte signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 1)] + JMPLT = 0x30, + /// + /// Transfers control to a target instruction if the first value is less than the second value. The target instruction is represented as a 4-bytes signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 4)] + JMPLT_L = 0x31, + /// + /// Transfers control to a target instruction if the first value is less than or equal to the second value. The target instruction is represented as a 1-byte signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 1)] + JMPLE = 0x32, + /// + /// Transfers control to a target instruction if the first value is less than or equal to the second value. The target instruction is represented as a 4-bytes signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 4)] + JMPLE_L = 0x33, + /// + /// Calls the function at the target address which is represented as a 1-byte signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 1)] + CALL = 0x34, + /// + /// Calls the function at the target address which is represented as a 4-bytes signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 4)] + CALL_L = 0x35, + /// + /// Pop the address of a function from the stack, and call the function. + /// + CALLA = 0x36, + /// + /// Calls the function which is described by the token. + /// + [OperandSize(Size = 2)] + CALLT = 0x37, + /// + /// It turns the vm state to FAULT immediately, and cannot be caught. + /// + ABORT = 0x38, + /// + /// Pop the top value of the stack. If it's false, exit vm execution and set vm state to FAULT. + /// + ASSERT = 0x39, + /// + /// Pop the top value of the stack, and throw it. + /// + THROW = 0x3A, + /// + /// TRY CatchOffset(sbyte) FinallyOffset(sbyte). If there's no catch body, set CatchOffset 0. If there's no finally body, set FinallyOffset 0. + /// + [OperandSize(Size = 2)] + TRY = 0x3B, + /// + /// TRY_L CatchOffset(int) FinallyOffset(int). If there's no catch body, set CatchOffset 0. If there's no finally body, set FinallyOffset 0. + /// + [OperandSize(Size = 8)] + TRY_L = 0x3C, + /// + /// Ensures that the appropriate surrounding finally blocks are executed. And then unconditionally transfers control to the specific target instruction, represented as a 1-byte signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 1)] + ENDTRY = 0x3D, + /// + /// Ensures that the appropriate surrounding finally blocks are executed. And then unconditionally transfers control to the specific target instruction, represented as a 4-byte signed offset from the beginning of the current instruction. + /// + [OperandSize(Size = 4)] + ENDTRY_L = 0x3E, + /// + /// End finally, If no exception happen or be catched, vm will jump to the target instruction of ENDTRY/ENDTRY_L. Otherwise vm will rethrow the exception to upper layer. + /// + ENDFINALLY = 0x3F, + /// + /// Returns from the current method. + /// + RET = 0x40, + /// + /// Calls to an interop service. + /// + [OperandSize(Size = 4)] + SYSCALL = 0x41, + + #endregion + + #region Stack + + /// + /// Puts the number of stack items onto the stack. + /// + DEPTH = 0x43, + /// + /// Removes the top stack item. + /// + DROP = 0x45, + /// + /// Removes the second-to-top stack item. + /// + NIP = 0x46, + /// + /// The item n back in the main stack is removed. + /// + XDROP = 0x48, + /// + /// Clear the stack + /// + CLEAR = 0x49, + /// + /// Duplicates the top stack item. + /// + DUP = 0x4A, + /// + /// Copies the second-to-top stack item to the top. + /// + OVER = 0x4B, + /// + /// The item n back in the stack is copied to the top. + /// + PICK = 0x4D, + /// + /// The item at the top of the stack is copied and inserted before the second-to-top item. + /// + TUCK = 0x4E, + /// + /// The top two items on the stack are swapped. + /// + SWAP = 0x50, + /// + /// The top three items on the stack are rotated to the left. + /// + ROT = 0x51, + /// + /// The item n back in the stack is moved to the top. + /// + ROLL = 0x52, + /// + /// Reverse the order of the top 3 items on the stack. + /// + REVERSE3 = 0x53, + /// + /// Reverse the order of the top 4 items on the stack. + /// + REVERSE4 = 0x54, + /// + /// Pop the number N on the stack, and reverse the order of the top N items on the stack. + /// + REVERSEN = 0x55, + + #endregion + + #region Slot + + /// + /// Initialize the static field list for the current execution context. + /// + [OperandSize(Size = 1)] + INITSSLOT = 0x56, + /// + /// Initialize the argument slot and the local variable list for the current execution context. + /// + [OperandSize(Size = 2)] + INITSLOT = 0x57, + /// + /// Loads the static field at index 0 onto the evaluation stack. + /// + LDSFLD0 = 0x58, + /// + /// Loads the static field at index 1 onto the evaluation stack. + /// + LDSFLD1 = 0x59, + /// + /// Loads the static field at index 2 onto the evaluation stack. + /// + LDSFLD2 = 0x5A, + /// + /// Loads the static field at index 3 onto the evaluation stack. + /// + LDSFLD3 = 0x5B, + /// + /// Loads the static field at index 4 onto the evaluation stack. + /// + LDSFLD4 = 0x5C, + /// + /// Loads the static field at index 5 onto the evaluation stack. + /// + LDSFLD5 = 0x5D, + /// + /// Loads the static field at index 6 onto the evaluation stack. + /// + LDSFLD6 = 0x5E, + /// + /// Loads the static field at a specified index onto the evaluation stack. The index is represented as a 1-byte unsigned integer. + /// + [OperandSize(Size = 1)] + LDSFLD = 0x5F, + /// + /// Stores the value on top of the evaluation stack in the static field list at index 0. + /// + STSFLD0 = 0x60, + /// + /// Stores the value on top of the evaluation stack in the static field list at index 1. + /// + STSFLD1 = 0x61, + /// + /// Stores the value on top of the evaluation stack in the static field list at index 2. + /// + STSFLD2 = 0x62, + /// + /// Stores the value on top of the evaluation stack in the static field list at index 3. + /// + STSFLD3 = 0x63, + /// + /// Stores the value on top of the evaluation stack in the static field list at index 4. + /// + STSFLD4 = 0x64, + /// + /// Stores the value on top of the evaluation stack in the static field list at index 5. + /// + STSFLD5 = 0x65, + /// + /// Stores the value on top of the evaluation stack in the static field list at index 6. + /// + STSFLD6 = 0x66, + /// + /// Stores the value on top of the evaluation stack in the static field list at a specified index. The index is represented as a 1-byte unsigned integer. + /// + [OperandSize(Size = 1)] + STSFLD = 0x67, + /// + /// Loads the local variable at index 0 onto the evaluation stack. + /// + LDLOC0 = 0x68, + /// + /// Loads the local variable at index 1 onto the evaluation stack. + /// + LDLOC1 = 0x69, + /// + /// Loads the local variable at index 2 onto the evaluation stack. + /// + LDLOC2 = 0x6A, + /// + /// Loads the local variable at index 3 onto the evaluation stack. + /// + LDLOC3 = 0x6B, + /// + /// Loads the local variable at index 4 onto the evaluation stack. + /// + LDLOC4 = 0x6C, + /// + /// Loads the local variable at index 5 onto the evaluation stack. + /// + LDLOC5 = 0x6D, + /// + /// Loads the local variable at index 6 onto the evaluation stack. + /// + LDLOC6 = 0x6E, + /// + /// Loads the local variable at a specified index onto the evaluation stack. The index is represented as a 1-byte unsigned integer. + /// + [OperandSize(Size = 1)] + LDLOC = 0x6F, + /// + /// Stores the value on top of the evaluation stack in the local variable list at index 0. + /// + STLOC0 = 0x70, + /// + /// Stores the value on top of the evaluation stack in the local variable list at index 1. + /// + STLOC1 = 0x71, + /// + /// Stores the value on top of the evaluation stack in the local variable list at index 2. + /// + STLOC2 = 0x72, + /// + /// Stores the value on top of the evaluation stack in the local variable list at index 3. + /// + STLOC3 = 0x73, + /// + /// Stores the value on top of the evaluation stack in the local variable list at index 4. + /// + STLOC4 = 0x74, + /// + /// Stores the value on top of the evaluation stack in the local variable list at index 5. + /// + STLOC5 = 0x75, + /// + /// Stores the value on top of the evaluation stack in the local variable list at index 6. + /// + STLOC6 = 0x76, + /// + /// Stores the value on top of the evaluation stack in the local variable list at a specified index. The index is represented as a 1-byte unsigned integer. + /// + [OperandSize(Size = 1)] + STLOC = 0x77, + /// + /// Loads the argument at index 0 onto the evaluation stack. + /// + LDARG0 = 0x78, + /// + /// Loads the argument at index 1 onto the evaluation stack. + /// + LDARG1 = 0x79, + /// + /// Loads the argument at index 2 onto the evaluation stack. + /// + LDARG2 = 0x7A, + /// + /// Loads the argument at index 3 onto the evaluation stack. + /// + LDARG3 = 0x7B, + /// + /// Loads the argument at index 4 onto the evaluation stack. + /// + LDARG4 = 0x7C, + /// + /// Loads the argument at index 5 onto the evaluation stack. + /// + LDARG5 = 0x7D, + /// + /// Loads the argument at index 6 onto the evaluation stack. + /// + LDARG6 = 0x7E, + /// + /// Loads the argument at a specified index onto the evaluation stack. The index is represented as a 1-byte unsigned integer. + /// + [OperandSize(Size = 1)] + LDARG = 0x7F, + /// + /// Stores the value on top of the evaluation stack in the argument slot at index 0. + /// + STARG0 = 0x80, + /// + /// Stores the value on top of the evaluation stack in the argument slot at index 1. + /// + STARG1 = 0x81, + /// + /// Stores the value on top of the evaluation stack in the argument slot at index 2. + /// + STARG2 = 0x82, + /// + /// Stores the value on top of the evaluation stack in the argument slot at index 3. + /// + STARG3 = 0x83, + /// + /// Stores the value on top of the evaluation stack in the argument slot at index 4. + /// + STARG4 = 0x84, + /// + /// Stores the value on top of the evaluation stack in the argument slot at index 5. + /// + STARG5 = 0x85, + /// + /// Stores the value on top of the evaluation stack in the argument slot at index 6. + /// + STARG6 = 0x86, + /// + /// Stores the value on top of the evaluation stack in the argument slot at a specified index. The index is represented as a 1-byte unsigned integer. + /// + [OperandSize(Size = 1)] + STARG = 0x87, + + #endregion + + #region Splice + + /// + /// Creates a new and pushes it onto the stack. + /// + NEWBUFFER = 0x88, + /// + /// Copies a range of bytes from one to another. + /// + MEMCPY = 0x89, + /// + /// Concatenates two strings. + /// + CAT = 0x8B, + /// + /// Returns a section of a string. + /// + SUBSTR = 0x8C, + /// + /// Keeps only characters left of the specified point in a string. + /// + LEFT = 0x8D, + /// + /// Keeps only characters right of the specified point in a string. + /// + RIGHT = 0x8E, + + #endregion + + #region Bitwise logic + + /// + /// Flips all of the bits in the input. + /// + INVERT = 0x90, + /// + /// Boolean and between each bit in the inputs. + /// + AND = 0x91, + /// + /// Boolean or between each bit in the inputs. + /// + OR = 0x92, + /// + /// Boolean exclusive or between each bit in the inputs. + /// + XOR = 0x93, + /// + /// Returns 1 if the inputs are exactly equal, 0 otherwise. + /// + EQUAL = 0x97, + /// + /// Returns 1 if the inputs are not equal, 0 otherwise. + /// + NOTEQUAL = 0x98, + + #endregion + + #region Arithmetic + + /// + /// Puts the sign of top stack item on top of the main stack. If value is negative, put -1; if positive, put 1; if value is zero, put 0. + /// + SIGN = 0x99, + /// + /// The input is made positive. + /// + ABS = 0x9A, + /// + /// The sign of the input is flipped. + /// + NEGATE = 0x9B, + /// + /// 1 is added to the input. + /// + INC = 0x9C, + /// + /// 1 is subtracted from the input. + /// + DEC = 0x9D, + /// + /// a is added to b. + /// + ADD = 0x9E, + /// + /// b is subtracted from a. + /// + SUB = 0x9F, + /// + /// a is multiplied by b. + /// + MUL = 0xA0, + /// + /// a is divided by b. + /// + DIV = 0xA1, + /// + /// Returns the remainder after dividing a by b. + /// + MOD = 0xA2, + /// + /// The result of raising value to the exponent power. + /// + POW = 0xA3, + /// + /// Returns the square root of a specified number. + /// + SQRT = 0xA4, + /// + /// Performs modulus division on a number multiplied by another number. + /// + MODMUL = 0xA5, + /// + /// Performs modulus division on a number raised to the power of another number. If the exponent is -1, it will have the calculation of the modular inverse. + /// + MODPOW = 0xA6, + /// + /// Shifts a left b bits, preserving sign. + /// + SHL = 0xA8, + /// + /// Shifts a right b bits, preserving sign. + /// + SHR = 0xA9, + /// + /// If the input is 0 or 1, it is flipped. Otherwise the output will be 0. + /// + NOT = 0xAA, + /// + /// If both a and b are not 0, the output is 1. Otherwise 0. + /// + BOOLAND = 0xAB, + /// + /// If a or b is not 0, the output is 1. Otherwise 0. + /// + BOOLOR = 0xAC, + /// + /// Returns 0 if the input is 0. 1 otherwise. + /// + NZ = 0xB1, + /// + /// Returns 1 if the numbers are equal, 0 otherwise. + /// + NUMEQUAL = 0xB3, + /// + /// Returns 1 if the numbers are not equal, 0 otherwise. + /// + NUMNOTEQUAL = 0xB4, + /// + /// Returns 1 if a is less than b, 0 otherwise. + /// + LT = 0xB5, + /// + /// Returns 1 if a is less than or equal to b, 0 otherwise. + /// + LE = 0xB6, + /// + /// Returns 1 if a is greater than b, 0 otherwise. + /// + GT = 0xB7, + /// + /// Returns 1 if a is greater than or equal to b, 0 otherwise. + /// + GE = 0xB8, + /// + /// Returns the smaller of a and b. + /// + MIN = 0xB9, + /// + /// Returns the larger of a and b. + /// + MAX = 0xBA, + /// + /// Returns 1 if x is within the specified range (left-inclusive), 0 otherwise. + /// + WITHIN = 0xBB, + + #endregion + + #region Compound-type + + /// + /// A value n is taken from top of main stack. The next n*2 items on main stack are removed, put inside n-sized map and this map is put on top of the main stack. + /// + PACKMAP = 0xBE, + /// + /// A value n is taken from top of main stack. The next n items on main stack are removed, put inside n-sized struct and this struct is put on top of the main stack. + /// + PACKSTRUCT = 0xBF, + /// + /// A value n is taken from top of main stack. The next n items on main stack are removed, put inside n-sized array and this array is put on top of the main stack. + /// + PACK = 0xC0, + /// + /// A collection is removed from top of the main stack. Its elements are put on top of the main stack (in reverse order) and the collection size is also put on main stack. + /// + UNPACK = 0xC1, + /// + /// An empty array (with size 0) is put on top of the main stack. + /// + NEWARRAY0 = 0xC2, + /// + /// A value n is taken from top of main stack. A null-filled array with size n is put on top of the main stack. + /// + NEWARRAY = 0xC3, + /// + /// A value n is taken from top of main stack. An array of type T with size n is put on top of the main stack. + /// + [OperandSize(Size = 1)] + NEWARRAY_T = 0xC4, + /// + /// An empty struct (with size 0) is put on top of the main stack. + /// + NEWSTRUCT0 = 0xC5, + /// + /// A value n is taken from top of main stack. A zero-filled struct with size n is put on top of the main stack. + /// + NEWSTRUCT = 0xC6, + /// + /// A Map is created and put on top of the main stack. + /// + NEWMAP = 0xC8, + /// + /// An array is removed from top of the main stack. Its size is put on top of the main stack. + /// + SIZE = 0xCA, + /// + /// An input index n (or key) and an array (or map) are removed from the top of the main stack. Puts True on top of main stack if array[n] (or map[n]) exist, and False otherwise. + /// + HASKEY = 0xCB, + /// + /// A map is taken from top of the main stack. The keys of this map are put on top of the main stack. + /// + KEYS = 0xCC, + /// + /// A map is taken from top of the main stack. The values of this map are put on top of the main stack. + /// + VALUES = 0xCD, + /// + /// An input index n (or key) and an array (or map) are taken from main stack. Element array[n] (or map[n]) is put on top of the main stack. + /// + PICKITEM = 0xCE, + /// + /// The item on top of main stack is removed and appended to the second item on top of the main stack. + /// + APPEND = 0xCF, + /// + /// A value v, index n (or key) and an array (or map) are taken from main stack. Attribution array[n]=v (or map[n]=v) is performed. + /// + SETITEM = 0xD0, + /// + /// An array is removed from the top of the main stack and its elements are reversed. + /// + REVERSEITEMS = 0xD1, + /// + /// An input index n (or key) and an array (or map) are removed from the top of the main stack. Element array[n] (or map[n]) is removed. + /// + REMOVE = 0xD2, + /// + /// Remove all the items from the compound-type. + /// + CLEARITEMS = 0xD3, + /// + /// Remove the last element from an array, and push it onto the stack. + /// + POPITEM = 0xD4, + + #endregion + + #region Types + + /// + /// Returns if the input is ; + /// otherwise. + /// + ISNULL = 0xD8, + /// + /// Returns if the top item of the stack is of the specified type; + /// otherwise. + /// + [OperandSize(Size = 1)] + ISTYPE = 0xD9, + /// + /// Converts the top item of the stack to the specified type. + /// + [OperandSize(Size = 1)] + CONVERT = 0xDB, + + #endregion + + #region Extensions + + /// + /// Pops the top stack item. Then, turns the vm state to FAULT immediately, and cannot be caught. The top stack + /// value is used as reason. + /// + ABORTMSG = 0xE0, + /// + /// Pops the top two stack items. If the second-to-top stack value is false, exits the vm execution and sets the + /// vm state to FAULT. In this case, the top stack value is used as reason for the exit. Otherwise, it is ignored. + /// + ASSERTMSG = 0xE1 + + #endregion + } +} diff --git a/src/Neo.VM/OperandSizeAttribute.cs b/src/Neo.VM/OperandSizeAttribute.cs new file mode 100644 index 0000000000..592c0a716a --- /dev/null +++ b/src/Neo.VM/OperandSizeAttribute.cs @@ -0,0 +1,32 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// OperandSizeAttribute.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; + +namespace Neo.VM +{ + /// + /// Indicates the operand length of an . + /// + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] + public class OperandSizeAttribute : Attribute + { + /// + /// When it is greater than 0, indicates the size of the operand. + /// + public int Size { get; set; } + + /// + /// When it is greater than 0, indicates the size prefix of the operand. + /// + public int SizePrefix { get; set; } + } +} diff --git a/src/Neo.VM/Properties/AssemblyInfo.cs b/src/Neo.VM/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..71329c03a1 --- /dev/null +++ b/src/Neo.VM/Properties/AssemblyInfo.cs @@ -0,0 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// AssemblyInfo.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.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Neo.VM.Tests")] diff --git a/src/Neo.VM/ReferenceCounter.cs b/src/Neo.VM/ReferenceCounter.cs new file mode 100644 index 0000000000..be7844dac8 --- /dev/null +++ b/src/Neo.VM/ReferenceCounter.cs @@ -0,0 +1,155 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ReferenceCounter.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.VM.StronglyConnectedComponents; +using Neo.VM.Types; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ + /// + /// Used for reference counting of objects in the VM. + /// + public sealed class ReferenceCounter + { + private const bool TrackAllItems = false; + + private readonly HashSet tracked_items = new(ReferenceEqualityComparer.Instance); + private readonly HashSet zero_referred = new(ReferenceEqualityComparer.Instance); + private LinkedList>? cached_components; + private int references_count = 0; + + /// + /// Indicates the number of this counter. + /// + public int Count => references_count; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool NeedTrack(StackItem item) + { +#pragma warning disable CS0162 + if (TrackAllItems) return true; +#pragma warning restore CS0162 + if (item is CompoundType or Buffer) return true; + return false; + } + + internal void AddReference(StackItem item, CompoundType parent) + { + references_count++; + if (!NeedTrack(item)) return; + cached_components = null; + tracked_items.Add(item); + item.ObjectReferences ??= new(ReferenceEqualityComparer.Instance); + if (!item.ObjectReferences.TryGetValue(parent, out var pEntry)) + { + pEntry = new(parent); + item.ObjectReferences.Add(parent, pEntry); + } + pEntry.References++; + } + + internal void AddStackReference(StackItem item, int count = 1) + { + references_count += count; + if (!NeedTrack(item)) return; + if (tracked_items.Add(item)) + cached_components?.AddLast(new HashSet(ReferenceEqualityComparer.Instance) { item }); + item.StackReferences += count; + zero_referred.Remove(item); + } + + internal void AddZeroReferred(StackItem item) + { + zero_referred.Add(item); + if (!NeedTrack(item)) return; + cached_components?.AddLast(new HashSet(ReferenceEqualityComparer.Instance) { item }); + tracked_items.Add(item); + } + + internal int CheckZeroReferred() + { + if (zero_referred.Count > 0) + { + zero_referred.Clear(); + if (cached_components is null) + { + //Tarjan tarjan = new(tracked_items.Where(p => p.StackReferences == 0)); + Tarjan tarjan = new(tracked_items); + cached_components = tarjan.Invoke(); + } + foreach (StackItem item in tracked_items) + item.Reset(); + for (var node = cached_components.First; node != null;) + { + var component = node.Value; + bool on_stack = false; + foreach (StackItem item in component) + { + if (item.StackReferences > 0 || item.ObjectReferences?.Values.Any(p => p.References > 0 && p.Item.OnStack) == true) + { + on_stack = true; + break; + } + } + if (on_stack) + { + foreach (StackItem item in component) + item.OnStack = true; + node = node.Next; + } + else + { + foreach (StackItem item in component) + { + tracked_items.Remove(item); + if (item is CompoundType compound) + { + references_count -= compound.SubItemsCount; + foreach (StackItem subitem in compound.SubItems) + { + if (component.Contains(subitem)) continue; + if (!NeedTrack(subitem)) continue; + subitem.ObjectReferences!.Remove(compound); + } + } + item.Cleanup(); + } + var nodeToRemove = node; + node = node.Next; + cached_components.Remove(nodeToRemove); + } + } + } + return references_count; + } + + internal void RemoveReference(StackItem item, CompoundType parent) + { + references_count--; + if (!NeedTrack(item)) return; + cached_components = null; + item.ObjectReferences![parent].References--; + if (item.StackReferences == 0) + zero_referred.Add(item); + } + + internal void RemoveStackReference(StackItem item) + { + references_count--; + if (!NeedTrack(item)) return; + if (--item.StackReferences == 0) + zero_referred.Add(item); + } + } +} diff --git a/src/Neo.VM/ReferenceEqualityComparer.cs b/src/Neo.VM/ReferenceEqualityComparer.cs new file mode 100644 index 0000000000..4e8eec9d2d --- /dev/null +++ b/src/Neo.VM/ReferenceEqualityComparer.cs @@ -0,0 +1,30 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ReferenceEqualityComparer.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.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ +#if !NET5_0_OR_GREATER + // https://github.dev/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ReferenceEqualityComparer.cs + public sealed class ReferenceEqualityComparer : IEqualityComparer, System.Collections.IEqualityComparer + { + private ReferenceEqualityComparer() { } + + public static ReferenceEqualityComparer Instance { get; } = new ReferenceEqualityComparer(); + + public new bool Equals(object? x, object? y) => ReferenceEquals(x, y); + + public int GetHashCode(object? obj) => RuntimeHelpers.GetHashCode(obj!); + } +#endif +} diff --git a/src/Neo.VM/Script.cs b/src/Neo.VM/Script.cs new file mode 100644 index 0000000000..5f6b29c5b7 --- /dev/null +++ b/src/Neo.VM/Script.cs @@ -0,0 +1,161 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Script.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.VM.Types; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ + /// + /// Represents the script executed in the VM. + /// + [DebuggerDisplay("Length={Length}")] + public class Script + { + private readonly ReadOnlyMemory _value; + private readonly bool strictMode; + private readonly Dictionary _instructions = new(); + + /// + /// The length of the script. + /// + public int Length + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return _value.Length; + } + } + + /// + /// Gets the at the specified index. + /// + /// The index to locate. + /// The at the specified index. + public OpCode this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return (OpCode)_value.Span[index]; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The bytecodes of the script. + public Script(ReadOnlyMemory script) : this(script, false) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The bytecodes of the script. + /// + /// Indicates whether strict mode is enabled. + /// In strict mode, the script will be checked, but the loading speed will be slower. + /// + /// In strict mode, the script was found to contain bad instructions. + public Script(ReadOnlyMemory script, bool strictMode) + { + this._value = script; + if (strictMode) + { + for (int ip = 0; ip < script.Length; ip += GetInstruction(ip).Size) { } + foreach (var (ip, instruction) in _instructions) + { + switch (instruction.OpCode) + { + case OpCode.JMP: + case OpCode.JMPIF: + case OpCode.JMPIFNOT: + case OpCode.JMPEQ: + case OpCode.JMPNE: + case OpCode.JMPGT: + case OpCode.JMPGE: + case OpCode.JMPLT: + case OpCode.JMPLE: + case OpCode.CALL: + case OpCode.ENDTRY: + if (!_instructions.ContainsKey(checked(ip + instruction.TokenI8))) + throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}"); + break; + case OpCode.PUSHA: + case OpCode.JMP_L: + case OpCode.JMPIF_L: + case OpCode.JMPIFNOT_L: + case OpCode.JMPEQ_L: + case OpCode.JMPNE_L: + case OpCode.JMPGT_L: + case OpCode.JMPGE_L: + case OpCode.JMPLT_L: + case OpCode.JMPLE_L: + case OpCode.CALL_L: + case OpCode.ENDTRY_L: + if (!_instructions.ContainsKey(checked(ip + instruction.TokenI32))) + throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}"); + break; + case OpCode.TRY: + if (!_instructions.ContainsKey(checked(ip + instruction.TokenI8))) + throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}"); + if (!_instructions.ContainsKey(checked(ip + instruction.TokenI8_1))) + throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}"); + break; + case OpCode.TRY_L: + if (!_instructions.ContainsKey(checked(ip + instruction.TokenI32))) + throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}"); + if (!_instructions.ContainsKey(checked(ip + instruction.TokenI32_1))) + throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}"); + break; + case OpCode.NEWARRAY_T: + case OpCode.ISTYPE: + case OpCode.CONVERT: + StackItemType type = (StackItemType)instruction.TokenU8; + if (!Enum.IsDefined(typeof(StackItemType), type)) + throw new BadScriptException(); + if (instruction.OpCode != OpCode.NEWARRAY_T && type == StackItemType.Any) + throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}"); + break; + } + } + } + this.strictMode = strictMode; + } + + /// + /// Get the at the specified position. + /// + /// The position to get the . + /// The at the specified position. + /// In strict mode, the was not found at the specified position. + public Instruction GetInstruction(int ip) + { + if (ip >= Length) throw new ArgumentOutOfRangeException(nameof(ip)); + if (!_instructions.TryGetValue(ip, out Instruction? instruction)) + { + if (strictMode) throw new ArgumentException($"ip not found with strict mode", nameof(ip)); + instruction = new Instruction(_value, ip); + _instructions.Add(ip, instruction); + } + return instruction; + } + + public static implicit operator ReadOnlyMemory(Script script) => script._value; + public static implicit operator Script(ReadOnlyMemory script) => new(script); + public static implicit operator Script(byte[] script) => new(script); + } +} diff --git a/src/Neo.VM/ScriptBuilder.cs b/src/Neo.VM/ScriptBuilder.cs new file mode 100644 index 0000000000..3b2f83171a --- /dev/null +++ b/src/Neo.VM/ScriptBuilder.cs @@ -0,0 +1,200 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ScriptBuilder.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; +using System.IO; +using System.Numerics; + +namespace Neo.VM +{ + /// + /// A helper class for building scripts. + /// + public class ScriptBuilder : IDisposable + { + private readonly MemoryStream ms = new(); + private readonly BinaryWriter writer; + + /// + /// The length of the script. + /// + public int Length => (int)ms.Position; + + /// + /// Initializes a new instance of the class. + /// + public ScriptBuilder() + { + writer = new BinaryWriter(ms); + } + + public void Dispose() + { + writer.Dispose(); + ms.Dispose(); + } + + /// + /// Emits an with the specified and operand. + /// + /// The to be emitted. + /// The operand to be emitted. + /// A reference to this instance after the emit operation has completed. + public ScriptBuilder Emit(OpCode opcode, ReadOnlySpan operand = default) + { + writer.Write((byte)opcode); + writer.Write(operand); + return this; + } + + /// + /// Emits a call with the specified offset. + /// + /// The offset to be called. + /// A reference to this instance after the emit operation has completed. + public ScriptBuilder EmitCall(int offset) + { + if (offset < sbyte.MinValue || offset > sbyte.MaxValue) + return Emit(OpCode.CALL_L, BitConverter.GetBytes(offset)); + else + return Emit(OpCode.CALL, new[] { (byte)offset }); + } + + /// + /// Emits a jump with the specified offset. + /// + /// The to be emitted. It must be a jump + /// The offset to jump. + /// A reference to this instance after the emit operation has completed. + public ScriptBuilder EmitJump(OpCode opcode, int offset) + { + if (opcode < OpCode.JMP || opcode > OpCode.JMPLE_L) + throw new ArgumentOutOfRangeException(nameof(opcode)); + if ((int)opcode % 2 == 0 && (offset < sbyte.MinValue || offset > sbyte.MaxValue)) + opcode += 1; + if ((int)opcode % 2 == 0) + return Emit(opcode, new[] { (byte)offset }); + else + return Emit(opcode, BitConverter.GetBytes(offset)); + } + + /// + /// Emits a push with the specified number. + /// + /// The number to be pushed. + /// A reference to this instance after the emit operation has completed. + public ScriptBuilder EmitPush(BigInteger value) + { + if (value >= -1 && value <= 16) return Emit(OpCode.PUSH0 + (byte)(int)value); + Span buffer = stackalloc byte[32]; + if (!value.TryWriteBytes(buffer, out int bytesWritten, isUnsigned: false, isBigEndian: false)) + throw new ArgumentOutOfRangeException(nameof(value)); + return bytesWritten switch + { + 1 => Emit(OpCode.PUSHINT8, PadRight(buffer, bytesWritten, 1, value.Sign < 0)), + 2 => Emit(OpCode.PUSHINT16, PadRight(buffer, bytesWritten, 2, value.Sign < 0)), + <= 4 => Emit(OpCode.PUSHINT32, PadRight(buffer, bytesWritten, 4, value.Sign < 0)), + <= 8 => Emit(OpCode.PUSHINT64, PadRight(buffer, bytesWritten, 8, value.Sign < 0)), + <= 16 => Emit(OpCode.PUSHINT128, PadRight(buffer, bytesWritten, 16, value.Sign < 0)), + _ => Emit(OpCode.PUSHINT256, PadRight(buffer, bytesWritten, 32, value.Sign < 0)), + }; + } + + /// + /// Emits a push with the specified boolean value. + /// + /// The value to be pushed. + /// A reference to this instance after the emit operation has completed. + public ScriptBuilder EmitPush(bool value) + { + return Emit(value ? OpCode.PUSHT : OpCode.PUSHF); + } + + /// + /// Emits a push with the specified data. + /// + /// The data to be pushed. + /// A reference to this instance after the emit operation has completed. + public ScriptBuilder EmitPush(ReadOnlySpan data) + { + if (data == null) + throw new ArgumentNullException(nameof(data)); + if (data.Length < 0x100) + { + Emit(OpCode.PUSHDATA1); + writer.Write((byte)data.Length); + writer.Write(data); + } + else if (data.Length < 0x10000) + { + Emit(OpCode.PUSHDATA2); + writer.Write((ushort)data.Length); + writer.Write(data); + } + else// if (data.Length < 0x100000000L) + { + Emit(OpCode.PUSHDATA4); + writer.Write(data.Length); + writer.Write(data); + } + return this; + } + + /// + /// Emits a push with the specified . + /// + /// The to be pushed. + /// A reference to this instance after the emit operation has completed. + public ScriptBuilder EmitPush(string data) + { + return EmitPush(Utility.StrictUTF8.GetBytes(data)); + } + + /// + /// Emits raw script. + /// + /// The raw script to be emitted. + /// A reference to this instance after the emit operation has completed. + public ScriptBuilder EmitRaw(ReadOnlySpan script = default) + { + writer.Write(script); + return this; + } + + /// + /// Emits an with . + /// + /// The operand of . + /// A reference to this instance after the emit operation has completed. + public ScriptBuilder EmitSysCall(uint api) + { + return Emit(OpCode.SYSCALL, BitConverter.GetBytes(api)); + } + + /// + /// Converts the value of this instance to a byte array. + /// + /// A byte array contains the script. + public byte[] ToArray() + { + writer.Flush(); + return ms.ToArray(); + } + + private 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]; + } + } +} diff --git a/src/Neo.VM/Slot.cs b/src/Neo.VM/Slot.cs new file mode 100644 index 0000000000..33b221a381 --- /dev/null +++ b/src/Neo.VM/Slot.cs @@ -0,0 +1,93 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Slot.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.VM.Types; +using System.Collections; +using System.Collections.Generic; + +namespace Neo.VM +{ + /// + /// Used to store local variables, arguments and static fields in the VM. + /// + public class Slot : IReadOnlyList + { + private readonly ReferenceCounter referenceCounter; + private readonly StackItem[] items; + + /// + /// Gets the item at the specified index in the slot. + /// + /// The zero-based index of the item to get. + /// The item at the specified index in the slot. + public StackItem this[int index] + { + get + { + return items[index]; + } + internal set + { + ref var oldValue = ref items[index]; + referenceCounter.RemoveStackReference(oldValue); + oldValue = value; + referenceCounter.AddStackReference(value); + } + } + + /// + /// Gets the number of items in the slot. + /// + public int Count => items.Length; + + /// + /// Creates a slot containing the specified items. + /// + /// The items to be contained. + /// The reference counter to be used. + public Slot(StackItem[] items, ReferenceCounter referenceCounter) + { + this.referenceCounter = referenceCounter; + this.items = items; + foreach (StackItem item in items) + referenceCounter.AddStackReference(item); + } + + /// + /// Create a slot of the specified size. + /// + /// Indicates the number of items contained in the slot. + /// The reference counter to be used. + public Slot(int count, ReferenceCounter referenceCounter) + { + this.referenceCounter = referenceCounter; + this.items = new StackItem[count]; + System.Array.Fill(items, StackItem.Null); + referenceCounter.AddStackReference(StackItem.Null, count); + } + + internal void ClearReferences() + { + foreach (StackItem item in items) + referenceCounter.RemoveStackReference(item); + } + + IEnumerator IEnumerable.GetEnumerator() + { + foreach (StackItem item in items) yield return item; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return items.GetEnumerator(); + } + } +} diff --git a/src/Neo.VM/StronglyConnectedComponents/Tarjan.cs b/src/Neo.VM/StronglyConnectedComponents/Tarjan.cs new file mode 100644 index 0000000000..5cb7531ab6 --- /dev/null +++ b/src/Neo.VM/StronglyConnectedComponents/Tarjan.cs @@ -0,0 +1,125 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Tarjan.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; +using System.Collections.Generic; +using T = Neo.VM.Types.StackItem; + +namespace Neo.VM.StronglyConnectedComponents +{ + class Tarjan + { + private readonly IEnumerable vertexs; + private readonly LinkedList> components = new(); + private readonly Stack stack = new(); + private int index = 0; + + public Tarjan(IEnumerable vertexs) + { + this.vertexs = vertexs; + } + + public LinkedList> Invoke() + { + foreach (var v in vertexs) + { + if (v.DFN < 0) + { + StrongConnectNonRecursive(v); + } + } + return components; + } + + private void StrongConnect(T v) + { + v.DFN = v.LowLink = ++index; + stack.Push(v); + v.OnStack = true; + + foreach (T w in v.Successors) + { + if (w.DFN < 0) + { + StrongConnect(w); + v.LowLink = Math.Min(v.LowLink, w.LowLink); + } + else if (w.OnStack) + { + v.LowLink = Math.Min(v.LowLink, w.DFN); + } + } + + if (v.LowLink == v.DFN) + { + HashSet scc = new(ReferenceEqualityComparer.Instance); + T w; + do + { + w = stack.Pop(); + w.OnStack = false; + scc.Add(w); + } while (v != w); + components.AddLast(scc); + } + } + + private void StrongConnectNonRecursive(T v) + { + Stack<(T node, T?, IEnumerator?, int)> sstack = new(); + sstack.Push((v, null, null, 0)); + while (sstack.TryPop(out var state)) + { + v = state.node; + var (_, w, s, n) = state; + switch (n) + { + case 0: + v.DFN = v.LowLink = ++index; + stack.Push(v); + v.OnStack = true; + s = v.Successors.GetEnumerator(); + goto case 2; + case 1: + v.LowLink = Math.Min(v.LowLink, w!.LowLink); + goto case 2; + case 2: + while (s!.MoveNext()) + { + w = s.Current; + if (w.DFN < 0) + { + sstack.Push((v, w, s, 1)); + v = w; + goto case 0; + } + else if (w.OnStack) + { + v.LowLink = Math.Min(v.LowLink, w.DFN); + } + } + if (v.LowLink == v.DFN) + { + HashSet scc = new(ReferenceEqualityComparer.Instance); + do + { + w = stack.Pop(); + w.OnStack = false; + scc.Add(w); + } while (v != w); + components.AddLast(scc); + } + break; + } + } + } + } +} diff --git a/src/Neo.VM/Types/Array.cs b/src/Neo.VM/Types/Array.cs new file mode 100644 index 0000000000..e15358a881 --- /dev/null +++ b/src/Neo.VM/Types/Array.cs @@ -0,0 +1,146 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Array.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; +using System.Collections; +using System.Collections.Generic; + +namespace Neo.VM.Types +{ + /// + /// Represents an array or a complex object in the VM. + /// + public class Array : CompoundType, IReadOnlyList + { + protected readonly List _array; + + /// + /// Get or set item in the array. + /// + /// The index of the item in the array. + /// The item at the specified index. + public StackItem this[int index] + { + get => _array[index]; + set + { + if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + ReferenceCounter?.RemoveReference(_array[index], this); + _array[index] = value; + ReferenceCounter?.AddReference(value, this); + } + } + + /// + /// The number of items in the array. + /// + public override int Count => _array.Count; + public override IEnumerable SubItems => _array; + public override int SubItemsCount => _array.Count; + public override StackItemType Type => StackItemType.Array; + + /// + /// Create an array containing the specified items. + /// + /// The items to be included in the array. + public Array(IEnumerable? items = null) + : this(null, items) + { + } + + /// + /// Create an array containing the specified items. And make the array use the specified . + /// + /// The to be used by this array. + /// The items to be included in the array. + public Array(ReferenceCounter? referenceCounter, IEnumerable? items = null) + : base(referenceCounter) + { + _array = items switch + { + null => new List(), + List list => list, + _ => new List(items) + }; + if (referenceCounter != null) + foreach (StackItem item in _array) + referenceCounter.AddReference(item, this); + } + + /// + /// Add a new item at the end of the array. + /// + /// The item to be added. + public void Add(StackItem item) + { + if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + _array.Add(item); + ReferenceCounter?.AddReference(item, this); + } + + public override void Clear() + { + if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + if (ReferenceCounter != null) + foreach (StackItem item in _array) + ReferenceCounter.RemoveReference(item, this); + _array.Clear(); + } + + public override StackItem ConvertTo(StackItemType type) + { + if (Type == StackItemType.Array && type == StackItemType.Struct) + return new Struct(ReferenceCounter, new List(_array)); + return base.ConvertTo(type); + } + + internal sealed override StackItem DeepCopy(Dictionary refMap, bool asImmutable) + { + if (refMap.TryGetValue(this, out StackItem? mappedItem)) return mappedItem; + Array result = this is Struct ? new Struct(ReferenceCounter) : new Array(ReferenceCounter); + refMap.Add(this, result); + foreach (StackItem item in _array) + result.Add(item.DeepCopy(refMap, asImmutable)); + result.IsReadOnly = true; + return result; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + return _array.GetEnumerator(); + } + + /// + /// Remove the item at the specified index. + /// + /// The index of the item to be removed. + public void RemoveAt(int index) + { + if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + ReferenceCounter?.RemoveReference(_array[index], this); + _array.RemoveAt(index); + } + + /// + /// Reverse all items in the array. + /// + public void Reverse() + { + if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + _array.Reverse(); + } + } +} diff --git a/src/Neo.VM/Types/Boolean.cs b/src/Neo.VM/Types/Boolean.cs new file mode 100644 index 0000000000..e0a9864d92 --- /dev/null +++ b/src/Neo.VM/Types/Boolean.cs @@ -0,0 +1,76 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Boolean.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; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Neo.VM.Types +{ + /// + /// Represents a boolean ( or ) value in the VM. + /// + [DebuggerDisplay("Type={GetType().Name}, Value={value}")] + public class Boolean : PrimitiveType + { + private static readonly ReadOnlyMemory TRUE = new byte[] { 1 }; + private static readonly ReadOnlyMemory FALSE = new byte[] { 0 }; + + private readonly bool value; + + public override ReadOnlyMemory Memory => value ? TRUE : FALSE; + public override int Size => sizeof(bool); + public override StackItemType Type => StackItemType.Boolean; + + /// + /// Create a new VM object representing the boolean type. + /// + /// The initial value of the object. + internal Boolean(bool value) + { + this.value = value; + } + + public override bool Equals(StackItem? other) + { + if (ReferenceEquals(this, other)) return true; + if (other is Boolean b) return value == b.value; + return false; + } + + public override bool GetBoolean() + { + return value; + } + + public override int GetHashCode() + { + return HashCode.Combine(value); + } + + public override BigInteger GetInteger() + { + return value ? BigInteger.One : BigInteger.Zero; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Boolean(bool value) + { + return value ? True : False; + } + + public override string ToString() + { + return value.ToString(); + } + } +} diff --git a/src/Neo.VM/Types/Buffer.cs b/src/Neo.VM/Types/Buffer.cs new file mode 100644 index 0000000000..8d170577e6 --- /dev/null +++ b/src/Neo.VM/Types/Buffer.cs @@ -0,0 +1,116 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Buffer.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; +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics; +using System.Numerics; + +namespace Neo.VM.Types +{ + /// + /// Represents a memory block that can be used for reading and writing in the VM. + /// + [DebuggerDisplay("Type={GetType().Name}, Value={System.Convert.ToHexString(GetSpan())}")] + public class Buffer : StackItem + { + /// + /// The internal byte array used to store the actual data. + /// + public readonly Memory InnerBuffer; + + /// + /// The size of the buffer. + /// + public int Size => InnerBuffer.Length; + public override StackItemType Type => StackItemType.Buffer; + + private readonly byte[] _buffer; + private bool _keep_alive = false; + + /// + /// Create a buffer of the specified size. + /// + /// The size of this buffer. + /// Indicates whether the created buffer is zero-initialized. + public Buffer(int size, bool zeroInitialize = true) + { + _buffer = ArrayPool.Shared.Rent(size); + InnerBuffer = new Memory(_buffer, 0, size); + if (zeroInitialize) InnerBuffer.Span.Clear(); + } + + /// + /// Create a buffer with the specified data. + /// + /// The data to be contained in this buffer. + public Buffer(ReadOnlySpan data) : this(data.Length, false) + { + data.CopyTo(InnerBuffer.Span); + } + + internal override void Cleanup() + { + if (!_keep_alive) + ArrayPool.Shared.Return(_buffer, clearArray: false); + } + + public void KeepAlive() + { + _keep_alive = true; + } + + public override StackItem ConvertTo(StackItemType type) + { + switch (type) + { + case StackItemType.Integer: + if (InnerBuffer.Length > Integer.MaxSize) + throw new InvalidCastException(); + return new BigInteger(InnerBuffer.Span); + case StackItemType.ByteString: +#if NET5_0_OR_GREATER + byte[] clone = GC.AllocateUninitializedArray(InnerBuffer.Length); +#else + byte[] clone = new byte[InnerBuffer.Length]; +#endif + InnerBuffer.CopyTo(clone); + return clone; + default: + return base.ConvertTo(type); + } + } + + internal override StackItem DeepCopy(Dictionary refMap, bool asImmutable) + { + if (refMap.TryGetValue(this, out StackItem? mappedItem)) return mappedItem; + StackItem result = asImmutable ? new ByteString(InnerBuffer.ToArray()) : new Buffer(InnerBuffer.Span); + refMap.Add(this, result); + return result; + } + + public override bool GetBoolean() + { + return true; + } + + public override ReadOnlySpan GetSpan() + { + return InnerBuffer.Span; + } + + public override string ToString() + { + return GetSpan().TryGetString(out var str) ? $"(\"{str}\")" : $"(\"Base64: {Convert.ToBase64String(GetSpan())}\")"; + } + } +} diff --git a/src/Neo.VM/Types/ByteString.cs b/src/Neo.VM/Types/ByteString.cs new file mode 100644 index 0000000000..4d0e941dc1 --- /dev/null +++ b/src/Neo.VM/Types/ByteString.cs @@ -0,0 +1,142 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ByteString.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.VM.Cryptography; +using System; +using System.Buffers.Binary; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Neo.VM.Types +{ + /// + /// Represents an immutable memory block in the VM. + /// + [DebuggerDisplay("Type={GetType().Name}, Value={System.Convert.ToHexString(GetSpan())}")] + public class ByteString : PrimitiveType + { + /// + /// An empty . + /// + public static readonly ByteString Empty = ReadOnlyMemory.Empty; + + private static readonly uint s_seed = unchecked((uint)new Random().Next()); + private int _hashCode = 0; + + public override ReadOnlyMemory Memory { get; } + public override StackItemType Type => StackItemType.ByteString; + + /// + /// Create a new with the specified data. + /// + /// The data to be contained in this . + public ByteString(ReadOnlyMemory data) + { + this.Memory = data; + } + + private bool Equals(ByteString other) + { + return GetSpan().SequenceEqual(other.GetSpan()); + } + + public override bool Equals(StackItem? other) + { + if (ReferenceEquals(this, other)) return true; + if (other is not ByteString b) return false; + return Equals(b); + } + + internal override bool Equals(StackItem? other, ExecutionEngineLimits limits) + { + uint maxComparableSize = limits.MaxComparableSize; + return Equals(other, ref maxComparableSize); + } + + internal bool Equals(StackItem? other, ref uint limits) + { + if (Size > limits || limits == 0) + throw new InvalidOperationException("The operand exceeds the maximum comparable size."); + uint comparedSize = 1; + try + { + if (other is not ByteString b) return false; + comparedSize = Math.Max((uint)Math.Max(Size, b.Size), comparedSize); + if (ReferenceEquals(this, b)) return true; + if (b.Size > limits) + throw new InvalidOperationException("The operand exceeds the maximum comparable size."); + return Equals(b); + } + finally + { + limits -= comparedSize; + } + } + + public override bool GetBoolean() + { + if (Size > Integer.MaxSize) throw new InvalidCastException(); + return Unsafe.NotZero(GetSpan()); + } + + public override int GetHashCode() + { + if (_hashCode == 0) + { + using Murmur32 murmur = new(s_seed); + _hashCode = BinaryPrimitives.ReadInt32LittleEndian(murmur.ComputeHash(GetSpan().ToArray())); + } + return _hashCode; + } + + public override BigInteger GetInteger() + { + if (Size > Integer.MaxSize) throw new InvalidCastException($"MaxSize exceed: {Size}"); + return new BigInteger(GetSpan()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlyMemory(ByteString value) + { + return value.Memory; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(ByteString value) + { + return value.Memory.Span; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ByteString(byte[] value) + { + return new ByteString(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ByteString(ReadOnlyMemory value) + { + return new ByteString(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ByteString(string value) + { + return new ByteString(Utility.StrictUTF8.GetBytes(value)); + } + + public override string ToString() + { + return GetSpan().TryGetString(out var str) ? $"\"{str}\"" : $"\"Base64: {Convert.ToBase64String(GetSpan())}\""; + } + } +} diff --git a/src/Neo.VM/Types/CompoundType.cs b/src/Neo.VM/Types/CompoundType.cs new file mode 100644 index 0000000000..02323e3f2f --- /dev/null +++ b/src/Neo.VM/Types/CompoundType.cs @@ -0,0 +1,76 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// CompoundType.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; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Neo.VM.Types +{ + /// + /// The base class for complex types in the VM. + /// + [DebuggerDisplay("Type={GetType().Name}, Count={Count}, Id={System.Collections.Generic.ReferenceEqualityComparer.Instance.GetHashCode(this)}")] + public abstract class CompoundType : StackItem + { + /// + /// The reference counter used to count the items in the VM object. + /// + protected readonly ReferenceCounter? ReferenceCounter; + + /// + /// Create a new with the specified reference counter. + /// + /// The reference counter to be used. + protected CompoundType(ReferenceCounter? referenceCounter) + { + this.ReferenceCounter = referenceCounter; + referenceCounter?.AddZeroReferred(this); + } + + /// + /// The number of items in this VM object. + /// + public abstract int Count { get; } + + public abstract IEnumerable SubItems { get; } + + public abstract int SubItemsCount { get; } + + public bool IsReadOnly { get; protected set; } + + /// + /// Remove all items from the VM object. + /// + public abstract void Clear(); + + internal abstract override StackItem DeepCopy(Dictionary refMap, bool asImmutable); + + public sealed override bool GetBoolean() + { + return true; + } + + /// + /// The operation is not supported. Always throw . + /// + /// This method always throws the exception. + public override int GetHashCode() + { + throw new NotSupportedException(); + } + + public override string ToString() + { + return Count.ToString(); + } + } +} diff --git a/src/Neo.VM/Types/Integer.cs b/src/Neo.VM/Types/Integer.cs new file mode 100644 index 0000000000..4d997d6c5a --- /dev/null +++ b/src/Neo.VM/Types/Integer.cs @@ -0,0 +1,139 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Integer.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; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Neo.VM.Types +{ + /// + /// Represents an integer value in the VM. + /// + [DebuggerDisplay("Type={GetType().Name}, Value={value}")] + public class Integer : PrimitiveType + { + /// + /// The maximum size of an integer in bytes. + /// + public const int MaxSize = 32; + + /// + /// Represents the number 0. + /// + public static readonly Integer Zero = 0; + private readonly BigInteger value; + + public override ReadOnlyMemory Memory => value.IsZero ? ReadOnlyMemory.Empty : value.ToByteArray(); + public override int Size { get; } + public override StackItemType Type => StackItemType.Integer; + + /// + /// Create an integer with the specified value. + /// + /// The value of the integer. + public Integer(BigInteger value) + { + if (value.IsZero) + { + Size = 0; + } + else + { + Size = value.GetByteCount(); + if (Size > MaxSize) throw new ArgumentException($"MaxSize exceed: {Size}"); + } + this.value = value; + } + + public override bool Equals(StackItem? other) + { + if (ReferenceEquals(this, other)) return true; + if (other is Integer i) return value == i.value; + return false; + } + + public override bool GetBoolean() + { + return !value.IsZero; + } + + public override int GetHashCode() + { + return HashCode.Combine(value); + } + + public override BigInteger GetInteger() + { + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Integer(sbyte value) + { + return (BigInteger)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Integer(byte value) + { + return (BigInteger)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Integer(short value) + { + return (BigInteger)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Integer(ushort value) + { + return (BigInteger)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Integer(int value) + { + return (BigInteger)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Integer(uint value) + { + return (BigInteger)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Integer(long value) + { + return (BigInteger)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Integer(ulong value) + { + return (BigInteger)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Integer(BigInteger value) + { + return new Integer(value); + } + + public override string ToString() + { + return value.ToString(); + } + } +} diff --git a/src/Neo.VM/Types/InteropInterface.cs b/src/Neo.VM/Types/InteropInterface.cs new file mode 100644 index 0000000000..ee1998316a --- /dev/null +++ b/src/Neo.VM/Types/InteropInterface.cs @@ -0,0 +1,69 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// InteropInterface.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; +using System.Diagnostics; + +namespace Neo.VM.Types +{ + /// + /// Represents an interface used to interoperate with the outside of the the VM. + /// + [DebuggerDisplay("Type={GetType().Name}, Value={_object}")] + public class InteropInterface : StackItem + { + private readonly object _object; + + public override StackItemType Type => StackItemType.InteropInterface; + + /// + /// Create an interoperability interface that wraps the specified . + /// + /// The wrapped . + public InteropInterface(object value) + { + _object = value ?? throw new ArgumentNullException(nameof(value)); + } + + public override bool Equals(StackItem? other) + { + if (ReferenceEquals(this, other)) return true; + if (other is InteropInterface i) return _object.Equals(i._object); + return false; + } + + public override bool GetBoolean() + { + return true; + } + + public override int GetHashCode() + { + return HashCode.Combine(_object); + } + + public override T GetInterface() + { + if (_object is T t) return t; + throw new InvalidCastException($"The item can't be casted to type {typeof(T)}"); + } + + internal object GetInterface() + { + return _object; + } + + public override string ToString() + { + return _object.ToString() ?? "NULL"; + } + } +} diff --git a/src/Neo.VM/Types/Map.cs b/src/Neo.VM/Types/Map.cs new file mode 100644 index 0000000000..5df66fb897 --- /dev/null +++ b/src/Neo.VM/Types/Map.cs @@ -0,0 +1,181 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Map.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.VM.Collections; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace Neo.VM.Types +{ + /// + /// Represents an ordered collection of key-value pairs in the VM. + /// + public class Map : CompoundType, IReadOnlyDictionary + { + /// + /// Indicates the maximum size of keys in bytes. + /// + public const int MaxKeySize = 64; + + private readonly OrderedDictionary dictionary = new(); + + /// + /// Gets or sets the element that has the specified key in the map. + /// + /// The key to locate. + /// The element that has the specified key in the map. + public StackItem this[PrimitiveType key] + { + get + { + if (key.Size > MaxKeySize) + throw new ArgumentException($"MaxKeySize exceed: {key.Size}"); + return dictionary[key]; + } + set + { + if (key.Size > MaxKeySize) + throw new ArgumentException($"MaxKeySize exceed: {key.Size}"); + if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + if (ReferenceCounter != null) + { + if (dictionary.TryGetValue(key, out StackItem? old_value)) + ReferenceCounter.RemoveReference(old_value, this); + else + ReferenceCounter.AddReference(key, this); + ReferenceCounter.AddReference(value, this); + } + dictionary[key] = value; + } + } + + public override int Count => dictionary.Count; + + /// + /// Gets an enumerable collection that contains the keys in the map. + /// + public IEnumerable Keys => dictionary.Keys; + + public override IEnumerable SubItems => Keys.Concat(Values); + + public override int SubItemsCount => dictionary.Count * 2; + + public override StackItemType Type => StackItemType.Map; + + /// + /// Gets an enumerable collection that contains the values in the map. + /// + public IEnumerable Values => dictionary.Values; + + /// + /// Create a new map with the specified reference counter. + /// + /// The reference counter to be used. + public Map(ReferenceCounter? referenceCounter = null) + : base(referenceCounter) + { + } + + public override void Clear() + { + if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + if (ReferenceCounter != null) + foreach (var pair in dictionary) + { + ReferenceCounter.RemoveReference(pair.Key, this); + ReferenceCounter.RemoveReference(pair.Value, this); + } + dictionary.Clear(); + } + + /// + /// Determines whether the map contains an element that has the specified key. + /// + /// The key to locate. + /// + /// if the map contains an element that has the specified key; + /// otherwise, . + /// + public bool ContainsKey(PrimitiveType key) + { + if (key.Size > MaxKeySize) + throw new ArgumentException($"MaxKeySize exceed: {key.Size}"); + return dictionary.ContainsKey(key); + } + + internal override StackItem DeepCopy(Dictionary refMap, bool asImmutable) + { + if (refMap.TryGetValue(this, out StackItem? mappedItem)) return mappedItem; + Map result = new(ReferenceCounter); + refMap.Add(this, result); + foreach (var (k, v) in dictionary) + result[k] = v.DeepCopy(refMap, asImmutable); + result.IsReadOnly = true; + return result; + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + return ((IDictionary)dictionary).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IDictionary)dictionary).GetEnumerator(); + } + + /// + /// Removes the element with the specified key from the map. + /// + /// The key of the element to remove. + /// + /// if the element is successfully removed; + /// otherwise, . + /// This method also returns if was not found in the original map. + /// + public bool Remove(PrimitiveType key) + { + if (key.Size > MaxKeySize) + throw new ArgumentException($"MaxKeySize exceed: {key.Size}"); + if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + if (!dictionary.Remove(key, out StackItem? old_value)) + return false; + ReferenceCounter?.RemoveReference(key, this); + ReferenceCounter?.RemoveReference(old_value, this); + return true; + } + + /// + /// Gets the value that is associated with the specified key. + /// + /// The key to locate. + /// + /// When this method returns, the value associated with the specified key, if the key is found; + /// otherwise, . + /// + /// + /// if the map contains an element that has the specified key; + /// otherwise, . + /// +// supress warning of value parameter nullability mismatch +#pragma warning disable CS8767 + public bool TryGetValue(PrimitiveType key, [MaybeNullWhen(false)] out StackItem value) +#pragma warning restore CS8767 + { + if (key.Size > MaxKeySize) + throw new ArgumentException($"MaxKeySize exceed: {key.Size}"); + return dictionary.TryGetValue(key, out value); + } + } +} diff --git a/src/Neo.VM/Types/Null.cs b/src/Neo.VM/Types/Null.cs new file mode 100644 index 0000000000..238d8b2389 --- /dev/null +++ b/src/Neo.VM/Types/Null.cs @@ -0,0 +1,65 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Null.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; +using System.Diagnostics.CodeAnalysis; + +namespace Neo.VM.Types +{ + /// + /// Represents in the VM. + /// + public class Null : StackItem + { + public override StackItemType Type => StackItemType.Any; + + internal Null() { } + + public override StackItem ConvertTo(StackItemType type) + { + if (type == StackItemType.Any || !Enum.IsDefined(typeof(StackItemType), type)) + throw new InvalidCastException($"Type can't be converted to StackItemType: {type}"); + return this; + } + + public override bool Equals(StackItem? other) + { + if (ReferenceEquals(this, other)) return true; + return other is Null; + } + + public override bool GetBoolean() + { + return false; + } + + public override int GetHashCode() + { + return 0; + } + + [return: MaybeNull] + public override T GetInterface() + { + return default; + } + + public override string? GetString() + { + return null; + } + + public override string ToString() + { + return "NULL"; + } + } +} diff --git a/src/Neo.VM/Types/Pointer.cs b/src/Neo.VM/Types/Pointer.cs new file mode 100644 index 0000000000..1faa52ca74 --- /dev/null +++ b/src/Neo.VM/Types/Pointer.cs @@ -0,0 +1,68 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Pointer.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; +using System.Diagnostics; + +namespace Neo.VM.Types +{ + /// + /// Represents the instruction pointer in the VM, used as the target of jump instructions. + /// + [DebuggerDisplay("Type={GetType().Name}, Position={Position}")] + public class Pointer : StackItem + { + /// + /// The object containing this pointer. + /// + public Script Script { get; } + + /// + /// The position of the pointer in the script. + /// + public int Position { get; } + + public override StackItemType Type => StackItemType.Pointer; + + /// + /// Create a code pointer with the specified script and position. + /// + /// The object containing this pointer. + /// The position of the pointer in the script. + public Pointer(Script script, int position) + { + this.Script = script; + this.Position = position; + } + + public override bool Equals(StackItem? other) + { + if (other == this) return true; + if (other is Pointer p) return Position == p.Position && Script == p.Script; + return false; + } + + public override bool GetBoolean() + { + return true; + } + + public override int GetHashCode() + { + return HashCode.Combine(Script, Position); + } + + public override string ToString() + { + return Position.ToString(); + } + } +} diff --git a/src/Neo.VM/Types/PrimitiveType.cs b/src/Neo.VM/Types/PrimitiveType.cs new file mode 100644 index 0000000000..8415b8a45c --- /dev/null +++ b/src/Neo.VM/Types/PrimitiveType.cs @@ -0,0 +1,139 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// PrimitiveType.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; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Neo.VM.Types +{ + /// + /// The base class for primitive types in the VM. + /// + public abstract class PrimitiveType : StackItem + { + public abstract ReadOnlyMemory Memory { get; } + + /// + /// The size of the VM object in bytes. + /// + public virtual int Size => Memory.Length; + + public override StackItem ConvertTo(StackItemType type) + { + if (type == Type) return this; + return type switch + { + StackItemType.Integer => GetInteger(), + StackItemType.ByteString => Memory, + StackItemType.Buffer => new Buffer(GetSpan()), + _ => base.ConvertTo(type) + }; + } + + internal sealed override StackItem DeepCopy(Dictionary refMap, bool asImmutable) + { + return this; + } + + public abstract override bool Equals(StackItem? other); + + /// + /// Get the hash code of the VM object, which is used for key comparison in the . + /// + /// The hash code of this VM object. + public abstract override int GetHashCode(); + + public sealed override ReadOnlySpan GetSpan() + { + return Memory.Span; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator PrimitiveType(sbyte value) + { + return (Integer)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator PrimitiveType(byte value) + { + return (Integer)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator PrimitiveType(short value) + { + return (Integer)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator PrimitiveType(ushort value) + { + return (Integer)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator PrimitiveType(int value) + { + return (Integer)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator PrimitiveType(uint value) + { + return (Integer)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator PrimitiveType(long value) + { + return (Integer)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator PrimitiveType(ulong value) + { + return (Integer)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator PrimitiveType(BigInteger value) + { + return (Integer)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator PrimitiveType(bool value) + { + return (Boolean)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator PrimitiveType(byte[] value) + { + return (ByteString)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator PrimitiveType(ReadOnlyMemory value) + { + return (ByteString)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator PrimitiveType(string value) + { + return (ByteString)value; + } + } +} diff --git a/src/Neo.VM/Types/StackItem.Vertex.cs b/src/Neo.VM/Types/StackItem.Vertex.cs new file mode 100644 index 0000000000..a988d5db32 --- /dev/null +++ b/src/Neo.VM/Types/StackItem.Vertex.cs @@ -0,0 +1,40 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// StackItem.Vertex.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; +using System.Collections.Generic; +using System.Linq; + +namespace Neo.VM.Types +{ + partial class StackItem + { + internal class ObjectReferenceEntry + { + public StackItem Item; + public int References; + public ObjectReferenceEntry(StackItem item) => Item = item; + } + + internal int StackReferences = 0; + internal Dictionary? ObjectReferences; + internal int DFN = -1; + internal int LowLink = 0; + internal bool OnStack = false; + + internal IEnumerable Successors => ObjectReferences?.Values.Where(p => p.References > 0).Select(p => p.Item) ?? System.Array.Empty(); + + internal void Reset() => (DFN, LowLink, OnStack) = (-1, 0, false); + + public override int GetHashCode() => + HashCode.Combine(GetSpan().ToArray()); + } +} diff --git a/src/Neo.VM/Types/StackItem.cs b/src/Neo.VM/Types/StackItem.cs new file mode 100644 index 0000000000..2d85becacc --- /dev/null +++ b/src/Neo.VM/Types/StackItem.cs @@ -0,0 +1,262 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// StackItem.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. + +#pragma warning disable CS0659 + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Neo.VM.Types +{ + /// + /// The base class for all types in the VM. + /// + public abstract partial class StackItem : IEquatable + { + [ThreadStatic] + private static Boolean? tls_true = null; + + /// + /// Represents in the VM. + /// + public static Boolean True + { + get + { + tls_true ??= new(true); + return tls_true; + } + } + + [ThreadStatic] + private static Boolean? tls_false = null; + + /// + /// Represents in the VM. + /// + public static Boolean False + { + get + { + tls_false ??= new(false); + return tls_false; + } + } + + [ThreadStatic] + private static Null? tls_null = null; + + /// + /// Represents in the VM. + /// + public static StackItem Null + { + get + { + tls_null ??= new(); + return tls_null; + } + } + + /// + /// Indicates whether the object is . + /// + public bool IsNull => this is Null; + + /// + /// The type of this VM object. + /// + public abstract StackItemType Type { get; } + + /// + /// Convert the VM object to the specified type. + /// + /// The type to be converted to. + /// The converted object. + public virtual StackItem ConvertTo(StackItemType type) + { + if (type == Type) return this; + if (type == StackItemType.Boolean) return GetBoolean(); + throw new InvalidCastException(); + } + + internal virtual void Cleanup() + { + } + + /// + /// Copy the object and all its children. + /// + /// The copied object. + public StackItem DeepCopy(bool asImmutable = false) + { + return DeepCopy(new(ReferenceEqualityComparer.Instance), asImmutable); + } + + internal virtual StackItem DeepCopy(Dictionary refMap, bool asImmutable) + { + return this; + } + + public sealed override bool Equals(object? obj) + { + if (ReferenceEquals(this, obj)) return true; + if (obj is StackItem item) return Equals(item); + return false; + } + + public virtual bool Equals(StackItem? other) + { + return ReferenceEquals(this, other); + } + + internal virtual bool Equals(StackItem? other, ExecutionEngineLimits limits) + { + return Equals(other); + } + + /// + /// Wrap the specified and return an containing the . + /// + /// The wrapped . + /// + public static StackItem FromInterface(object? value) + { + if (value is null) return Null; + return new InteropInterface(value); + } + + /// + /// Get the boolean value represented by the VM object. + /// + /// The boolean value represented by the VM object. + public abstract bool GetBoolean(); + + /// + /// Get the integer value represented by the VM object. + /// + /// The integer value represented by the VM object. + public virtual BigInteger GetInteger() + { + throw new InvalidCastException(); + } + + /// + /// Get the wrapped by this interface and convert it to the specified type. + /// + /// The type to convert to. + /// The wrapped . + [return: MaybeNull] + public virtual T GetInterface() where T : notnull + { + throw new InvalidCastException(); + } + + /// + /// Get the readonly span used to read the VM object data. + /// + /// + public virtual ReadOnlySpan GetSpan() + { + throw new InvalidCastException(); + } + + /// + /// Get the value represented by the VM object. + /// + /// The value represented by the VM object. + public virtual string? GetString() + { + return Utility.StrictUTF8.GetString(GetSpan()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator StackItem(sbyte value) + { + return (Integer)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator StackItem(byte value) + { + return (Integer)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator StackItem(short value) + { + return (Integer)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator StackItem(ushort value) + { + return (Integer)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator StackItem(int value) + { + return (Integer)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator StackItem(uint value) + { + return (Integer)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator StackItem(long value) + { + return (Integer)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator StackItem(ulong value) + { + return (Integer)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator StackItem(BigInteger value) + { + return (Integer)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator StackItem(bool value) + { + return value ? True : False; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator StackItem(byte[] value) + { + return (ByteString)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator StackItem(ReadOnlyMemory value) + { + return (ByteString)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator StackItem(string value) + { + return (ByteString)value; + } + } +} diff --git a/src/Neo.VM/Types/StackItemType.cs b/src/Neo.VM/Types/StackItemType.cs new file mode 100644 index 0000000000..c65921e1ac --- /dev/null +++ b/src/Neo.VM/Types/StackItemType.cs @@ -0,0 +1,69 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// StackItemType.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.Types +{ + /// + /// An enumeration representing the types in the VM. + /// + public enum StackItemType : byte + { + /// + /// Represents any type. + /// + Any = 0x00, + + /// + /// Represents a code pointer. + /// + Pointer = 0x10, + + /// + /// Represents the boolean ( or ) type. + /// + Boolean = 0x20, + + /// + /// Represents an integer. + /// + Integer = 0x21, + + /// + /// Represents an immutable memory block. + /// + ByteString = 0x28, + + /// + /// Represents a memory block that can be used for reading and writing. + /// + Buffer = 0x30, + + /// + /// Represents an array or a complex object. + /// + Array = 0x40, + + /// + /// Represents a structure. + /// + Struct = 0x41, + + /// + /// Represents an ordered collection of key-value pairs. + /// + Map = 0x48, + + /// + /// Represents an interface used to interoperate with the outside of the the VM. + /// + InteropInterface = 0x60, + } +} diff --git a/src/Neo.VM/Types/Struct.cs b/src/Neo.VM/Types/Struct.cs new file mode 100644 index 0000000000..8170414363 --- /dev/null +++ b/src/Neo.VM/Types/Struct.cs @@ -0,0 +1,134 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Struct.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; +using System.Collections.Generic; + +namespace Neo.VM.Types +{ + /// + /// Represents a structure in the VM. + /// + public class Struct : Array + { + public override StackItemType Type => StackItemType.Struct; + + /// + /// Create a structure with the specified fields. + /// + /// The fields to be included in the structure. + public Struct(IEnumerable? fields = null) + : this(null, fields) + { + } + + /// + /// Create a structure with the specified fields. And make the structure use the specified . + /// + /// The to be used by this structure. + /// The fields to be included in the structure. + public Struct(ReferenceCounter? referenceCounter, IEnumerable? fields = null) + : base(referenceCounter, fields) + { + } + + /// + /// Create a new structure with the same content as this structure. All nested structures will be copied by value. + /// + /// Execution engine limits + /// The copied structure. + public Struct Clone(ExecutionEngineLimits limits) + { + int count = (int)(limits.MaxStackSize - 1); + Struct result = new(ReferenceCounter); + Queue queue = new(); + queue.Enqueue(result); + queue.Enqueue(this); + while (queue.Count > 0) + { + Struct a = queue.Dequeue(); + Struct b = queue.Dequeue(); + foreach (StackItem item in b) + { + count--; + if (count < 0) throw new InvalidOperationException("Beyond clone limits!"); + if (item is Struct sb) + { + Struct sa = new(ReferenceCounter); + a.Add(sa); + queue.Enqueue(sa); + queue.Enqueue(sb); + } + else + { + a.Add(item); + } + } + } + return result; + } + + public override StackItem ConvertTo(StackItemType type) + { + if (type == StackItemType.Array) + return new Array(ReferenceCounter, new List(_array)); + return base.ConvertTo(type); + } + + public override bool Equals(StackItem? other) + { + throw new NotSupportedException(); + } + + internal override bool Equals(StackItem? other, ExecutionEngineLimits limits) + { + if (other is not Struct s) return false; + Stack stack1 = new(); + Stack stack2 = new(); + stack1.Push(this); + stack2.Push(s); + uint count = limits.MaxStackSize; + uint maxComparableSize = limits.MaxComparableSize; + while (stack1.Count > 0) + { + if (count-- == 0) + throw new InvalidOperationException("Too many struct items to compare."); + StackItem a = stack1.Pop(); + StackItem b = stack2.Pop(); + if (a is ByteString byteString) + { + if (!byteString.Equals(b, ref maxComparableSize)) return false; + } + else + { + if (maxComparableSize == 0) + throw new InvalidOperationException("The operand exceeds the maximum comparable size."); + maxComparableSize -= 1; + if (a is Struct sa) + { + if (ReferenceEquals(a, b)) continue; + if (b is not Struct sb) return false; + if (sa.Count != sb.Count) return false; + foreach (StackItem item in sa) + stack1.Push(item); + foreach (StackItem item in sb) + stack2.Push(item); + } + else + { + if (!a.Equals(b)) return false; + } + } + } + return true; + } + } +} diff --git a/src/Neo.VM/Unsafe.cs b/src/Neo.VM/Unsafe.cs new file mode 100644 index 0000000000..e3f4366be2 --- /dev/null +++ b/src/Neo.VM/Unsafe.cs @@ -0,0 +1,42 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Unsafe.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; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ + unsafe internal static class Unsafe + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool NotZero(ReadOnlySpan x) + { + int len = x.Length; + if (len == 0) return false; + fixed (byte* xp = x) + { + long* xlp = (long*)xp; + for (; len >= 8; len -= 8) + { + if (*xlp != 0) return true; + xlp++; + } + byte* xbp = (byte*)xlp; + for (; len > 0; len--) + { + if (*xbp != 0) return true; + xbp++; + } + } + return false; + } + } +} diff --git a/src/Neo.VM/Utility.cs b/src/Neo.VM/Utility.cs new file mode 100644 index 0000000000..7c1852b7f9 --- /dev/null +++ b/src/Neo.VM/Utility.cs @@ -0,0 +1,120 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Utility.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; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Neo.VM +{ + public static class Utility + { + public static Encoding StrictUTF8 { get; } + + static Utility() + { + StrictUTF8 = (Encoding)Encoding.UTF8.Clone(); + StrictUTF8.DecoderFallback = DecoderFallback.ExceptionFallback; + StrictUTF8.EncoderFallback = EncoderFallback.ExceptionFallback; + } + + public static bool TryGetString(this ReadOnlySpan bytes, out string? value) + { + try + { + value = StrictUTF8.GetString(bytes); + return true; + } + catch + { + value = default; + return false; + } + } + + public static BigInteger ModInverse(this BigInteger value, BigInteger modulus) + { + if (value <= 0) throw new ArgumentOutOfRangeException(nameof(value)); + if (modulus < 2) throw new ArgumentOutOfRangeException(nameof(modulus)); + BigInteger r = value, old_r = modulus, s = 1, old_s = 0; + while (r > 0) + { + var q = old_r / r; + (old_r, r) = (r, old_r % r); + (old_s, s) = (s, old_s - q * s); + } + var result = old_s % modulus; + if (result < 0) result += modulus; + if (!(value * result % modulus).IsOne) throw new InvalidOperationException(); + return result; + } + + public static BigInteger Sqrt(this BigInteger value) + { + if (value < 0) throw new InvalidOperationException("value can not be negative"); + if (value.IsZero) return BigInteger.Zero; + if (value < 4) return BigInteger.One; + + var z = value; + var x = BigInteger.One << (int)(((value - 1).GetBitLength() + 1) >> 1); + while (x < z) + { + z = x; + x = (value / x + x) / 2; + } + + return z; + } + + /// + /// Gets the number of bits required for shortest two's complement representation of the current instance without the sign bit. + /// + /// The minimum non-negative number of bits in two's complement notation without the sign bit. + /// This method returns 0 if the value of current object is equal to or . For positive integers the return value is equal to the ordinary binary representation string length. + public static long GetBitLength(this BigInteger value) + { +#if NET5_0_OR_GREATER + return value.GetBitLength(); +#else + if (value == 0 || value == BigInteger.MinusOne) return 0; + + // Note: This method is imprecise and might not work as expected with integers larger than 256 bits. + var b = value.ToByteArray(); + if (b.Length == 1 || (b.Length == 2 && b[1] == 0)) + { + return BitCount(value.Sign > 0 ? b[0] : (byte)(255 - b[0])); + } + return (b.Length - 1) * 8 + BitCount(value.Sign > 0 ? b[^1] : 255 - b[^1]); +#endif + } + +#if !NET5_0_OR_GREATER + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int BitCount(int w) + { + return w < 1 << 15 ? (w < 1 << 7 + ? (w < 1 << 3 ? (w < 1 << 1 + ? (w < 1 << 0 ? (w < 0 ? 32 : 0) : 1) + : (w < 1 << 2 ? 2 : 3)) : (w < 1 << 5 + ? (w < 1 << 4 ? 4 : 5) + : (w < 1 << 6 ? 6 : 7))) + : (w < 1 << 11 + ? (w < 1 << 9 ? (w < 1 << 8 ? 8 : 9) : (w < 1 << 10 ? 10 : 11)) + : (w < 1 << 13 ? (w < 1 << 12 ? 12 : 13) : (w < 1 << 14 ? 14 : 15)))) : (w < 1 << 23 ? (w < 1 << 19 + ? (w < 1 << 17 ? (w < 1 << 16 ? 16 : 17) : (w < 1 << 18 ? 18 : 19)) + : (w < 1 << 21 ? (w < 1 << 20 ? 20 : 21) : (w < 1 << 22 ? 22 : 23))) : (w < 1 << 27 + ? (w < 1 << 25 ? (w < 1 << 24 ? 24 : 25) : (w < 1 << 26 ? 26 : 27)) + : (w < 1 << 29 ? (w < 1 << 28 ? 28 : 29) : (w < 1 << 30 ? 30 : 31)))); + } +#endif + } +} diff --git a/src/Neo.VM/VMState.cs b/src/Neo.VM/VMState.cs new file mode 100644 index 0000000000..b9b0fa22d8 --- /dev/null +++ b/src/Neo.VM/VMState.cs @@ -0,0 +1,39 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// VMState.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 +{ + /// + /// Indicates the status of the VM. + /// + public enum VMState : byte + { + /// + /// Indicates that the execution is in progress or has not yet begun. + /// + NONE = 0, + + /// + /// Indicates that the execution has been completed successfully. + /// + HALT = 1 << 0, + + /// + /// Indicates that the execution has ended, and an exception that cannot be caught is thrown. + /// + FAULT = 1 << 1, + + /// + /// Indicates that a breakpoint is currently being hit. + /// + BREAK = 1 << 2, + } +} diff --git a/src/Neo.VM/VMUnhandledException.cs b/src/Neo.VM/VMUnhandledException.cs new file mode 100644 index 0000000000..78377059ae --- /dev/null +++ b/src/Neo.VM/VMUnhandledException.cs @@ -0,0 +1,53 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// VMUnhandledException.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.VM.Types; +using System; +using System.Text; +using Array = Neo.VM.Types.Array; + +namespace Neo.VM +{ + /// + /// Represents an unhandled exception in the VM. + /// Thrown when there is an exception in the VM that is not caught by any script. + /// + public class VMUnhandledException : Exception + { + /// + /// The unhandled exception in the VM. + /// + public StackItem ExceptionObject { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The unhandled exception in the VM. + public VMUnhandledException(StackItem ex) : base(GetExceptionMessage(ex)) + { + ExceptionObject = ex; + } + + private static string GetExceptionMessage(StackItem e) + { + StringBuilder sb = new("An unhandled exception was thrown."); + ByteString? s = e as ByteString; + if (s is null && e is Array array && array.Count > 0) + s = array[0] as ByteString; + if (s != null) + { + sb.Append(' '); + sb.Append(Encoding.UTF8.GetString(s.GetSpan())); + } + return sb.ToString(); + } + } +} diff --git a/src/neo/BigDecimal.cs b/src/Neo/BigDecimal.cs similarity index 90% rename from src/neo/BigDecimal.cs rename to src/Neo/BigDecimal.cs index f9c6e0ffb1..05cdf86626 100644 --- a/src/neo/BigDecimal.cs +++ b/src/Neo/BigDecimal.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// BigDecimal.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. @@ -24,17 +25,17 @@ public struct BigDecimal : IComparable, IEquatable /// /// The value of the number. /// - public BigInteger Value => value; + public readonly BigInteger Value => value; /// /// The number of decimal places for this number. /// - public byte Decimals => decimals; + public readonly byte Decimals => decimals; /// /// The sign of the number. /// - public int Sign => value.Sign; + public readonly int Sign => value.Sign; /// /// Initializes a new instance of the struct. @@ -54,7 +55,7 @@ public BigDecimal(BigInteger value, byte decimals) public unsafe BigDecimal(decimal value) { Span span = stackalloc int[4]; - decimal.GetBits(value, span); + span = decimal.GetBits(value); fixed (int* p = span) { ReadOnlySpan buffer = new(p, 16); @@ -72,7 +73,7 @@ public unsafe BigDecimal(decimal value) public unsafe BigDecimal(decimal value, byte decimals) { Span span = stackalloc int[4]; - decimal.GetBits(value, span); + span = decimal.GetBits(value); fixed (int* p = span) { ReadOnlySpan buffer = new(p, 16); @@ -92,7 +93,7 @@ public unsafe BigDecimal(decimal value, byte decimals) /// /// The new decimals field. /// The that has the new number of decimal places. - public BigDecimal ChangeDecimals(byte decimals) + public readonly BigDecimal ChangeDecimals(byte decimals) { if (this.decimals == decimals) return this; BigInteger value; @@ -128,7 +129,7 @@ public static BigDecimal Parse(string s, byte decimals) /// Gets a representing the number. /// /// The representing the number. - public override string ToString() + public override readonly string ToString() { BigInteger divisor = BigInteger.Pow(10, decimals); BigInteger result = BigInteger.DivRem(value, divisor, out BigInteger remainder); @@ -181,7 +182,7 @@ public static bool TryParse(string s, byte decimals, out BigDecimal result) return true; } - public int CompareTo(BigDecimal other) + public readonly int CompareTo(BigDecimal other) { BigInteger left = value, right = other.value; if (decimals < other.decimals) @@ -191,18 +192,18 @@ public int CompareTo(BigDecimal other) return left.CompareTo(right); } - public override bool Equals(object obj) + public override readonly bool Equals(object obj) { if (obj is not BigDecimal @decimal) return false; return Equals(@decimal); } - public bool Equals(BigDecimal other) + public readonly bool Equals(BigDecimal other) { return CompareTo(other) == 0; } - public override int GetHashCode() + public override readonly int GetHashCode() { BigInteger divisor = BigInteger.Pow(10, decimals); BigInteger result = BigInteger.DivRem(value, divisor, out BigInteger remainder); diff --git a/src/Neo/ContainsTransactionType.cs b/src/Neo/ContainsTransactionType.cs new file mode 100644 index 0000000000..cb91eb303f --- /dev/null +++ b/src/Neo/ContainsTransactionType.cs @@ -0,0 +1,20 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ContainsTransactionType.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 +{ + public enum ContainsTransactionType + { + NotExist, + ExistsInPool, + ExistsInLedger + } +} diff --git a/src/neo/Cryptography/Base58.cs b/src/Neo/Cryptography/Base58.cs similarity index 90% rename from src/neo/Cryptography/Base58.cs rename to src/Neo/Cryptography/Base58.cs index 92ec97291e..adecc8b631 100644 --- a/src/neo/Cryptography/Base58.cs +++ b/src/Neo/Cryptography/Base58.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Base58.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. @@ -45,7 +46,9 @@ public static byte[] Base58CheckDecode(this string input) } /// - /// Converts a byte array to its equivalent representation that is encoded with base-58 digits. The encoded contains the checksum of the binary data. + /// Converts a byte array to its equivalent + /// representation that is encoded with base-58 digits. + /// The encoded contains the checksum of the binary data. /// /// The byte array to convert. /// The representation, in base-58, of the contents of . @@ -67,7 +70,7 @@ public static string Base58CheckEncode(this ReadOnlySpan data) /// A byte array that is equivalent to . public static byte[] Decode(string input) { - // Decode Base58 string to BigInteger + // Decode Base58 string to BigInteger var bi = BigInteger.Zero; for (int i = 0; i < input.Length; i++) { diff --git a/src/neo/Cryptography/BloomFilter.cs b/src/Neo/Cryptography/BloomFilter.cs similarity index 69% rename from src/neo/Cryptography/BloomFilter.cs rename to src/Neo/Cryptography/BloomFilter.cs index f9da635046..a1639678e5 100644 --- a/src/neo/Cryptography/BloomFilter.cs +++ b/src/Neo/Cryptography/BloomFilter.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// BloomFilter.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. @@ -37,6 +38,21 @@ public class BloomFilter /// public uint Tweak { get; private set; } + /// + /// Initializes a new instance of the class. + /// + /// The size of the bit array used by the bloom filter. + /// The number of hash functions used by the bloom filter. + /// Used to generate the seeds of the murmur hash functions. + public BloomFilter(int m, int k, uint nTweak) + { + if (k < 0 || m < 0) throw new ArgumentOutOfRangeException(); + this.seeds = Enumerable.Range(0, k).Select(p => (uint)p * 0xFBA4C795 + nTweak).ToArray(); + this.bits = new BitArray(m); + this.bits.Length = m; + this.Tweak = nTweak; + } + /// /// Initializes a new instance of the class. /// @@ -44,11 +60,11 @@ public class BloomFilter /// The number of hash functions used by the bloom filter. /// Used to generate the seeds of the murmur hash functions. /// The initial elements contained in this object. - public BloomFilter(int m, int k, uint nTweak, byte[] elements = null) + public BloomFilter(int m, int k, uint nTweak, ReadOnlyMemory elements) { if (k < 0 || m < 0) throw new ArgumentOutOfRangeException(); this.seeds = Enumerable.Range(0, k).Select(p => (uint)p * 0xFBA4C795 + nTweak).ToArray(); - this.bits = elements == null ? new BitArray(m) : new BitArray(elements); + this.bits = new BitArray(elements.ToArray()); this.bits.Length = m; this.Tweak = nTweak; } @@ -57,9 +73,9 @@ public BloomFilter(int m, int k, uint nTweak, byte[] elements = null) /// Adds an element to the . /// /// The object to add to the . - public void Add(byte[] element) + public void Add(ReadOnlyMemory element) { - foreach (uint i in seeds.AsParallel().Select(s => element.Murmur32(s))) + foreach (uint i in seeds.AsParallel().Select(s => element.Span.Murmur32(s))) bits.Set((int)(i % (uint)bits.Length), true); } diff --git a/src/neo/Cryptography/Crypto.cs b/src/Neo/Cryptography/Crypto.cs similarity index 94% rename from src/neo/Cryptography/Crypto.cs rename to src/Neo/Cryptography/Crypto.cs index f703a8a4a4..e09b556d82 100644 --- a/src/neo/Cryptography/Crypto.cs +++ b/src/Neo/Cryptography/Crypto.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Crypto.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. diff --git a/src/neo/Cryptography/ECC/ECCurve.cs b/src/Neo/Cryptography/ECC/ECCurve.cs similarity index 87% rename from src/neo/Cryptography/ECC/ECCurve.cs rename to src/Neo/Cryptography/ECC/ECCurve.cs index e909d6378b..b228f7ab31 100644 --- a/src/neo/Cryptography/ECC/ECCurve.cs +++ b/src/Neo/Cryptography/ECC/ECCurve.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ECCurve.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. @@ -36,7 +37,7 @@ public class ECCurve private ECCurve(BigInteger Q, BigInteger A, BigInteger B, BigInteger N, byte[] G) { this.Q = Q; - this.ExpectedECPointLength = ((int)Q.GetBitLength() + 7) / 8; + this.ExpectedECPointLength = ((int)VM.Utility.GetBitLength(Q) + 7) / 8; this.A = new ECFieldElement(A, this); this.B = new ECFieldElement(B, this); this.N = N; diff --git a/src/neo/Cryptography/ECC/ECFieldElement.cs b/src/Neo/Cryptography/ECC/ECFieldElement.cs similarity index 93% rename from src/neo/Cryptography/ECC/ECFieldElement.cs rename to src/Neo/Cryptography/ECC/ECFieldElement.cs index d138666b3f..e08b5bb71f 100644 --- a/src/neo/Cryptography/ECC/ECFieldElement.cs +++ b/src/Neo/Cryptography/ECC/ECFieldElement.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ECFieldElement.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. @@ -53,7 +54,7 @@ public bool Equals(ECFieldElement other) private static BigInteger[] FastLucasSequence(BigInteger p, BigInteger P, BigInteger Q, BigInteger k) { - int n = (int)k.GetBitLength(); + int n = (int)VM.Utility.GetBitLength(k); int s = k.GetLowestSetBit(); BigInteger Uh = 1; @@ -125,7 +126,7 @@ public ECFieldElement Sqrt() BigInteger P; do { - P = rand.NextBigInteger((int)curve.Q.GetBitLength()); + P = rand.NextBigInteger((int)VM.Utility.GetBitLength(curve.Q)); } while (P >= curve.Q || BigInteger.ModPow(P * P - fourQ, legendreExponent, curve.Q) != qMinusOne); BigInteger[] result = FastLucasSequence(curve.Q, P, Q, k); diff --git a/src/neo/Cryptography/ECC/ECPoint.cs b/src/Neo/Cryptography/ECC/ECPoint.cs similarity index 89% rename from src/neo/Cryptography/ECC/ECPoint.cs rename to src/Neo/Cryptography/ECC/ECPoint.cs index 70004fc343..b2d44bf6cf 100644 --- a/src/neo/Cryptography/ECC/ECPoint.cs +++ b/src/Neo/Cryptography/ECC/ECPoint.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ECPoint.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. @@ -143,45 +144,28 @@ private static ECPoint DecompressPoint(int yTilde, BigInteger X1, ECCurve curve) return new ECPoint(x, beta, curve); } - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { - ECPoint p = DeserializeFrom(reader, Curve); + ECPoint p = DeserializeFrom(ref reader, Curve); X = p.X; Y = p.Y; } /// - /// Deserializes an object from a . + /// Deserializes an object from a . /// - /// The for reading data. + /// The for reading data. /// The object used to construct the . /// The deserialized point. - public static ECPoint DeserializeFrom(BinaryReader reader, ECCurve curve) + public static ECPoint DeserializeFrom(ref MemoryReader reader, ECCurve curve) { - Span buffer = stackalloc byte[1 + curve.ExpectedECPointLength * 2]; - buffer[0] = reader.ReadByte(); - switch (buffer[0]) + int size = reader.Peek() switch { - case 0x02: - case 0x03: - { - if (reader.Read(buffer[1..(1 + curve.ExpectedECPointLength)]) != curve.ExpectedECPointLength) - { - throw new FormatException(); - } - return DecodePoint(buffer[..(1 + curve.ExpectedECPointLength)], curve); - } - case 0x04: - { - if (reader.Read(buffer[1..(1 + curve.ExpectedECPointLength * 2)]) != curve.ExpectedECPointLength * 2) - { - throw new FormatException(); - } - return DecodePoint(buffer, curve); - } - default: - throw new FormatException("Invalid point encoding " + buffer[0]); - } + 0x02 or 0x03 => 1 + curve.ExpectedECPointLength, + 0x04 => 1 + curve.ExpectedECPointLength * 2, + _ => throw new FormatException("Invalid point encoding " + reader.Peek()) + }; + return DecodePoint(reader.ReadMemory(size).Span, curve); } /// @@ -253,15 +237,15 @@ public override int GetHashCode() internal static ECPoint Multiply(ECPoint p, BigInteger k) { // floor(log2(k)) - int m = (int)k.GetBitLength(); + int m = (int)VM.Utility.GetBitLength(k); // width of the Window NAF sbyte width; - // Required length of precomputation array + // Required length of precomputing array int reqPreCompLen; - // Determine optimal width and corresponding length of precomputation + // Determine optimal width and corresponding length of precomputing array // array based on literature values if (m < 13) { @@ -299,7 +283,7 @@ internal static ECPoint Multiply(ECPoint p, BigInteger k) reqPreCompLen = 127; } - // The length of the precomputation array + // The length of the precomputing array int preCompLen = 1; ECPoint[] preComp = new ECPoint[] { p }; @@ -307,7 +291,7 @@ internal static ECPoint Multiply(ECPoint p, BigInteger k) if (preCompLen < reqPreCompLen) { - // Precomputation array must be made bigger, copy existing preComp + // Precomputing array must be made bigger, copy existing preComp // array into the larger new preComp array ECPoint[] oldPreComp = preComp; preComp = new ECPoint[reqPreCompLen]; @@ -315,7 +299,7 @@ internal static ECPoint Multiply(ECPoint p, BigInteger k) for (int i = preCompLen; i < reqPreCompLen; i++) { - // Compute the new ECPoints for the precomputation array. + // Compute the new ECPoints for the precomputing array. // The values 1, 3, 5, ..., 2^(width-1)-1 times p are // computed preComp[i] = twiceP + preComp[i - 1]; @@ -407,7 +391,7 @@ internal ECPoint Twice() private static sbyte[] WindowNaf(sbyte width, BigInteger k) { - sbyte[] wnaf = new sbyte[k.GetBitLength() + 1]; + sbyte[] wnaf = new sbyte[VM.Utility.GetBitLength(k) + 1]; short pow2wB = (short)(1 << width); int i = 0; int length = 0; diff --git a/src/neo/Cryptography/Helper.cs b/src/Neo/Cryptography/Helper.cs similarity index 59% rename from src/neo/Cryptography/Helper.cs rename to src/Neo/Cryptography/Helper.cs index 6fccfcf81b..840b6ebb7b 100644 --- a/src/neo/Cryptography/Helper.cs +++ b/src/Neo/Cryptography/Helper.cs @@ -1,24 +1,26 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// 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 Neo.IO; using Neo.Network.P2P.Payloads; using Neo.Wallets; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Parameters; using System; using System.Buffers.Binary; -using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security; using System.Security.Cryptography; -using System.Text; using static Neo.Helper; using ECPoint = Neo.Cryptography.ECC.ECPoint; @@ -29,6 +31,7 @@ namespace Neo.Cryptography /// public static class Helper { + private static readonly bool IsOSX = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); /// /// Computes the hash value for the specified byte array using the ripemd160 algorithm. /// @@ -63,6 +66,20 @@ public static uint Murmur32(this byte[] value, uint seed) return BinaryPrimitives.ReadUInt32LittleEndian(murmur.ComputeHash(value)); } + /// + /// Computes the hash value for the specified byte array using the murmur algorithm. + /// + /// The input to compute the hash code for. + /// The seed used by the murmur algorithm. + /// The computed hash code. + public static uint Murmur32(this ReadOnlySpan value, uint seed) + { + Span buffer = stackalloc byte[sizeof(uint)]; + using Murmur32 murmur = new(seed); + murmur.TryComputeHash(value, buffer, out _); + return BinaryPrimitives.ReadUInt32LittleEndian(buffer); + } + /// /// Computes the 128-bit hash value for the specified byte array using the murmur algorithm. /// @@ -75,6 +92,20 @@ public static byte[] Murmur128(this byte[] value, uint seed) return murmur.ComputeHash(value); } + /// + /// Computes the 128-bit hash value for the specified byte array using the murmur algorithm. + /// + /// The input to compute the hash code for. + /// The seed used by the murmur algorithm. + /// The computed hash code. + public static byte[] Murmur128(this ReadOnlySpan value, uint seed) + { + byte[] buffer = new byte[16]; + using Murmur128 murmur = new(seed); + murmur.TryComputeHash(value, buffer, out _); + return buffer; + } + /// /// Computes the hash value for the specified byte array using the sha256 algorithm. /// @@ -125,10 +156,26 @@ public static byte[] Sha256(this Span value) public static byte[] AES256Encrypt(this byte[] plainData, byte[] key, byte[] nonce, byte[] associatedData = null) { if (nonce.Length != 12) throw new ArgumentOutOfRangeException(nameof(nonce)); - var cipherBytes = new byte[plainData.Length]; var tag = new byte[16]; - using var cipher = new AesGcm(key); - cipher.Encrypt(nonce, plainData, cipherBytes, tag, associatedData); + var cipherBytes = new byte[plainData.Length]; + if (!IsOSX) + { + using var cipher = new AesGcm(key); + cipher.Encrypt(nonce, plainData, cipherBytes, tag, associatedData); + } + else + { + var cipher = new GcmBlockCipher(new AesEngine()); + var parameters = new AeadParameters( + new KeyParameter(key), + 128, //128 = 16 * 8 => (tag size * 8) + nonce, + associatedData); + cipher.Init(true, parameters); + cipherBytes = new byte[cipher.GetOutputSize(plainData.Length)]; + var length = cipher.ProcessBytes(plainData, 0, plainData.Length, cipherBytes, 0); + cipher.DoFinal(cipherBytes, length); + } return Concat(nonce, cipherBytes, tag); } @@ -139,8 +186,24 @@ public static byte[] AES256Decrypt(this byte[] encryptedData, byte[] key, byte[] var cipherBytes = encrypted[12..^16]; var tag = encrypted[^16..]; var decryptedData = new byte[cipherBytes.Length]; - using var cipher = new AesGcm(key); - cipher.Decrypt(nonce, cipherBytes, tag, decryptedData, associatedData); + if (!IsOSX) + { + using var cipher = new AesGcm(key); + cipher.Decrypt(nonce, cipherBytes, tag, decryptedData, associatedData); + } + else + { + var cipher = new GcmBlockCipher(new AesEngine()); + var parameters = new AeadParameters( + new KeyParameter(key), + 128, //128 = 16 * 8 => (tag size * 8) + nonce.ToArray(), + associatedData); + cipher.Init(false, parameters); + decryptedData = new byte[cipher.GetOutputSize(cipherBytes.Length)]; + var length = cipher.ProcessBytes(cipherBytes.ToArray(), 0, cipherBytes.Length, decryptedData, 0); + cipher.DoFinal(decryptedData, length); + } return decryptedData; } @@ -178,52 +241,28 @@ internal static bool Test(this BloomFilter filter, Transaction tx) return false; } - internal static byte[] ToAesKey(this string password) - { - using SHA256 sha256 = SHA256.Create(); - byte[] passwordBytes = Encoding.UTF8.GetBytes(password); - byte[] passwordHash = sha256.ComputeHash(passwordBytes); - byte[] passwordHash2 = sha256.ComputeHash(passwordHash); - Array.Clear(passwordBytes, 0, passwordBytes.Length); - Array.Clear(passwordHash, 0, passwordHash.Length); - return passwordHash2; - } - - internal static byte[] ToAesKey(this SecureString password) - { - using SHA256 sha256 = SHA256.Create(); - byte[] passwordBytes = password.ToArray(); - byte[] passwordHash = sha256.ComputeHash(passwordBytes); - byte[] passwordHash2 = sha256.ComputeHash(passwordHash); - Array.Clear(passwordBytes, 0, passwordBytes.Length); - Array.Clear(passwordHash, 0, passwordHash.Length); - return passwordHash2; - } + /// + /// Rotates the specified value left by the specified number of bits. + /// Similar in behavior to the x86 instruction ROL. + /// + /// The value to rotate. + /// The number of bits to rotate by. + /// Any value outside the range [0..31] is treated as congruent mod 32. + /// The rotated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint RotateLeft(uint value, int offset) + => (value << offset) | (value >> (32 - offset)); - internal static byte[] ToArray(this SecureString s) - { - if (s == null) - throw new NullReferenceException(); - if (s.Length == 0) - return Array.Empty(); - List result = new(); - IntPtr ptr = SecureStringMarshal.SecureStringToGlobalAllocAnsi(s); - try - { - int i = 0; - do - { - byte b = Marshal.ReadByte(ptr, i++); - if (b == 0) - break; - result.Add(b); - } while (true); - } - finally - { - Marshal.ZeroFreeGlobalAllocAnsi(ptr); - } - return result.ToArray(); - } + /// + /// Rotates the specified value left by the specified number of bits. + /// Similar in behavior to the x86 instruction ROL. + /// + /// The value to rotate. + /// The number of bits to rotate by. + /// Any value outside the range [0..63] is treated as congruent mod 64. + /// The rotated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong RotateLeft(ulong value, int offset) + => (value << offset) | (value >> (64 - offset)); } } diff --git a/src/neo/Cryptography/MerkleTree.cs b/src/Neo/Cryptography/MerkleTree.cs similarity index 94% rename from src/neo/Cryptography/MerkleTree.cs rename to src/Neo/Cryptography/MerkleTree.cs index 376fedc11e..58d3856b2c 100644 --- a/src/neo/Cryptography/MerkleTree.cs +++ b/src/Neo/Cryptography/MerkleTree.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// MerkleTree.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. diff --git a/src/neo/Cryptography/MerkleTreeNode.cs b/src/Neo/Cryptography/MerkleTreeNode.cs similarity index 60% rename from src/neo/Cryptography/MerkleTreeNode.cs rename to src/Neo/Cryptography/MerkleTreeNode.cs index 378d478d7f..8fcc2dd5ce 100644 --- a/src/neo/Cryptography/MerkleTreeNode.cs +++ b/src/Neo/Cryptography/MerkleTreeNode.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// MerkleTreeNode.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. diff --git a/src/Neo/Cryptography/Murmur128.cs b/src/Neo/Cryptography/Murmur128.cs new file mode 100644 index 0000000000..3ee258b4a2 --- /dev/null +++ b/src/Neo/Cryptography/Murmur128.cs @@ -0,0 +1,153 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Murmur128.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; +using System.Buffers.Binary; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; + +namespace Neo.Cryptography +{ + /// + /// Computes the 128 bits murmur hash for the input data. + /// + public sealed class Murmur128 : HashAlgorithm + { + private const ulong c1 = 0x87c37b91114253d5; + private const ulong c2 = 0x4cf5ad432745937f; + private const int r1 = 31; + private const int r2 = 33; + private const uint m = 5; + private const uint n1 = 0x52dce729; + private const uint n2 = 0x38495ab5; + + private readonly uint seed; + private int length; + + public override int HashSize => 128; + + private ulong H1 { get; set; } + private ulong H2 { get; set; } + + /// + /// Initializes a new instance of the class with the specified seed. + /// + /// The seed to be used. + public Murmur128(uint seed) + { + this.seed = seed; + Initialize(); + } + + protected override void HashCore(byte[] array, int ibStart, int cbSize) + { + HashCore(array.AsSpan(ibStart, cbSize)); + } + + protected override void HashCore(ReadOnlySpan source) + { + int cbSize = source.Length; + length += cbSize; + int remainder = cbSize & 15; + int alignedLength = cbSize - remainder; + for (int i = 0; i < alignedLength; i += 16) + { + ulong k1 = BinaryPrimitives.ReadUInt64LittleEndian(source[i..]); + k1 *= c1; + k1 = Helper.RotateLeft(k1, r1); + k1 *= c2; + H1 ^= k1; + H1 = Helper.RotateLeft(H1, 27); + H1 += H2; + H1 = H1 * m + n1; + + ulong k2 = BinaryPrimitives.ReadUInt64LittleEndian(source[(i + 8)..]); + k2 *= c2; + k2 = Helper.RotateLeft(k2, r2); + k2 *= c1; + H2 ^= k2; + H2 = Helper.RotateLeft(H2, 31); + H2 += H1; + H2 = H2 * m + n2; + } + + if (remainder > 0) + { + ulong remainingBytesL = 0, remainingBytesH = 0; + switch (remainder) + { + case 15: remainingBytesH ^= (ulong)source[alignedLength + 14] << 48; goto case 14; + case 14: remainingBytesH ^= (ulong)source[alignedLength + 13] << 40; goto case 13; + case 13: remainingBytesH ^= (ulong)source[alignedLength + 12] << 32; goto case 12; + case 12: remainingBytesH ^= (ulong)source[alignedLength + 11] << 24; goto case 11; + case 11: remainingBytesH ^= (ulong)source[alignedLength + 10] << 16; goto case 10; + case 10: remainingBytesH ^= (ulong)source[alignedLength + 9] << 8; goto case 9; + case 9: remainingBytesH ^= (ulong)source[alignedLength + 8] << 0; goto case 8; + case 8: remainingBytesL ^= (ulong)source[alignedLength + 7] << 56; goto case 7; + case 7: remainingBytesL ^= (ulong)source[alignedLength + 6] << 48; goto case 6; + case 6: remainingBytesL ^= (ulong)source[alignedLength + 5] << 40; goto case 5; + case 5: remainingBytesL ^= (ulong)source[alignedLength + 4] << 32; goto case 4; + case 4: remainingBytesL ^= (ulong)source[alignedLength + 3] << 24; goto case 3; + case 3: remainingBytesL ^= (ulong)source[alignedLength + 2] << 16; goto case 2; + case 2: remainingBytesL ^= (ulong)source[alignedLength + 1] << 8; goto case 1; + case 1: remainingBytesL ^= (ulong)source[alignedLength] << 0; break; + } + + H2 ^= Helper.RotateLeft(remainingBytesH * c2, r2) * c1; + H1 ^= Helper.RotateLeft(remainingBytesL * c1, r1) * c2; + } + } + + protected override byte[] HashFinal() + { + byte[] buffer = new byte[sizeof(ulong) * 2]; + TryHashFinal(buffer, out _); + return buffer; + } + + protected override bool TryHashFinal(Span destination, out int bytesWritten) + { + ulong len = (ulong)length; + H1 ^= len; H2 ^= len; + + H1 += H2; + H2 += H1; + + H1 = FMix(H1); + H2 = FMix(H2); + + H1 += H2; + H2 += H1; + + if (BinaryPrimitives.TryWriteUInt64LittleEndian(destination, H1)) + BinaryPrimitives.TryWriteUInt64LittleEndian(destination[sizeof(ulong)..], H2); + + bytesWritten = Math.Min(destination.Length, sizeof(ulong) * 2); + return bytesWritten == sizeof(ulong) * 2; + } + + public override void Initialize() + { + H1 = H2 = seed; + length = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong FMix(ulong h) + { + h = (h ^ (h >> 33)) * 0xff51afd7ed558ccd; + h = (h ^ (h >> 33)) * 0xc4ceb9fe1a85ec53; + + return (h ^ (h >> 33)); + } + } +} diff --git a/src/Neo/Cryptography/Murmur32.cs b/src/Neo/Cryptography/Murmur32.cs new file mode 100644 index 0000000000..8feaa92d37 --- /dev/null +++ b/src/Neo/Cryptography/Murmur32.cs @@ -0,0 +1,107 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Murmur32.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; +using System.Buffers.Binary; +using System.Numerics; +using System.Security.Cryptography; + +namespace Neo.Cryptography +{ + /// + /// Computes the murmur hash for the input data. + /// + public sealed class Murmur32 : HashAlgorithm + { + private const uint c1 = 0xcc9e2d51; + private const uint c2 = 0x1b873593; + private const int r1 = 15; + private const int r2 = 13; + private const uint m = 5; + private const uint n = 0xe6546b64; + + private readonly uint seed; + private uint hash; + private int length; + + public override int HashSize => 32; + + /// + /// Initializes a new instance of the class with the specified seed. + /// + /// The seed to be used. + public Murmur32(uint seed) + { + this.seed = seed; + Initialize(); + } + + protected override void HashCore(byte[] array, int ibStart, int cbSize) + { + HashCore(array.AsSpan(ibStart, cbSize)); + } + + protected override void HashCore(ReadOnlySpan source) + { + length += source.Length; + for (; source.Length >= 4; source = source[4..]) + { + uint k = BinaryPrimitives.ReadUInt32LittleEndian(source); + k *= c1; + k = Helper.RotateLeft(k, r1); + k *= c2; + hash ^= k; + hash = Helper.RotateLeft(hash, r2); + hash = hash * m + n; + } + if (source.Length > 0) + { + uint remainingBytes = 0; + switch (source.Length) + { + case 3: remainingBytes ^= (uint)source[2] << 16; goto case 2; + case 2: remainingBytes ^= (uint)source[1] << 8; goto case 1; + case 1: remainingBytes ^= source[0]; break; + } + remainingBytes *= c1; + remainingBytes = Helper.RotateLeft(remainingBytes, r1); + remainingBytes *= c2; + hash ^= remainingBytes; + } + } + + protected override byte[] HashFinal() + { + byte[] buffer = new byte[sizeof(uint)]; + TryHashFinal(buffer, out _); + return buffer; + } + + protected override bool TryHashFinal(Span destination, out int bytesWritten) + { + hash ^= (uint)length; + hash ^= hash >> 16; + hash *= 0x85ebca6b; + hash ^= hash >> 13; + hash *= 0xc2b2ae35; + hash ^= hash >> 16; + + bytesWritten = Math.Min(destination.Length, sizeof(uint)); + return BinaryPrimitives.TryWriteUInt32LittleEndian(destination, hash); + } + + public override void Initialize() + { + hash = seed; + length = 0; + } + } +} diff --git a/src/neo/Cryptography/RIPEMD160Managed.cs b/src/Neo/Cryptography/RIPEMD160Managed.cs similarity index 99% rename from src/neo/Cryptography/RIPEMD160Managed.cs rename to src/Neo/Cryptography/RIPEMD160Managed.cs index e48c487006..932c1cfea6 100644 --- a/src/neo/Cryptography/RIPEMD160Managed.cs +++ b/src/Neo/Cryptography/RIPEMD160Managed.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// RIPEMD160Managed.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. diff --git a/src/Neo/Hardfork.cs b/src/Neo/Hardfork.cs new file mode 100644 index 0000000000..9ef6a63c1c --- /dev/null +++ b/src/Neo/Hardfork.cs @@ -0,0 +1,20 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Hardfork.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 +{ + public enum Hardfork : byte + { + HF_Aspidochelone, + HF_Basilisk, + HF_Cockatrice + } +} diff --git a/src/neo/Helper.cs b/src/Neo/Helper.cs similarity index 95% rename from src/neo/Helper.cs rename to src/Neo/Helper.cs index fa47cb6669..00099bc148 100644 --- a/src/neo/Helper.cs +++ b/src/Neo/Helper.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// 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. @@ -288,7 +289,7 @@ public static ulong ToTimestampMS(this DateTime time) } /// - /// Checks if address is IPv4 Maped to IPv6 format, if so, Map to IPv4. + /// Checks if address is IPv4 Mapped to IPv6 format, if so, Map to IPv4. /// Otherwise, return current address. /// internal static IPAddress Unmap(this IPAddress address) @@ -299,7 +300,7 @@ internal static IPAddress Unmap(this IPAddress address) } /// - /// Checks if IPEndPoint is IPv4 Maped to IPv6 format, if so, unmap to IPv4. + /// Checks if IPEndPoint is IPv4 Mapped to IPv6 format, if so, unmap to IPv4. /// Otherwise, return current endpoint. /// internal static IPEndPoint Unmap(this IPEndPoint endPoint) diff --git a/src/Neo/IO/Actors/Idle.cs b/src/Neo/IO/Actors/Idle.cs new file mode 100644 index 0000000000..e722d057ee --- /dev/null +++ b/src/Neo/IO/Actors/Idle.cs @@ -0,0 +1,18 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Idle.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.IO.Actors +{ + internal sealed class Idle + { + public static Idle Instance { get; } = new Idle(); + } +} diff --git a/src/neo/IO/Actors/PriorityMailbox.cs b/src/Neo/IO/Actors/PriorityMailbox.cs similarity index 74% rename from src/neo/IO/Actors/PriorityMailbox.cs rename to src/Neo/IO/Actors/PriorityMailbox.cs index 6dc8f0b429..0b08c04d70 100644 --- a/src/neo/IO/Actors/PriorityMailbox.cs +++ b/src/Neo/IO/Actors/PriorityMailbox.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// PriorityMailbox.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. diff --git a/src/neo/IO/Actors/PriorityMessageQueue.cs b/src/Neo/IO/Actors/PriorityMessageQueue.cs similarity index 86% rename from src/neo/IO/Actors/PriorityMessageQueue.cs rename to src/Neo/IO/Actors/PriorityMessageQueue.cs index e28c6d7261..225720ec97 100644 --- a/src/neo/IO/Actors/PriorityMessageQueue.cs +++ b/src/Neo/IO/Actors/PriorityMessageQueue.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// PriorityMessageQueue.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. diff --git a/src/neo/IO/Caching/Cache.cs b/src/Neo/IO/Caching/Cache.cs similarity index 95% rename from src/neo/IO/Caching/Cache.cs rename to src/Neo/IO/Caching/Cache.cs index 88f96b0dba..5d89dd59b1 100644 --- a/src/neo/IO/Caching/Cache.cs +++ b/src/Neo/IO/Caching/Cache.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Cache.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. diff --git a/src/neo/IO/Caching/ECPointCache.cs b/src/Neo/IO/Caching/ECPointCache.cs similarity index 62% rename from src/neo/IO/Caching/ECPointCache.cs rename to src/Neo/IO/Caching/ECPointCache.cs index eea6deb7a8..a32671d30f 100644 --- a/src/neo/IO/Caching/ECPointCache.cs +++ b/src/Neo/IO/Caching/ECPointCache.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ECPointCache.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. diff --git a/src/neo/IO/Caching/FIFOCache.cs b/src/Neo/IO/Caching/FIFOCache.cs similarity index 62% rename from src/neo/IO/Caching/FIFOCache.cs rename to src/Neo/IO/Caching/FIFOCache.cs index ab41c405e0..af3e5e469d 100644 --- a/src/neo/IO/Caching/FIFOCache.cs +++ b/src/Neo/IO/Caching/FIFOCache.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// FIFOCache.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. diff --git a/src/neo/IO/Caching/HashSetCache.cs b/src/Neo/IO/Caching/HashSetCache.cs similarity index 90% rename from src/neo/IO/Caching/HashSetCache.cs rename to src/Neo/IO/Caching/HashSetCache.cs index 3e6eea90f7..8870e0ecf0 100644 --- a/src/neo/IO/Caching/HashSetCache.cs +++ b/src/Neo/IO/Caching/HashSetCache.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// HashSetCache.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. @@ -18,12 +19,12 @@ class HashSetCache : IReadOnlyCollection where T : IEquatable { /// /// Sets where the Hashes are stored - /// + /// private readonly LinkedList> sets = new(); /// /// Maximum capacity of each bucket inside each HashSet of . - /// + /// private readonly int bucketCapacity; /// diff --git a/src/neo/IO/Caching/IndexedQueue.cs b/src/Neo/IO/Caching/IndexedQueue.cs similarity index 94% rename from src/neo/IO/Caching/IndexedQueue.cs rename to src/Neo/IO/Caching/IndexedQueue.cs index 0cdca447a5..54440a871b 100644 --- a/src/neo/IO/Caching/IndexedQueue.cs +++ b/src/Neo/IO/Caching/IndexedQueue.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// IndexedQueue.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. @@ -107,9 +108,9 @@ public void Enqueue(T item) } /// - /// Provides access to the item at the front of the queue without dequeueing it + /// Provides access to the item at the front of the queue without dequeuing it /// - /// The frontmost item + /// The front most item public T Peek() { if (_count == 0) @@ -201,7 +202,7 @@ public void TrimExcess() } /// - /// Copys the queue's items to a destination array + /// Copy the queue's items to a destination array /// /// The destination array /// The index in the destination to start copying at diff --git a/src/Neo/IO/Caching/KeyedCollectionSlim.cs b/src/Neo/IO/Caching/KeyedCollectionSlim.cs new file mode 100644 index 0000000000..a24dd87e4c --- /dev/null +++ b/src/Neo/IO/Caching/KeyedCollectionSlim.cs @@ -0,0 +1,52 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// KeyedCollectionSlim.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; +using System.Collections.Generic; + +namespace Neo.IO.Caching; + +abstract class KeyedCollectionSlim where TKey : notnull +{ + private readonly LinkedList _items = new(); + private readonly Dictionary> dict = new(); + + public int Count => dict.Count; + public TItem First => _items.First.Value; + + protected abstract TKey GetKeyForItem(TItem item); + + public void Add(TItem item) + { + var key = GetKeyForItem(item); + var node = _items.AddLast(item); + if (!dict.TryAdd(key, node)) + { + _items.RemoveLast(); + throw new ArgumentException("An element with the same key already exists in the collection."); + } + } + + public bool Contains(TKey key) => dict.ContainsKey(key); + + public void Remove(TKey key) + { + if (dict.Remove(key, out var node)) + _items.Remove(node); + } + + public void RemoveFirst() + { + var key = GetKeyForItem(_items.First.Value); + dict.Remove(key); + _items.RemoveFirst(); + } +} diff --git a/src/neo/IO/Caching/ReflectionCache.cs b/src/Neo/IO/Caching/ReflectionCache.cs similarity index 77% rename from src/neo/IO/Caching/ReflectionCache.cs rename to src/Neo/IO/Caching/ReflectionCache.cs index 30ad2b8541..2fd8f5fceb 100644 --- a/src/neo/IO/Caching/ReflectionCache.cs +++ b/src/Neo/IO/Caching/ReflectionCache.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ReflectionCache.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. @@ -43,7 +44,7 @@ public static object CreateInstance(T key, object def = null) return def; } - public static ISerializable CreateSerializable(T key, byte[] data) + public static ISerializable CreateSerializable(T key, ReadOnlyMemory data) { if (dictionary.TryGetValue(key, out Type t)) return data.AsSerializable(t); diff --git a/src/neo/IO/Caching/ReflectionCacheAttribute.cs b/src/Neo/IO/Caching/ReflectionCacheAttribute.cs similarity index 65% rename from src/neo/IO/Caching/ReflectionCacheAttribute.cs rename to src/Neo/IO/Caching/ReflectionCacheAttribute.cs index 4578369321..20aeb91320 100644 --- a/src/neo/IO/Caching/ReflectionCacheAttribute.cs +++ b/src/Neo/IO/Caching/ReflectionCacheAttribute.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ReflectionCacheAttribute.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. diff --git a/src/neo/IO/Caching/RelayCache.cs b/src/Neo/IO/Caching/RelayCache.cs similarity index 61% rename from src/neo/IO/Caching/RelayCache.cs rename to src/Neo/IO/Caching/RelayCache.cs index 5b805f1816..0f617f4d24 100644 --- a/src/neo/IO/Caching/RelayCache.cs +++ b/src/Neo/IO/Caching/RelayCache.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// RelayCache.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. diff --git a/src/neo/IO/Helper.cs b/src/Neo/IO/Helper.cs similarity index 77% rename from src/neo/IO/Helper.cs rename to src/Neo/IO/Helper.cs index 7f27129aaf..2e6d362ee1 100644 --- a/src/neo/IO/Helper.cs +++ b/src/Neo/IO/Helper.cs @@ -1,16 +1,16 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// 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 K4os.Compression.LZ4; using System; -using System.Buffers; using System.Buffers.Binary; using System.Collections.Generic; using System.IO; @@ -34,8 +34,7 @@ public static class Helper /// The converted object. public static T AsSerializable(this byte[] value, int start = 0) where T : ISerializable, new() { - using MemoryStream ms = new(value, start, value.Length - start, false); - using BinaryReader reader = new(ms, Utility.StrictUTF8, true); + MemoryReader reader = new(value.AsMemory(start)); return reader.ReadSerializable(); } @@ -45,15 +44,11 @@ public static class Helper /// The type to convert to. /// The byte array to be converted. /// The converted object. - public static unsafe T AsSerializable(this ReadOnlySpan value) where T : ISerializable, new() + public static T AsSerializable(this ReadOnlyMemory value) where T : ISerializable, new() { if (value.IsEmpty) throw new FormatException(); - fixed (byte* pointer = value) - { - using UnmanagedMemoryStream ms = new(pointer, value.Length); - using BinaryReader reader = new(ms, Utility.StrictUTF8, true); - return reader.ReadSerializable(); - } + MemoryReader reader = new(value); + return reader.ReadSerializable(); } /// @@ -62,14 +57,13 @@ public static class Helper /// The byte array to be converted. /// The type to convert to. /// The converted object. - public static ISerializable AsSerializable(this byte[] value, Type type) + public static ISerializable AsSerializable(this ReadOnlyMemory value, Type type) { if (!typeof(ISerializable).GetTypeInfo().IsAssignableFrom(type)) throw new InvalidCastException(); ISerializable serializable = (ISerializable)Activator.CreateInstance(type); - using MemoryStream ms = new(value, false); - using BinaryReader reader = new(ms, Utility.StrictUTF8, true); - serializable.Deserialize(reader); + MemoryReader reader = new(value); + serializable.Deserialize(ref reader); return serializable; } @@ -82,8 +76,7 @@ public static ISerializable AsSerializable(this byte[] value, Type type) /// The converted array. public static T[] AsSerializableArray(this byte[] value, int max = 0x1000000) where T : ISerializable, new() { - using MemoryStream ms = new(value, false); - using BinaryReader reader = new(ms, Utility.StrictUTF8, true); + MemoryReader reader = new(value); return reader.ReadSerializableArray(max); } @@ -94,15 +87,11 @@ public static ISerializable AsSerializable(this byte[] value, Type type) /// The byte array to be converted. /// The maximum number of elements contained in the converted array. /// The converted array. - public static unsafe T[] AsSerializableArray(this ReadOnlySpan value, int max = 0x1000000) where T : ISerializable, new() + public static T[] AsSerializableArray(this ReadOnlyMemory value, int max = 0x1000000) where T : ISerializable, new() { if (value.IsEmpty) throw new FormatException(); - fixed (byte* pointer = value) - { - using UnmanagedMemoryStream ms = new(pointer, value.Length); - using BinaryReader reader = new(ms, Utility.StrictUTF8, true); - return reader.ReadSerializableArray(max); - } + MemoryReader reader = new(value); + return reader.ReadSerializableArray(max); } /// @@ -110,15 +99,13 @@ public static ISerializable AsSerializable(this byte[] value, Type type) /// /// The data to be compressed. /// The compressed data. - public static byte[] CompressLz4(this byte[] data) + public static ReadOnlyMemory CompressLz4(this ReadOnlySpan data) { int maxLength = LZ4Codec.MaximumOutputSize(data.Length); - using var buffer = MemoryPool.Shared.Rent(maxLength); - int length = LZ4Codec.Encode(data, buffer.Memory.Span); - byte[] result = new byte[sizeof(uint) + length]; - BinaryPrimitives.WriteInt32LittleEndian(result, data.Length); - buffer.Memory[..length].CopyTo(result.AsMemory(4)); - return result; + byte[] buffer = new byte[sizeof(uint) + maxLength]; + BinaryPrimitives.WriteInt32LittleEndian(buffer, data.Length); + int length = LZ4Codec.Encode(data, buffer.AsSpan(sizeof(uint))); + return buffer.AsMemory(0, sizeof(uint) + length); } /// @@ -127,31 +114,16 @@ public static byte[] CompressLz4(this byte[] data) /// The compressed data. /// The maximum data size after decompression. /// The original data. - public static byte[] DecompressLz4(this byte[] data, int maxOutput) + public static byte[] DecompressLz4(this ReadOnlySpan data, int maxOutput) { int length = BinaryPrimitives.ReadInt32LittleEndian(data); if (length < 0 || length > maxOutput) throw new FormatException(); byte[] result = new byte[length]; - if (LZ4Codec.Decode(data.AsSpan(4), result) != length) + if (LZ4Codec.Decode(data[4..], result) != length) throw new FormatException(); return result; } - /// - /// Fills the buffer with the data in the specified . - /// - /// The to be used. - /// The buffer used to store data. - public static void FillBuffer(this BinaryReader reader, Span buffer) - { - while (!buffer.IsEmpty) - { - int count = reader.Read(buffer); - if (count == 0) throw new EndOfStreamException(); - buffer = buffer[count..]; - } - } - /// /// Gets the size of variable-length of the data. /// @@ -202,6 +174,16 @@ public static int GetVarSize(this IReadOnlyCollection value) return GetVarSize(value.Count) + value_size; } + /// + /// Gets the size of the specified array encoded in variable-length encoding. + /// + /// The specified array. + /// The size of the array. + public static int GetVarSize(this ReadOnlyMemory value) + { + return GetVarSize(value.Length) + value.Length; + } + /// /// Gets the size of the specified encoded in variable-length encoding. /// @@ -241,25 +223,13 @@ public static byte[] ReadFixedBytes(this BinaryReader reader, int size) } /// - /// Reads a of the specified length from a . - /// - /// The for reading data. - /// The length of the . - /// The read from the . - public static string ReadFixedString(this BinaryReader reader, int length) - { - byte[] data = reader.ReadFixedBytes(length); - return Utility.StrictUTF8.GetString(data.TakeWhile(p => p != 0).ToArray()); - } - - /// - /// Reads an array from a . + /// Reads an array from a . /// /// The type of the array element. - /// The for reading data. + /// The for reading data. /// The maximum number of elements in the array. - /// The array read from the . - public static T[] ReadNullableArray(this BinaryReader reader, int max = 0x1000000) where T : class, ISerializable, new() + /// The array read from the . + public static T[] ReadNullableArray(this ref MemoryReader reader, int max = 0x1000000) where T : class, ISerializable, new() { T[] array = new T[reader.ReadVarInt((ulong)max)]; for (int i = 0; i < array.Length; i++) @@ -268,32 +238,32 @@ public static string ReadFixedString(this BinaryReader reader, int length) } /// - /// Reads an object from a . + /// Reads an object from a . /// /// The type of the object. - /// The for reading data. - /// The object read from the . - public static T ReadSerializable(this BinaryReader reader) where T : ISerializable, new() + /// The for reading data. + /// The object read from the . + public static T ReadSerializable(this ref MemoryReader reader) where T : ISerializable, new() { T obj = new(); - obj.Deserialize(reader); + obj.Deserialize(ref reader); return obj; } /// - /// Reads an array from a . + /// Reads an array from a . /// /// The type of the array element. - /// The for reading data. + /// The for reading data. /// The maximum number of elements in the array. - /// The array read from the . - public static T[] ReadSerializableArray(this BinaryReader reader, int max = 0x1000000) where T : ISerializable, new() + /// The array read from the . + public static T[] ReadSerializableArray(this ref MemoryReader reader, int max = 0x1000000) where T : ISerializable, new() { T[] array = new T[reader.ReadVarInt((ulong)max)]; for (int i = 0; i < array.Length; i++) { array[i] = new T(); - array[i].Deserialize(reader); + array[i].Deserialize(ref reader); } return array; } @@ -331,17 +301,6 @@ public static ulong ReadVarInt(this BinaryReader reader, ulong max = ulong.MaxVa return value; } - /// - /// Reads a from a . - /// - /// The for reading data. - /// The maximum size of the . - /// The read from the . - public static string ReadVarString(this BinaryReader reader, int max = 0x1000000) - { - return Utility.StrictUTF8.GetString(reader.ReadVarBytes(max)); - } - /// /// Converts an object to a byte array. /// diff --git a/src/neo/Ledger/Blockchain.ApplicationExecuted.cs b/src/Neo/Ledger/Blockchain.ApplicationExecuted.cs similarity index 87% rename from src/neo/Ledger/Blockchain.ApplicationExecuted.cs rename to src/Neo/Ledger/Blockchain.ApplicationExecuted.cs index c069b04c36..211ac14117 100644 --- a/src/neo/Ledger/Blockchain.ApplicationExecuted.cs +++ b/src/Neo/Ledger/Blockchain.ApplicationExecuted.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Blockchain.ApplicationExecuted.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. diff --git a/src/neo/Ledger/Blockchain.cs b/src/Neo/Ledger/Blockchain.cs similarity index 88% rename from src/neo/Ledger/Blockchain.cs rename to src/Neo/Ledger/Blockchain.cs index 4836e7da89..ff9c8e29d8 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/Neo/Ledger/Blockchain.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Blockchain.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. @@ -15,7 +16,6 @@ using Neo.Network.P2P; using Neo.Network.P2P.Payloads; using Neo.Persistence; -using Neo.Plugins; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; @@ -27,6 +27,9 @@ namespace Neo.Ledger { + public delegate void CommittingHandler(NeoSystem system, Block block, DataCache snapshot, IReadOnlyList applicationExecutedList); + public delegate void CommittedHandler(NeoSystem system, Block block); + /// /// Actor used to verify and relay . /// @@ -114,6 +117,9 @@ public class RelayResult internal class Initialize { } private class UnverifiedBlocksList { public LinkedList Blocks = new(); public HashSet Nodes = new(); } + public static event CommittingHandler Committing; + public static event CommittedHandler Committed; + private readonly static Script onPersistScript, postPersistScript; private const int MaxTxToReverifyPerIdle = 10; private readonly NeoSystem system; @@ -201,6 +207,8 @@ private void OnFillMemoryPool(IEnumerable transactions) { if (NativeContract.Ledger.ContainsTransaction(snapshot, tx.Hash)) continue; + if (NativeContract.Ledger.ContainsConflictHash(snapshot, tx.Hash, tx.Signers.Select(s => s.Account), system.Settings.MaxTraceableBlocks)) + continue; // First remove the tx if it is unverified in the pool. system.MemPool.TryRemoveUnVerified(tx.Hash, out _); // Add to the memory pool @@ -331,7 +339,12 @@ private VerifyResult OnNewExtensiblePayload(ExtensiblePayload payload) private VerifyResult OnNewTransaction(Transaction transaction) { - if (system.ContainsTransaction(transaction.Hash)) return VerifyResult.AlreadyExists; + switch (system.ContainsTransaction(transaction.Hash)) + { + case ContainsTransactionType.ExistsInPool: return VerifyResult.AlreadyInPool; + case ContainsTransactionType.ExistsInLedger: return VerifyResult.AlreadyExists; + } + if (system.ContainsConflictHash(transaction.Hash, transaction.Signers.Select(s => s.Account))) return VerifyResult.HasConflicts; return system.MemPool.TryAdd(transaction, system.StoreView); } @@ -384,10 +397,22 @@ protected override void OnReceive(object message) private void OnTransaction(Transaction tx) { - if (system.ContainsTransaction(tx.Hash)) - SendRelayResult(tx, VerifyResult.AlreadyExists); - else - system.TxRouter.Forward(new TransactionRouter.Preverify(tx, true)); + switch (system.ContainsTransaction(tx.Hash)) + { + case ContainsTransactionType.ExistsInPool: + SendRelayResult(tx, VerifyResult.AlreadyInPool); + break; + case ContainsTransactionType.ExistsInLedger: + SendRelayResult(tx, VerifyResult.AlreadyExists); + break; + default: + { + if (system.ContainsConflictHash(tx.Hash, tx.Signers.Select(s => s.Account))) + SendRelayResult(tx, VerifyResult.HasConflicts); + else system.TxRouter.Forward(new TransactionRouter.Preverify(tx, true)); + break; + } + } } private void Persist(Block block) @@ -399,7 +424,12 @@ private void Persist(Block block) using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, block, system.Settings, 0)) { engine.LoadScript(onPersistScript); - if (engine.Execute() != VMState.HALT) throw new InvalidOperationException(); + if (engine.Execute() != VMState.HALT) + { + if (engine.FaultException != null) + throw engine.FaultException; + throw new InvalidOperationException(); + } ApplicationExecuted application_executed = new(engine); Context.System.EventStream.Publish(application_executed); all_application_executed.Add(application_executed); @@ -428,35 +458,21 @@ private void Persist(Block block) using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.PostPersist, null, snapshot, block, system.Settings, 0)) { engine.LoadScript(postPersistScript); - if (engine.Execute() != VMState.HALT) throw new InvalidOperationException(); + if (engine.Execute() != VMState.HALT) + { + if (engine.FaultException != null) + throw engine.FaultException; + throw new InvalidOperationException(); + } ApplicationExecuted application_executed = new(engine); Context.System.EventStream.Publish(application_executed); all_application_executed.Add(application_executed); } - foreach (IPersistencePlugin plugin in Plugin.PersistencePlugins) - plugin.OnPersist(system, block, snapshot, all_application_executed); + Committing?.Invoke(system, block, snapshot, all_application_executed); snapshot.Commit(); - List commitExceptions = null; - foreach (IPersistencePlugin plugin in Plugin.PersistencePlugins) - { - try - { - plugin.OnCommit(system, block, snapshot); - } - catch (Exception ex) - { - if (plugin.ShouldThrowExceptionFromCommit(ex)) - { - if (commitExceptions == null) - commitExceptions = new List(); - - commitExceptions.Add(ex); - } - } - } - if (commitExceptions != null) throw new AggregateException(commitExceptions); - system.MemPool.UpdatePoolForBlockPersisted(block, snapshot); } + Committed?.Invoke(system, block); + system.MemPool.UpdatePoolForBlockPersisted(block, system.StoreView); extensibleWitnessWhiteList = null; block_cache.Remove(block.PrevHash); Context.System.EventStream.Publish(new PersistCompleted { Block = block }); diff --git a/src/neo/Ledger/HeaderCache.cs b/src/Neo/Ledger/HeaderCache.cs similarity index 92% rename from src/neo/Ledger/HeaderCache.cs rename to src/Neo/Ledger/HeaderCache.cs index cdacd3bfa6..ac2208011f 100644 --- a/src/neo/Ledger/HeaderCache.cs +++ b/src/Neo/Ledger/HeaderCache.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// HeaderCache.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. diff --git a/src/neo/Ledger/MemoryPool.cs b/src/Neo/Ledger/MemoryPool.cs similarity index 67% rename from src/neo/Ledger/MemoryPool.cs rename to src/Neo/Ledger/MemoryPool.cs index 426c7f1fd8..23eb711e87 100644 --- a/src/neo/Ledger/MemoryPool.cs +++ b/src/Neo/Ledger/MemoryPool.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// MemoryPool.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. @@ -12,7 +13,6 @@ using Neo.Network.P2P; using Neo.Network.P2P.Payloads; using Neo.Persistence; -using Neo.Plugins; using System; using System.Collections; using System.Collections.Generic; @@ -27,13 +27,16 @@ namespace Neo.Ledger /// public class MemoryPool : IReadOnlyCollection { - // Allow a reverified transaction to be rebroadcasted if it has been this many block times since last broadcast. + public event EventHandler TransactionAdded; + public event EventHandler TransactionRemoved; + + // Allow a reverified transaction to be rebroadcast if it has been this many block times since last broadcast. private const int BlocksTillRebroadcast = 10; private int RebroadcastMultiplierThreshold => Capacity / 10; private readonly double MaxMillisecondsToReverifyTx; - // These two are not expected to be hit, they are just safegaurds. + // These two are not expected to be hit, they are just safeguards. private readonly double MaxMillisecondsToReverifyTxPerIdle; private readonly NeoSystem _system; @@ -53,7 +56,11 @@ public class MemoryPool : IReadOnlyCollection /// private readonly Dictionary _unsortedTransactions = new(); /// - /// Stores the verified sorted transactins currently in the pool. + /// Store transaction hashes that conflict with verified mempooled transactions. + /// + private readonly Dictionary> _conflicts = new(); + /// + /// Stores the verified sorted transactions currently in the pool. /// private readonly SortedSet _sortedTransactions = new(); @@ -273,7 +280,8 @@ private PoolItem GetLowestFeeTransaction(out Dictionary unsor } } - // Note: this must only be called from a single thread (the Blockchain actor) + // Note: this must only be called from a single thread (the Blockchain actor) and + // doesn't take into account conflicting transactions. internal bool CanTransactionFitInPool(Transaction tx) { if (Count < Capacity) return true; @@ -285,38 +293,98 @@ internal VerifyResult TryAdd(Transaction tx, DataCache snapshot) { var poolItem = new PoolItem(tx); - if (_unsortedTransactions.ContainsKey(tx.Hash)) return VerifyResult.AlreadyExists; + if (_unsortedTransactions.ContainsKey(tx.Hash)) return VerifyResult.AlreadyInPool; List removedTransactions = null; _txRwLock.EnterWriteLock(); try { - VerifyResult result = tx.VerifyStateDependent(_system.Settings, snapshot, VerificationContext); + if (!CheckConflicts(tx, out List conflictsToBeRemoved)) return VerifyResult.HasConflicts; + VerifyResult result = tx.VerifyStateDependent(_system.Settings, snapshot, VerificationContext, conflictsToBeRemoved.Select(c => c.Tx)); if (result != VerifyResult.Succeed) return result; _unsortedTransactions.Add(tx.Hash, poolItem); VerificationContext.AddTransaction(tx); _sortedTransactions.Add(poolItem); + foreach (var conflict in conflictsToBeRemoved) + { + if (TryRemoveVerified(conflict.Tx.Hash, out var _)) + VerificationContext.RemoveTransaction(conflict.Tx); + } + removedTransactions = conflictsToBeRemoved.Select(itm => itm.Tx).ToList(); + foreach (var attr in tx.GetAttributes()) + { + if (!_conflicts.TryGetValue(attr.Hash, out var pooled)) + { + pooled = new HashSet(); + } + pooled.Add(tx.Hash); + _conflicts.AddOrSet(attr.Hash, pooled); + } if (Count > Capacity) - removedTransactions = RemoveOverCapacity(); + removedTransactions.AddRange(RemoveOverCapacity()); } finally { _txRwLock.ExitWriteLock(); } - foreach (IMemoryPoolTxObserverPlugin plugin in Plugin.TxObserverPlugins) - { - plugin.TransactionAdded(_system, poolItem.Tx); - if (removedTransactions != null) - plugin.TransactionsRemoved(_system, MemoryPoolTxRemovalReason.CapacityExceeded, removedTransactions); - } + TransactionAdded?.Invoke(this, poolItem.Tx); + if (removedTransactions.Count() > 0) + TransactionRemoved?.Invoke(this, new() + { + Transactions = removedTransactions, + Reason = TransactionRemovalReason.CapacityExceeded + }); if (!_unsortedTransactions.ContainsKey(tx.Hash)) return VerifyResult.OutOfMemory; return VerifyResult.Succeed; } + /// + /// Checks whether there is no mismatch in Conflicts attributes between the current transaction + /// and mempooled unsorted transactions. If true, then these unsorted transactions will be added + /// into conflictsList. + /// + /// The current transaction needs to be checked. + /// The list of conflicting verified transactions that should be removed from the pool if tx fits the pool. + /// True if transaction fits the pool, otherwise false. + private bool CheckConflicts(Transaction tx, out List conflictsList) + { + conflictsList = new(); + long conflictsFeeSum = 0; + // Step 1: check if `tx` was in Conflicts attributes of unsorted transactions. + if (_conflicts.TryGetValue(tx.Hash, out var conflicting)) + { + foreach (var hash in conflicting) + { + var unsortedTx = _unsortedTransactions[hash]; + if (unsortedTx.Tx.Signers.Select(s => s.Account).Contains(tx.Sender)) + conflictsFeeSum += unsortedTx.Tx.NetworkFee; + conflictsList.Add(unsortedTx); + } + } + // Step 2: check if unsorted transactions were in `tx`'s Conflicts attributes. + foreach (var hash in tx.GetAttributes().Select(p => p.Hash)) + { + if (_unsortedTransactions.TryGetValue(hash, out PoolItem unsortedTx)) + { + if (!tx.Signers.Select(p => p.Account).Intersect(unsortedTx.Tx.Signers.Select(p => p.Account)).Any()) return false; + conflictsFeeSum += unsortedTx.Tx.NetworkFee; + conflictsList.Add(unsortedTx); + } + } + // Network fee of tx have to be larger than the sum of conflicting txs network fees. + if (conflictsFeeSum != 0 && conflictsFeeSum >= tx.NetworkFee) + return false; + + // Step 3: take into account sender's conflicting transactions while balance check, + // this will be done in VerifyStateDependant. + + return true; + } + private List RemoveOverCapacity() { List removedTransactions = new(); @@ -329,7 +397,10 @@ private List RemoveOverCapacity() removedTransactions.Add(minItem.Tx); if (ReferenceEquals(sortedPool, _sortedTransactions)) + { + RemoveConflictsOfVerified(minItem); VerificationContext.RemoveTransaction(minItem.Tx); + } } while (Count > Capacity); return removedTransactions; @@ -344,9 +415,27 @@ private bool TryRemoveVerified(UInt256 hash, out PoolItem item) _unsortedTransactions.Remove(hash); _sortedTransactions.Remove(item); + RemoveConflictsOfVerified(item); + return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void RemoveConflictsOfVerified(PoolItem item) + { + foreach (var h in item.Tx.GetAttributes().Select(attr => attr.Hash)) + { + if (_conflicts.TryGetValue(h, out HashSet conflicts)) + { + conflicts.Remove(item.Tx.Hash); + if (conflicts.Count() == 0) + { + _conflicts.Remove(h); + } + } + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool TryRemoveUnVerified(UInt256 hash, out PoolItem item) { @@ -371,28 +460,67 @@ internal void InvalidateVerifiedTransactions() _unsortedTransactions.Clear(); VerificationContext = new TransactionVerificationContext(); _sortedTransactions.Clear(); + _conflicts.Clear(); } // Note: this must only be called from a single thread (the Blockchain actor) internal void UpdatePoolForBlockPersisted(Block block, DataCache snapshot) { + var conflictingItems = new List(); _txRwLock.EnterWriteLock(); try { + Dictionary> conflicts = new Dictionary>(); // First remove the transactions verified in the block. + // No need to modify VerificationContext as it will be reset afterwards. foreach (Transaction tx in block.Transactions) { - if (TryRemoveVerified(tx.Hash, out _)) continue; - TryRemoveUnVerified(tx.Hash, out _); + if (!TryRemoveVerified(tx.Hash, out _)) TryRemoveUnVerified(tx.Hash, out _); + var conflictingSigners = tx.Signers.Select(s => s.Account); + foreach (var h in tx.GetAttributes().Select(a => a.Hash)) + { + if (conflicts.TryGetValue(h, out var signersList)) + { + signersList.AddRange(conflictingSigners); + continue; + } + signersList = conflictingSigners.ToList(); + conflicts.Add(h, signersList); + } } - // Add all the previously verified transactions back to the unverified transactions + // Then remove the transactions conflicting with the accepted ones. + // No need to modify VerificationContext as it will be reset afterwards. + var persisted = block.Transactions.Select(t => t.Hash); + var stale = new List(); + foreach (var item in _sortedTransactions) + { + if ((conflicts.TryGetValue(item.Tx.Hash, out var signersList) && signersList.Intersect(item.Tx.Signers.Select(s => s.Account)).Any()) || item.Tx.GetAttributes().Select(a => a.Hash).Intersect(persisted).Any()) + { + stale.Add(item.Tx.Hash); + conflictingItems.Add(item.Tx); + } + } + foreach (var h in stale) + { + if (!TryRemoveVerified(h, out _)) TryRemoveUnVerified(h, out _); + } + + // Add all the previously verified transactions back to the unverified transactions and clear mempool conflicts list. InvalidateVerifiedTransactions(); } finally { _txRwLock.ExitWriteLock(); } + if (conflictingItems.Count() > 0) + { + TransactionRemoved?.Invoke(this, new() + { + Transactions = conflictingItems.ToArray(), + Reason = TransactionRemovalReason.Conflict, + }); + } // If we know about headers of future blocks, no point in verifying transactions from the unverified tx pool // until we get caught up. @@ -428,10 +556,31 @@ private int ReverifyTransactions(SortedSet verifiedSortedTxPool, // Since unverifiedSortedTxPool is ordered in an ascending manner, we take from the end. foreach (PoolItem item in unverifiedSortedTxPool.Reverse().Take(count)) { - if (item.Tx.VerifyStateDependent(_system.Settings, snapshot, VerificationContext) == VerifyResult.Succeed) + if (CheckConflicts(item.Tx, out List conflictsToBeRemoved) && + item.Tx.VerifyStateDependent(_system.Settings, snapshot, VerificationContext, conflictsToBeRemoved.Select(c => c.Tx)) == VerifyResult.Succeed) { reverifiedItems.Add(item); - VerificationContext.AddTransaction(item.Tx); + if (_unsortedTransactions.TryAdd(item.Tx.Hash, item)) + { + verifiedSortedTxPool.Add(item); + foreach (var attr in item.Tx.GetAttributes()) + { + if (!_conflicts.TryGetValue(attr.Hash, out var pooled)) + { + pooled = new HashSet(); + } + pooled.Add(item.Tx.Hash); + _conflicts.AddOrSet(attr.Hash, pooled); + } + VerificationContext.AddTransaction(item.Tx); + foreach (var conflict in conflictsToBeRemoved) + { + if (TryRemoveVerified(conflict.Tx.Hash, out var _)) + VerificationContext.RemoveTransaction(conflict.Tx); + invalidItems.Add(conflict); + } + + } } else // Transaction no longer valid -- it will be removed from unverifiedTxPool. invalidItems.Add(item); @@ -447,18 +596,14 @@ private int ReverifyTransactions(SortedSet verifiedSortedTxPool, var rebroadcastCutOffTime = TimeProvider.Current.UtcNow.AddMilliseconds(-_system.Settings.MillisecondsPerBlock * blocksTillRebroadcast); foreach (PoolItem item in reverifiedItems) { - if (_unsortedTransactions.TryAdd(item.Tx.Hash, item)) + if (_unsortedTransactions.ContainsKey(item.Tx.Hash)) { - verifiedSortedTxPool.Add(item); - if (item.LastBroadcastTimestamp < rebroadcastCutOffTime) { _system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = item.Tx }, _system.Blockchain); item.LastBroadcastTimestamp = TimeProvider.Current.UtcNow; } } - else - VerificationContext.RemoveTransaction(item.Tx); _unverifiedTransactions.Remove(item.Tx.Hash); unverifiedSortedTxPool.Remove(item); @@ -476,8 +621,11 @@ private int ReverifyTransactions(SortedSet verifiedSortedTxPool, } var invalidTransactions = invalidItems.Select(p => p.Tx).ToArray(); - foreach (IMemoryPoolTxObserverPlugin plugin in Plugin.TxObserverPlugins) - plugin.TransactionsRemoved(_system, MemoryPoolTxRemovalReason.NoLongerValid, invalidTransactions); + TransactionRemoved?.Invoke(this, new() + { + Transactions = invalidTransactions, + Reason = TransactionRemovalReason.NoLongerValid + }); return reverifiedItems.Count; } diff --git a/src/neo/Ledger/PoolItem.cs b/src/Neo/Ledger/PoolItem.cs similarity index 87% rename from src/neo/Ledger/PoolItem.cs rename to src/Neo/Ledger/PoolItem.cs index 37dfb906c5..f8451c1cd9 100644 --- a/src/neo/Ledger/PoolItem.cs +++ b/src/Neo/Ledger/PoolItem.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// PoolItem.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. diff --git a/src/Neo/Ledger/TransactionRemovalReason.cs b/src/Neo/Ledger/TransactionRemovalReason.cs new file mode 100644 index 0000000000..42e11b3f75 --- /dev/null +++ b/src/Neo/Ledger/TransactionRemovalReason.cs @@ -0,0 +1,34 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TransactionRemovalReason.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.Ledger +{ + /// + /// The reason a transaction was removed. + /// + public enum TransactionRemovalReason : byte + { + /// + /// The transaction was rejected since it was the lowest priority transaction and the memory pool capacity was exceeded. + /// + CapacityExceeded, + + /// + /// The transaction was rejected due to failing re-validation after a block was persisted. + /// + NoLongerValid, + + /// + /// The transaction was rejected due to conflict with higher priority transactions with Conflicts attribute. + /// + Conflict, + } +} diff --git a/src/Neo/Ledger/TransactionRemovedEventArgs.cs b/src/Neo/Ledger/TransactionRemovedEventArgs.cs new file mode 100644 index 0000000000..403b24a195 --- /dev/null +++ b/src/Neo/Ledger/TransactionRemovedEventArgs.cs @@ -0,0 +1,31 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TransactionRemovedEventArgs.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.Network.P2P.Payloads; +using System.Collections.Generic; + +namespace Neo.Ledger; + +/// +/// Represents the event data of . +/// +public sealed class TransactionRemovedEventArgs +{ + /// + /// The s that is being removed. + /// + public IReadOnlyCollection Transactions { get; init; } + + /// + /// The reason a transaction was removed. + /// + public TransactionRemovalReason Reason { get; init; } +} diff --git a/src/neo/Ledger/TransactionRouter.cs b/src/Neo/Ledger/TransactionRouter.cs similarity index 78% rename from src/neo/Ledger/TransactionRouter.cs rename to src/Neo/Ledger/TransactionRouter.cs index 2047a629e8..9cc0ed8a9e 100644 --- a/src/neo/Ledger/TransactionRouter.cs +++ b/src/Neo/Ledger/TransactionRouter.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// TransactionRouter.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. diff --git a/src/neo/Ledger/TransactionVerificationContext.cs b/src/Neo/Ledger/TransactionVerificationContext.cs similarity index 74% rename from src/neo/Ledger/TransactionVerificationContext.cs rename to src/Neo/Ledger/TransactionVerificationContext.cs index 6a35467f0e..2300c6da30 100644 --- a/src/neo/Ledger/TransactionVerificationContext.cs +++ b/src/Neo/Ledger/TransactionVerificationContext.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// TransactionVerificationContext.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. @@ -12,6 +13,7 @@ using Neo.Persistence; using Neo.SmartContract.Native; using System.Collections.Generic; +using System.Linq; using System.Numerics; namespace Neo.Ledger @@ -50,15 +52,18 @@ public void AddTransaction(Transaction tx) /// Determine whether the specified conflicts with other transactions. /// /// The specified . + /// The list of that conflicts with the specified one and are to be removed from the pool. /// The snapshot used to verify the . /// if the passes the check; otherwise, . - public bool CheckTransaction(Transaction tx, DataCache snapshot) + public bool CheckTransaction(Transaction tx, IEnumerable conflictingTxs, DataCache snapshot) { BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, tx.Sender); senderFee.TryGetValue(tx.Sender, out var totalSenderFeeFromPool); - BigInteger fee = tx.SystemFee + tx.NetworkFee + totalSenderFeeFromPool; - if (balance < fee) return false; + BigInteger expectedFee = tx.SystemFee + tx.NetworkFee + totalSenderFeeFromPool; + foreach (var conflictTx in conflictingTxs.Where(c => c.Sender.Equals(tx.Sender))) + expectedFee -= (conflictTx.NetworkFee + conflictTx.SystemFee); + if (balance < expectedFee) return false; var oracle = tx.GetAttribute(); if (oracle != null && oracleResponses.ContainsKey(oracle.Id)) diff --git a/src/neo/Ledger/VerifyResult.cs b/src/Neo/Ledger/VerifyResult.cs similarity index 77% rename from src/neo/Ledger/VerifyResult.cs rename to src/Neo/Ledger/VerifyResult.cs index 593aba695a..7a242b4c60 100644 --- a/src/neo/Ledger/VerifyResult.cs +++ b/src/Neo/Ledger/VerifyResult.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// VerifyResult.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. @@ -27,6 +28,11 @@ public enum VerifyResult : byte /// AlreadyExists, + /// + /// Indicates that an with the same hash already exists in the memory pool. + /// + AlreadyInPool, + /// /// Indicates that the is full and the transaction cannot be verified. /// @@ -77,6 +83,11 @@ public enum VerifyResult : byte /// PolicyFail, + /// + /// Indicates that the failed to verify because it conflicts with on-chain or mempooled transactions. + /// + HasConflicts, + /// /// Indicates that the failed to verify due to other reasons. /// diff --git a/src/Neo/Neo.csproj b/src/Neo/Neo.csproj new file mode 100644 index 0000000000..694f4e1c02 --- /dev/null +++ b/src/Neo/Neo.csproj @@ -0,0 +1,32 @@ + + + + netstandard2.1;net7.0 + true + NEO;AntShares;Blockchain;Smart Contract + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/neo/NeoSystem.cs b/src/Neo/NeoSystem.cs similarity index 76% rename from src/neo/NeoSystem.cs rename to src/Neo/NeoSystem.cs index de087f411e..1303770a3f 100644 --- a/src/neo/NeoSystem.cs +++ b/src/Neo/NeoSystem.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// NeoSystem.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. @@ -96,8 +97,8 @@ public class NeoSystem : IDisposable internal RelayCache RelayCache { get; } = new(100); private ImmutableList services = ImmutableList.Empty; - private readonly string storage_engine; private readonly IStore store; + private readonly IStoreProvider storageProvider; private ChannelsConfig start_message = null; private int suspend = 0; @@ -113,14 +114,26 @@ static NeoSystem() /// Initializes a new instance of the class. /// /// The protocol settings of the . - /// The storage engine used to create the objects. If this parameter is , a default in-memory storage engine will be used. - /// The path of the storage. If is the default in-memory storage engine, this parameter is ignored. - public NeoSystem(ProtocolSettings settings, string storageEngine = null, string storagePath = null) + /// The storage engine used to create the objects. If this parameter is , a default in-memory storage engine will be used. + /// The path of the storage. If is the default in-memory storage engine, this parameter is ignored. + public NeoSystem(ProtocolSettings settings, string? storageProvider = null, string? storagePath = null) : + this(settings, StoreFactory.GetStoreProvider(storageProvider ?? nameof(MemoryStore)) + ?? throw new ArgumentException($"Can't find the storage provider {storageProvider}", nameof(storageProvider)), storagePath) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The protocol settings of the . + /// The to use. + /// The path of the storage. If is the default in-memory storage engine, this parameter is ignored. + public NeoSystem(ProtocolSettings settings, IStoreProvider storageProvider, string? storagePath = null) { this.Settings = settings; this.GenesisBlock = CreateGenesisBlock(settings); - this.storage_engine = storageEngine; - this.store = LoadStore(storagePath); + this.storageProvider = storageProvider; + this.store = storageProvider.GetStore(storagePath); this.MemPool = new MemoryPool(this); this.Blockchain = ActorSystem.ActorOf(Ledger.Blockchain.Props(this)); this.LocalNode = ActorSystem.ActorOf(Network.P2P.LocalNode.Props(this)); @@ -163,9 +176,10 @@ private static void CurrentDomain_UnhandledException(object sender, UnhandledExc public void Dispose() { + EnsureStopped(LocalNode); + EnsureStopped(Blockchain); foreach (var p in Plugin.Plugins) p.Dispose(); - EnsureStoped(LocalNode); // Dispose will call ActorSystem.Terminate() ActorSystem.Dispose(); ActorSystem.WhenTerminated.Wait(); @@ -194,15 +208,14 @@ public T GetService(Func filter = null) IEnumerable result = services.OfType(); if (filter is null) return result.FirstOrDefault(); - else - return result.FirstOrDefault(filter); + return result.FirstOrDefault(filter); } /// /// Blocks the current thread until the specified actor has stopped. /// /// The actor to wait. - public void EnsureStoped(IActorRef actor) + public void EnsureStopped(IActorRef actor) { using Inbox inbox = Inbox.Create(ActorSystem); inbox.Watch(actor); @@ -217,9 +230,7 @@ public void EnsureStoped(IActorRef actor) /// The loaded . public IStore LoadStore(string path) { - return string.IsNullOrEmpty(storage_engine) || storage_engine == nameof(MemoryStore) - ? new MemoryStore() - : Plugin.Storages[storage_engine].GetStore(path); + return storageProvider.GetStore(path); } /// @@ -275,10 +286,22 @@ public SnapshotCache GetSnapshot() /// /// The hash of the transaction /// if the transaction exists; otherwise, . - public bool ContainsTransaction(UInt256 hash) + public ContainsTransactionType ContainsTransaction(UInt256 hash) + { + if (MemPool.ContainsKey(hash)) return ContainsTransactionType.ExistsInPool; + return NativeContract.Ledger.ContainsTransaction(StoreView, hash) ? + ContainsTransactionType.ExistsInLedger : ContainsTransactionType.NotExist; + } + + /// + /// Determines whether the specified transaction conflicts with some on-chain transaction. + /// + /// The hash of the transaction + /// The list of signer accounts of the transaction + /// if the transaction conflicts with on-chain transaction; otherwise, . + public bool ContainsConflictHash(UInt256 hash, IEnumerable signers) { - if (MemPool.ContainsKey(hash)) return true; - return NativeContract.Ledger.ContainsTransaction(StoreView, hash); + return NativeContract.Ledger.ContainsConflictHash(StoreView, hash, signers, Settings.MaxTraceableBlocks); } } } diff --git a/src/neo/Network/P2P/Capabilities/FullNodeCapability.cs b/src/Neo/Network/P2P/Capabilities/FullNodeCapability.cs similarity index 74% rename from src/neo/Network/P2P/Capabilities/FullNodeCapability.cs rename to src/Neo/Network/P2P/Capabilities/FullNodeCapability.cs index 7ebdeac75e..52bc765d7c 100644 --- a/src/neo/Network/P2P/Capabilities/FullNodeCapability.cs +++ b/src/Neo/Network/P2P/Capabilities/FullNodeCapability.cs @@ -1,13 +1,15 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// FullNodeCapability.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.IO; using System.IO; namespace Neo.Network.P2P.Capabilities @@ -35,7 +37,7 @@ public FullNodeCapability(uint startHeight = 0) : base(NodeCapabilityType.FullNo StartHeight = startHeight; } - protected override void DeserializeWithoutType(BinaryReader reader) + protected override void DeserializeWithoutType(ref MemoryReader reader) { StartHeight = reader.ReadUInt32(); } diff --git a/src/neo/Network/P2P/Capabilities/NodeCapability.cs b/src/Neo/Network/P2P/Capabilities/NodeCapability.cs similarity index 70% rename from src/neo/Network/P2P/Capabilities/NodeCapability.cs rename to src/Neo/Network/P2P/Capabilities/NodeCapability.cs index d5f32eddb8..18ec61c38d 100644 --- a/src/neo/Network/P2P/Capabilities/NodeCapability.cs +++ b/src/Neo/Network/P2P/Capabilities/NodeCapability.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// NodeCapability.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. @@ -35,39 +36,41 @@ protected NodeCapability(NodeCapabilityType type) this.Type = type; } - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { if (reader.ReadByte() != (byte)Type) { throw new FormatException(); } - DeserializeWithoutType(reader); + DeserializeWithoutType(ref reader); } /// - /// Deserializes an object from a . + /// Deserializes an object from a . /// - /// The for reading data. + /// The for reading data. /// The deserialized . - public static NodeCapability DeserializeFrom(BinaryReader reader) + public static NodeCapability DeserializeFrom(ref MemoryReader reader) { NodeCapabilityType type = (NodeCapabilityType)reader.ReadByte(); NodeCapability capability = type switch { +#pragma warning disable CS0612 // Type or member is obsolete NodeCapabilityType.TcpServer or NodeCapabilityType.WsServer => new ServerCapability(type), +#pragma warning restore CS0612 // Type or member is obsolete NodeCapabilityType.FullNode => new FullNodeCapability(), _ => throw new FormatException(), }; - capability.DeserializeWithoutType(reader); + capability.DeserializeWithoutType(ref reader); return capability; } /// - /// Deserializes the object from a . + /// Deserializes the object from a . /// - /// The for reading data. - protected abstract void DeserializeWithoutType(BinaryReader reader); + /// The for reading data. + protected abstract void DeserializeWithoutType(ref MemoryReader reader); void ISerializable.Serialize(BinaryWriter writer) { diff --git a/src/neo/Network/P2P/Capabilities/NodeCapabilityType.cs b/src/Neo/Network/P2P/Capabilities/NodeCapabilityType.cs similarity index 70% rename from src/neo/Network/P2P/Capabilities/NodeCapabilityType.cs rename to src/Neo/Network/P2P/Capabilities/NodeCapabilityType.cs index 5566183181..419d086fa9 100644 --- a/src/neo/Network/P2P/Capabilities/NodeCapabilityType.cs +++ b/src/Neo/Network/P2P/Capabilities/NodeCapabilityType.cs @@ -1,13 +1,16 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// NodeCapabilityType.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; + namespace Neo.Network.P2P.Capabilities { /// @@ -25,6 +28,7 @@ public enum NodeCapabilityType : byte /// /// Indicates that the node is listening on a WebSocket port. /// + [Obsolete] WsServer = 0x02, #endregion diff --git a/src/neo/Network/P2P/Capabilities/ServerCapability.cs b/src/Neo/Network/P2P/Capabilities/ServerCapability.cs similarity index 73% rename from src/neo/Network/P2P/Capabilities/ServerCapability.cs rename to src/Neo/Network/P2P/Capabilities/ServerCapability.cs index 69ac5b420b..e8c2d110df 100644 --- a/src/neo/Network/P2P/Capabilities/ServerCapability.cs +++ b/src/Neo/Network/P2P/Capabilities/ServerCapability.cs @@ -1,13 +1,15 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ServerCapability.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.IO; using System; using System.IO; @@ -34,7 +36,9 @@ public class ServerCapability : NodeCapability /// The port that the node is listening on. public ServerCapability(NodeCapabilityType type, ushort port = 0) : base(type) { +#pragma warning disable CS0612 // Type or member is obsolete if (type != NodeCapabilityType.TcpServer && type != NodeCapabilityType.WsServer) +#pragma warning restore CS0612 // Type or member is obsolete { throw new ArgumentException(nameof(type)); } @@ -42,7 +46,7 @@ public ServerCapability(NodeCapabilityType type, ushort port = 0) : base(type) Port = port; } - protected override void DeserializeWithoutType(BinaryReader reader) + protected override void DeserializeWithoutType(ref MemoryReader reader) { Port = reader.ReadUInt16(); } diff --git a/src/neo/Network/P2P/ChannelsConfig.cs b/src/Neo/Network/P2P/ChannelsConfig.cs similarity index 70% rename from src/neo/Network/P2P/ChannelsConfig.cs rename to src/Neo/Network/P2P/ChannelsConfig.cs index 628d3ea01e..cc64242066 100644 --- a/src/neo/Network/P2P/ChannelsConfig.cs +++ b/src/Neo/Network/P2P/ChannelsConfig.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ChannelsConfig.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. @@ -22,11 +23,6 @@ public class ChannelsConfig /// public IPEndPoint Tcp { get; set; } - /// - /// Web socket configuration. - /// - public IPEndPoint WebSocket { get; set; } - /// /// Minimum desired connections. /// diff --git a/src/neo/Network/P2P/Connection.cs b/src/Neo/Network/P2P/Connection.cs similarity index 72% rename from src/neo/Network/P2P/Connection.cs rename to src/Neo/Network/P2P/Connection.cs index c0bb001147..53e30a2bde 100644 --- a/src/neo/Network/P2P/Connection.cs +++ b/src/Neo/Network/P2P/Connection.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Connection.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. @@ -12,8 +13,6 @@ using Akka.IO; using System; using System.Net; -using System.Net.WebSockets; -using System.Threading; namespace Neo.Network.P2P { @@ -47,7 +46,6 @@ internal class Close { public bool Abort; } private ICancelable timer; private readonly IActorRef tcp; - private readonly WebSocket ws; private bool disconnected = false; /// @@ -66,33 +64,9 @@ protected Connection(object connection, IPEndPoint remote, IPEndPoint local) case IActorRef tcp: this.tcp = tcp; break; - case WebSocket ws: - this.ws = ws; - WsReceive(); - break; } } - private void WsReceive() - { - byte[] buffer = new byte[512]; - ws.ReceiveAsync(buffer, CancellationToken.None).PipeTo(Self, - success: p => - { - switch (p.MessageType) - { - case WebSocketMessageType.Binary: - return new Tcp.Received(ByteString.FromBytes(buffer, 0, p.Count)); - case WebSocketMessageType.Close: - return Tcp.PeerClosed.Instance; - default: - ws.Abort(); - return Tcp.Aborted.Instance; - } - }, - failure: ex => new Tcp.ErrorClosed(ex.Message)); - } - /// /// Disconnect from the remote node. /// @@ -104,10 +78,6 @@ public void Disconnect(bool abort = false) { tcp.Tell(abort ? Tcp.Abort.Instance : Tcp.Close.Instance); } - else - { - ws.Abort(); - } Context.Stop(Self); } @@ -162,7 +132,6 @@ protected override void PostStop() if (!disconnected) tcp?.Tell(Tcp.Close.Instance); timer.CancelIfNotNull(); - ws?.Dispose(); base.PostStop(); } @@ -176,13 +145,6 @@ protected void SendData(ByteString data) { tcp.Tell(Tcp.Write.Create(data, Ack.Instance)); } - else - { - ArraySegment segment = new(data.ToArray()); - ws.SendAsync(segment, WebSocketMessageType.Binary, true, CancellationToken.None).PipeTo(Self, - success: () => Ack.Instance, - failure: ex => new Tcp.ErrorClosed(ex.Message)); - } } } } diff --git a/src/neo/Network/P2P/Helper.cs b/src/Neo/Network/P2P/Helper.cs similarity index 84% rename from src/neo/Network/P2P/Helper.cs rename to src/Neo/Network/P2P/Helper.cs index c6845a98b4..f171a7b0a3 100644 --- a/src/neo/Network/P2P/Helper.cs +++ b/src/Neo/Network/P2P/Helper.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// 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. diff --git a/src/neo/Network/P2P/LocalNode.cs b/src/Neo/Network/P2P/LocalNode.cs similarity index 93% rename from src/neo/Network/P2P/LocalNode.cs rename to src/Neo/Network/P2P/LocalNode.cs index 0929b59140..f0bce492d5 100644 --- a/src/neo/Network/P2P/LocalNode.cs +++ b/src/Neo/Network/P2P/LocalNode.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// LocalNode.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. @@ -112,7 +113,7 @@ private void BroadcastMessage(MessageCommand command, ISerializable payload = nu /// /// Broadcast a message to all connected nodes. /// - /// The message to be broadcasted. + /// The message to be broadcast. private void BroadcastMessage(Message message) => SendToRemoteNodes(message); /// @@ -160,7 +161,8 @@ internal static IPEndPoint GetIpEndPoint(string hostAndPort) /// /// Checks the new connection. - /// If it is equal to the nonce of local or any remote node, it'll return false, else we'll return true and update the Listener address of the connected remote node. + /// If it is equal to the nonce of local or any remote node, it'll return false, + /// else we'll return true and update the Listener address of the connected remote node. /// /// Remote node actor. /// Remote node object. @@ -200,8 +202,10 @@ public IEnumerable GetUnconnectedPeers() } /// - /// Performs a broadcast with the command , which, eventually, tells all known connections. - /// If there are no connected peers it will try with the default, respecting limit. + /// Performs a broadcast with the command , + /// which, eventually, tells all known connections. + /// If there are no connected peers it will try with the default, + /// respecting limit. /// /// Number of peers that are being requested. protected override void NeedMorePeers(int count) @@ -244,7 +248,8 @@ protected override void OnReceive(object message) private void OnRelayDirectly(IInventory inventory) { var message = new RemoteNode.Relay { Inventory = inventory }; - // When relaying a block, if the block's index is greater than 'LastBlockIndex' of the RemoteNode, relay the block; + // When relaying a block, if the block's index is greater than + // 'LastBlockIndex' of the RemoteNode, relay the block; // otherwise, don't relay. if (inventory is Block block) { diff --git a/src/neo/Network/P2P/Message.cs b/src/Neo/Network/P2P/Message.cs similarity index 83% rename from src/neo/Network/P2P/Message.cs rename to src/Neo/Network/P2P/Message.cs index a36d6e080c..9d3c63a85b 100644 --- a/src/neo/Network/P2P/Message.cs +++ b/src/Neo/Network/P2P/Message.cs @@ -1,15 +1,15 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Message.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 Akka.IO; -using Neo.Cryptography; using Neo.IO; using Neo.IO.Caching; using System; @@ -46,7 +46,7 @@ public class Message : ISerializable /// public ISerializable Payload; - private byte[] _payload_compressed; + private ReadOnlyMemory _payload_compressed; public int Size => sizeof(MessageFlags) + sizeof(MessageCommand) + _payload_compressed.GetVarSize(); @@ -79,7 +79,7 @@ public static Message Create(MessageCommand command, ISerializable payload = nul // Try compression if (tryCompression && message._payload_compressed.Length > CompressionMinSize) { - var compressed = message._payload_compressed.CompressLz4(); + var compressed = message._payload_compressed.Span.CompressLz4(); if (compressed.Length < message._payload_compressed.Length - CompressionThreshold) { message._payload_compressed = compressed; @@ -93,17 +93,17 @@ public static Message Create(MessageCommand command, ISerializable payload = nul private void DecompressPayload() { if (_payload_compressed.Length == 0) return; - byte[] decompressed = Flags.HasFlag(MessageFlags.Compressed) - ? _payload_compressed.DecompressLz4(PayloadMaxSize) + ReadOnlyMemory decompressed = Flags.HasFlag(MessageFlags.Compressed) + ? _payload_compressed.Span.DecompressLz4(PayloadMaxSize) : _payload_compressed; Payload = ReflectionCache.CreateSerializable(Command, decompressed); } - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { Flags = (MessageFlags)reader.ReadByte(); Command = (MessageCommand)reader.ReadByte(); - _payload_compressed = reader.ReadVarBytes(PayloadMaxSize); + _payload_compressed = reader.ReadVarMemory(PayloadMaxSize); DecompressPayload(); } @@ -111,7 +111,7 @@ void ISerializable.Serialize(BinaryWriter writer) { writer.Write((byte)Flags); writer.Write((byte)Command); - writer.WriteVarBytes(_payload_compressed); + writer.WriteVarBytes(_payload_compressed.Span); } internal static int TryDeserialize(ByteString data, out Message msg) @@ -151,7 +151,7 @@ internal static int TryDeserialize(ByteString data, out Message msg) { Flags = flags, Command = (MessageCommand)header[1], - _payload_compressed = length <= 0 ? Array.Empty() : data.Slice(payloadIndex, (int)length).ToArray() + _payload_compressed = length <= 0 ? ReadOnlyMemory.Empty : data.Slice(payloadIndex, (int)length).ToArray() }; msg.DecompressPayload(); diff --git a/src/neo/Network/P2P/MessageCommand.cs b/src/Neo/Network/P2P/MessageCommand.cs similarity index 93% rename from src/neo/Network/P2P/MessageCommand.cs rename to src/Neo/Network/P2P/MessageCommand.cs index 52dcac236a..e0a5444dd5 100644 --- a/src/neo/Network/P2P/MessageCommand.cs +++ b/src/Neo/Network/P2P/MessageCommand.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// MessageCommand.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. diff --git a/src/neo/Network/P2P/MessageFlags.cs b/src/Neo/Network/P2P/MessageFlags.cs similarity index 64% rename from src/neo/Network/P2P/MessageFlags.cs rename to src/Neo/Network/P2P/MessageFlags.cs index 81a8326cb4..69466c9606 100644 --- a/src/neo/Network/P2P/MessageFlags.cs +++ b/src/Neo/Network/P2P/MessageFlags.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// MessageFlags.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. diff --git a/src/neo/Network/P2P/Payloads/AddrPayload.cs b/src/Neo/Network/P2P/Payloads/AddrPayload.cs similarity index 80% rename from src/neo/Network/P2P/Payloads/AddrPayload.cs rename to src/Neo/Network/P2P/Payloads/AddrPayload.cs index 0e030e51d0..ebdceb65ec 100644 --- a/src/neo/Network/P2P/Payloads/AddrPayload.cs +++ b/src/Neo/Network/P2P/Payloads/AddrPayload.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// AddrPayload.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. @@ -44,7 +45,7 @@ public static AddrPayload Create(params NetworkAddressWithTime[] addresses) }; } - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { AddressList = reader.ReadSerializableArray(MaxCountToSend); if (AddressList.Length == 0) diff --git a/src/neo/Network/P2P/Payloads/Block.cs b/src/Neo/Network/P2P/Payloads/Block.cs similarity index 90% rename from src/neo/Network/P2P/Payloads/Block.cs rename to src/Neo/Network/P2P/Payloads/Block.cs index 260efd3baa..3c727b9886 100644 --- a/src/neo/Network/P2P/Payloads/Block.cs +++ b/src/Neo/Network/P2P/Payloads/Block.cs @@ -1,16 +1,17 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Block.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.Cryptography; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Ledger; using Neo.Persistence; using System; @@ -85,7 +86,7 @@ public sealed class Block : IEquatable, IInventory public int Size => Header.Size + Transactions.GetVarSize(); Witness[] IVerifiable.Witnesses { get => ((IVerifiable)Header).Witnesses; set => throw new NotSupportedException(); } - public void Deserialize(BinaryReader reader) + public void Deserialize(ref MemoryReader reader) { Header = reader.ReadSerializable
(); Transactions = reader.ReadSerializableArray(ushort.MaxValue); @@ -95,7 +96,7 @@ public void Deserialize(BinaryReader reader) throw new FormatException(); } - void IVerifiable.DeserializeUnsigned(BinaryReader reader) => throw new NotSupportedException(); + void IVerifiable.DeserializeUnsigned(ref MemoryReader reader) => throw new NotSupportedException(); public bool Equals(Block other) { diff --git a/src/neo/Network/P2P/Payloads/Conditions/AndCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/AndCondition.cs similarity index 51% rename from src/neo/Network/P2P/Payloads/Conditions/AndCondition.cs rename to src/Neo/Network/P2P/Payloads/Conditions/AndCondition.cs index d141443eac..e395b10d5d 100644 --- a/src/neo/Network/P2P/Payloads/Conditions/AndCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/AndCondition.cs @@ -1,16 +1,19 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// AndCondition.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.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; +using Neo.VM; +using Neo.VM.Types; using System; using System.IO; using System.Linq; @@ -30,10 +33,10 @@ public class AndCondition : WitnessCondition public override int Size => base.Size + Expressions.GetVarSize(); public override WitnessConditionType Type => WitnessConditionType.And; - protected override void DeserializeWithoutType(BinaryReader reader, int maxNestDepth) + protected override void DeserializeWithoutType(ref MemoryReader reader, int maxNestDepth) { if (maxNestDepth <= 0) throw new FormatException(); - Expressions = DeserializeConditions(reader, maxNestDepth - 1); + Expressions = DeserializeConditions(ref reader, maxNestDepth - 1); if (Expressions.Length == 0) throw new FormatException(); } @@ -47,9 +50,13 @@ protected override void SerializeWithoutType(BinaryWriter writer) writer.Write(Expressions); } - private protected override void ParseJson(JObject json) + private protected override void ParseJson(JObject json, int maxNestDepth) { - Expressions = json["expressions"].GetArray().Select(FromJson).ToArray(); + if (maxNestDepth <= 0) throw new FormatException(); + JArray expressions = (JArray)json["expressions"]; + if (expressions.Count > MaxSubitems) throw new FormatException(); + Expressions = expressions.Select(p => FromJson((JObject)p, maxNestDepth - 1)).ToArray(); + if (Expressions.Length == 0) throw new FormatException(); } public override JObject ToJson() @@ -58,5 +65,12 @@ public override JObject ToJson() json["expressions"] = Expressions.Select(p => p.ToJson()).ToArray(); return json; } + + public override StackItem ToStackItem(ReferenceCounter referenceCounter) + { + var result = (VM.Types.Array)base.ToStackItem(referenceCounter); + result.Add(new VM.Types.Array(referenceCounter, Expressions.Select(p => p.ToStackItem(referenceCounter)))); + return result; + } } } diff --git a/src/neo/Network/P2P/Payloads/Conditions/BooleanCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/BooleanCondition.cs similarity index 58% rename from src/neo/Network/P2P/Payloads/Conditions/BooleanCondition.cs rename to src/Neo/Network/P2P/Payloads/Conditions/BooleanCondition.cs index 3ae6c336e6..e7609fcbaf 100644 --- a/src/neo/Network/P2P/Payloads/Conditions/BooleanCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/BooleanCondition.cs @@ -1,15 +1,19 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// BooleanCondition.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.IO.Json; +using Neo.IO; +using Neo.Json; using Neo.SmartContract; +using Neo.VM; +using Neo.VM.Types; using System.IO; namespace Neo.Network.P2P.Payloads.Conditions @@ -24,7 +28,7 @@ public class BooleanCondition : WitnessCondition public override int Size => base.Size + sizeof(bool); public override WitnessConditionType Type => WitnessConditionType.Boolean; - protected override void DeserializeWithoutType(BinaryReader reader, int maxNestDepth) + protected override void DeserializeWithoutType(ref MemoryReader reader, int maxNestDepth) { Expression = reader.ReadBoolean(); } @@ -39,7 +43,7 @@ protected override void SerializeWithoutType(BinaryWriter writer) writer.Write(Expression); } - private protected override void ParseJson(JObject json) + private protected override void ParseJson(JObject json, int maxNestDepth) { Expression = json["expression"].GetBoolean(); } @@ -50,5 +54,12 @@ public override JObject ToJson() json["expression"] = Expression; return json; } + + public override StackItem ToStackItem(ReferenceCounter referenceCounter) + { + var result = (VM.Types.Array)base.ToStackItem(referenceCounter); + result.Add(Expression); + return result; + } } } diff --git a/src/neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs similarity index 59% rename from src/neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs rename to src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs index 836aa40117..f853743b72 100644 --- a/src/neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs @@ -1,16 +1,19 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// CalledByContractCondition.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.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; +using Neo.VM; +using Neo.VM.Types; using System.IO; namespace Neo.Network.P2P.Payloads.Conditions @@ -25,7 +28,7 @@ public class CalledByContractCondition : WitnessCondition public override int Size => base.Size + UInt160.Length; public override WitnessConditionType Type => WitnessConditionType.CalledByContract; - protected override void DeserializeWithoutType(BinaryReader reader, int maxNestDepth) + protected override void DeserializeWithoutType(ref MemoryReader reader, int maxNestDepth) { Hash = reader.ReadSerializable(); } @@ -40,7 +43,7 @@ protected override void SerializeWithoutType(BinaryWriter writer) writer.Write(Hash); } - private protected override void ParseJson(JObject json) + private protected override void ParseJson(JObject json, int maxNestDepth) { Hash = UInt160.Parse(json["hash"].GetString()); } @@ -51,5 +54,12 @@ public override JObject ToJson() json["hash"] = Hash.ToString(); return json; } + + public override StackItem ToStackItem(ReferenceCounter referenceCounter) + { + var result = (VM.Types.Array)base.ToStackItem(referenceCounter); + result.Add(Hash.ToArray()); + return result; + } } } diff --git a/src/Neo/Network/P2P/Payloads/Conditions/CalledByEntryCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/CalledByEntryCondition.cs new file mode 100644 index 0000000000..8f8e21f80a --- /dev/null +++ b/src/Neo/Network/P2P/Payloads/Conditions/CalledByEntryCondition.cs @@ -0,0 +1,37 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// CalledByEntryCondition.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.IO; +using Neo.Json; +using Neo.SmartContract; +using System.IO; + +namespace Neo.Network.P2P.Payloads.Conditions +{ + public class CalledByEntryCondition : WitnessCondition + { + public override WitnessConditionType Type => WitnessConditionType.CalledByEntry; + + public override bool Match(ApplicationEngine engine) + { + var state = engine.CurrentContext.GetState(); + if (state.CallingContext is null) return true; + state = state.CallingContext.GetState(); + return state.CallingContext is null; + } + + protected override void DeserializeWithoutType(ref MemoryReader reader, int maxNestDepth) { } + + protected override void SerializeWithoutType(BinaryWriter writer) { } + + private protected override void ParseJson(JObject json, int maxNestDepth) { } + } +} diff --git a/src/neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs similarity index 65% rename from src/neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs rename to src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs index a7e9754566..82dab60fcf 100644 --- a/src/neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs @@ -1,18 +1,21 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// CalledByGroupCondition.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.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.SmartContract.Native; +using Neo.VM; +using Neo.VM.Types; using System.IO; using System.Linq; @@ -28,7 +31,7 @@ public class CalledByGroupCondition : WitnessCondition public override int Size => base.Size + Group.Size; public override WitnessConditionType Type => WitnessConditionType.CalledByGroup; - protected override void DeserializeWithoutType(BinaryReader reader, int maxNestDepth) + protected override void DeserializeWithoutType(ref MemoryReader reader, int maxNestDepth) { Group = reader.ReadSerializable(); } @@ -45,7 +48,7 @@ protected override void SerializeWithoutType(BinaryWriter writer) writer.Write(Group); } - private protected override void ParseJson(JObject json) + private protected override void ParseJson(JObject json, int maxNestDepth) { Group = ECPoint.Parse(json["group"].GetString(), ECCurve.Secp256r1); } @@ -56,5 +59,12 @@ public override JObject ToJson() json["group"] = Group.ToString(); return json; } + + public override StackItem ToStackItem(ReferenceCounter referenceCounter) + { + var result = (VM.Types.Array)base.ToStackItem(referenceCounter); + result.Add(Group.ToArray()); + return result; + } } } diff --git a/src/neo/Network/P2P/Payloads/Conditions/GroupCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs similarity index 65% rename from src/neo/Network/P2P/Payloads/Conditions/GroupCondition.cs rename to src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs index b94773fb9e..ee937aff8b 100644 --- a/src/neo/Network/P2P/Payloads/Conditions/GroupCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs @@ -1,18 +1,21 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// GroupCondition.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.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.SmartContract.Native; +using Neo.VM; +using Neo.VM.Types; using System.IO; using System.Linq; @@ -28,7 +31,7 @@ public class GroupCondition : WitnessCondition public override int Size => base.Size + Group.Size; public override WitnessConditionType Type => WitnessConditionType.Group; - protected override void DeserializeWithoutType(BinaryReader reader, int maxNestDepth) + protected override void DeserializeWithoutType(ref MemoryReader reader, int maxNestDepth) { Group = reader.ReadSerializable(); } @@ -45,7 +48,7 @@ protected override void SerializeWithoutType(BinaryWriter writer) writer.Write(Group); } - private protected override void ParseJson(JObject json) + private protected override void ParseJson(JObject json, int maxNestDepth) { Group = ECPoint.Parse(json["group"].GetString(), ECCurve.Secp256r1); } @@ -56,5 +59,12 @@ public override JObject ToJson() json["group"] = Group.ToString(); return json; } + + public override StackItem ToStackItem(ReferenceCounter referenceCounter) + { + var result = (VM.Types.Array)base.ToStackItem(referenceCounter); + result.Add(Group.ToArray()); + return result; + } } } diff --git a/src/neo/Network/P2P/Payloads/Conditions/NotCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs similarity index 54% rename from src/neo/Network/P2P/Payloads/Conditions/NotCondition.cs rename to src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs index d71c2c03d2..83ecd4d973 100644 --- a/src/neo/Network/P2P/Payloads/Conditions/NotCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs @@ -1,16 +1,19 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// NotCondition.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.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; +using Neo.VM; +using Neo.VM.Types; using System; using System.IO; @@ -29,10 +32,10 @@ public class NotCondition : WitnessCondition public override int Size => base.Size + Expression.Size; public override WitnessConditionType Type => WitnessConditionType.Not; - protected override void DeserializeWithoutType(BinaryReader reader, int maxNestDepth) + protected override void DeserializeWithoutType(ref MemoryReader reader, int maxNestDepth) { if (maxNestDepth <= 0) throw new FormatException(); - Expression = DeserializeFrom(reader, maxNestDepth - 1); + Expression = DeserializeFrom(ref reader, maxNestDepth - 1); } public override bool Match(ApplicationEngine engine) @@ -45,9 +48,10 @@ protected override void SerializeWithoutType(BinaryWriter writer) writer.Write(Expression); } - private protected override void ParseJson(JObject json) + private protected override void ParseJson(JObject json, int maxNestDepth) { - Expression = FromJson(json["expression"]); + if (maxNestDepth <= 0) throw new FormatException(); + Expression = FromJson((JObject)json["expression"], maxNestDepth - 1); } public override JObject ToJson() @@ -56,5 +60,12 @@ public override JObject ToJson() json["expression"] = Expression.ToJson(); return json; } + + public override StackItem ToStackItem(ReferenceCounter referenceCounter) + { + var result = (VM.Types.Array)base.ToStackItem(referenceCounter); + result.Add(Expression.ToStackItem(referenceCounter)); + return result; + } } } diff --git a/src/neo/Network/P2P/Payloads/Conditions/OrCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/OrCondition.cs similarity index 51% rename from src/neo/Network/P2P/Payloads/Conditions/OrCondition.cs rename to src/Neo/Network/P2P/Payloads/Conditions/OrCondition.cs index 7fb150a9b6..b06fc922a8 100644 --- a/src/neo/Network/P2P/Payloads/Conditions/OrCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/OrCondition.cs @@ -1,16 +1,19 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// OrCondition.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.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; +using Neo.VM; +using Neo.VM.Types; using System; using System.IO; using System.Linq; @@ -30,10 +33,10 @@ public class OrCondition : WitnessCondition public override int Size => base.Size + Expressions.GetVarSize(); public override WitnessConditionType Type => WitnessConditionType.Or; - protected override void DeserializeWithoutType(BinaryReader reader, int maxNestDepth) + protected override void DeserializeWithoutType(ref MemoryReader reader, int maxNestDepth) { if (maxNestDepth <= 0) throw new FormatException(); - Expressions = DeserializeConditions(reader, maxNestDepth - 1); + Expressions = DeserializeConditions(ref reader, maxNestDepth - 1); if (Expressions.Length == 0) throw new FormatException(); } @@ -47,9 +50,13 @@ protected override void SerializeWithoutType(BinaryWriter writer) writer.Write(Expressions); } - private protected override void ParseJson(JObject json) + private protected override void ParseJson(JObject json, int maxNestDepth) { - Expressions = json["expressions"].GetArray().Select(FromJson).ToArray(); + if (maxNestDepth <= 0) throw new FormatException(); + JArray expressions = (JArray)json["expressions"]; + if (expressions.Count > MaxSubitems) throw new FormatException(); + Expressions = expressions.Select(p => FromJson((JObject)p, maxNestDepth - 1)).ToArray(); + if (Expressions.Length == 0) throw new FormatException(); } public override JObject ToJson() @@ -58,5 +65,12 @@ public override JObject ToJson() json["expressions"] = Expressions.Select(p => p.ToJson()).ToArray(); return json; } + + public override StackItem ToStackItem(ReferenceCounter referenceCounter) + { + var result = (VM.Types.Array)base.ToStackItem(referenceCounter); + result.Add(new VM.Types.Array(referenceCounter, Expressions.Select(p => p.ToStackItem(referenceCounter)))); + return result; + } } } diff --git a/src/neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs similarity index 59% rename from src/neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs rename to src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs index 6c43c3a10d..9199e1a9a2 100644 --- a/src/neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs @@ -1,16 +1,19 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ScriptHashCondition.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.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; +using Neo.VM; +using Neo.VM.Types; using System.IO; namespace Neo.Network.P2P.Payloads.Conditions @@ -25,7 +28,7 @@ public class ScriptHashCondition : WitnessCondition public override int Size => base.Size + UInt160.Length; public override WitnessConditionType Type => WitnessConditionType.ScriptHash; - protected override void DeserializeWithoutType(BinaryReader reader, int maxNestDepth) + protected override void DeserializeWithoutType(ref MemoryReader reader, int maxNestDepth) { Hash = reader.ReadSerializable(); } @@ -40,7 +43,7 @@ protected override void SerializeWithoutType(BinaryWriter writer) writer.Write(Hash); } - private protected override void ParseJson(JObject json) + private protected override void ParseJson(JObject json, int maxNestDepth) { Hash = UInt160.Parse(json["hash"].GetString()); } @@ -51,5 +54,12 @@ public override JObject ToJson() json["hash"] = Hash.ToString(); return json; } + + public override StackItem ToStackItem(ReferenceCounter referenceCounter) + { + var result = (VM.Types.Array)base.ToStackItem(referenceCounter); + result.Add(Hash.ToArray()); + return result; + } } } diff --git a/src/neo/Network/P2P/Payloads/Conditions/WitnessCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/WitnessCondition.cs similarity index 67% rename from src/neo/Network/P2P/Payloads/Conditions/WitnessCondition.cs rename to src/Neo/Network/P2P/Payloads/Conditions/WitnessCondition.cs index 324d081d0f..7738a6d4a8 100644 --- a/src/neo/Network/P2P/Payloads/Conditions/WitnessCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/WitnessCondition.cs @@ -1,25 +1,28 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// WitnessCondition.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.IO; using Neo.IO.Caching; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; +using Neo.VM; +using Neo.VM.Types; using System; using System.IO; namespace Neo.Network.P2P.Payloads.Conditions { - public abstract class WitnessCondition : ISerializable + public abstract class WitnessCondition : IInteroperable, ISerializable { - private const int MaxSubitems = 16; + internal const int MaxSubitems = 16; internal const int MaxNestingDepth = 2; /// @@ -29,47 +32,47 @@ public abstract class WitnessCondition : ISerializable public virtual int Size => sizeof(WitnessConditionType); - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { if (reader.ReadByte() != (byte)Type) throw new FormatException(); - DeserializeWithoutType(reader, MaxNestingDepth); + DeserializeWithoutType(ref reader, MaxNestingDepth); } /// - /// Deserializes an array from a . + /// Deserializes an array from a . /// - /// The for reading data. + /// The for reading data. /// The maximum nesting depth allowed during deserialization. /// The deserialized array. - protected static WitnessCondition[] DeserializeConditions(BinaryReader reader, int maxNestDepth) + protected static WitnessCondition[] DeserializeConditions(ref MemoryReader reader, int maxNestDepth) { WitnessCondition[] conditions = new WitnessCondition[reader.ReadVarInt(MaxSubitems)]; for (int i = 0; i < conditions.Length; i++) - conditions[i] = DeserializeFrom(reader, maxNestDepth); + conditions[i] = DeserializeFrom(ref reader, maxNestDepth); return conditions; } /// - /// Deserializes an object from a . + /// Deserializes an object from a . /// - /// The for reading data. + /// The for reading data. /// The maximum nesting depth allowed during deserialization. /// The deserialized . - public static WitnessCondition DeserializeFrom(BinaryReader reader, int maxNestDepth) + public static WitnessCondition DeserializeFrom(ref MemoryReader reader, int maxNestDepth) { WitnessConditionType type = (WitnessConditionType)reader.ReadByte(); if (ReflectionCache.CreateInstance(type) is not WitnessCondition condition) throw new FormatException(); - condition.DeserializeWithoutType(reader, maxNestDepth); + condition.DeserializeWithoutType(ref reader, maxNestDepth); return condition; } /// - /// Deserializes the object from a . + /// Deserializes the object from a . /// - /// The for reading data. + /// The for reading data. /// The maximum nesting depth allowed during deserialization. - protected abstract void DeserializeWithoutType(BinaryReader reader, int maxNestDepth); + protected abstract void DeserializeWithoutType(ref MemoryReader reader, int maxNestDepth); /// /// Checks whether the current context matches the condition. @@ -90,21 +93,20 @@ void ISerializable.Serialize(BinaryWriter writer) /// The for writing data. protected abstract void SerializeWithoutType(BinaryWriter writer); - private protected virtual void ParseJson(JObject json) - { - } + private protected abstract void ParseJson(JObject json, int maxNestDepth); /// /// Converts the from a JSON object. /// /// The represented by a JSON object. + /// The maximum nesting depth allowed during deserialization. /// The converted . - public static WitnessCondition FromJson(JObject json) + public static WitnessCondition FromJson(JObject json, int maxNestDepth) { WitnessConditionType type = Enum.Parse(json["type"].GetString()); if (ReflectionCache.CreateInstance(type) is not WitnessCondition condition) throw new FormatException("Invalid WitnessConditionType."); - condition.ParseJson(json); + condition.ParseJson(json, maxNestDepth); return condition; } @@ -119,5 +121,15 @@ public virtual JObject ToJson() ["type"] = Type }; } + + void IInteroperable.FromStackItem(StackItem stackItem) + { + throw new NotSupportedException(); + } + + public virtual StackItem ToStackItem(ReferenceCounter referenceCounter) + { + return new VM.Types.Array(referenceCounter, new StackItem[] { (byte)Type }); + } } } diff --git a/src/neo/Network/P2P/Payloads/Conditions/WitnessConditionType.cs b/src/Neo/Network/P2P/Payloads/Conditions/WitnessConditionType.cs similarity index 87% rename from src/neo/Network/P2P/Payloads/Conditions/WitnessConditionType.cs rename to src/Neo/Network/P2P/Payloads/Conditions/WitnessConditionType.cs index d23c8831bb..e86f7fc343 100644 --- a/src/neo/Network/P2P/Payloads/Conditions/WitnessConditionType.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/WitnessConditionType.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// WitnessConditionType.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. diff --git a/src/Neo/Network/P2P/Payloads/Conflicts.cs b/src/Neo/Network/P2P/Payloads/Conflicts.cs new file mode 100644 index 0000000000..082de2014d --- /dev/null +++ b/src/Neo/Network/P2P/Payloads/Conflicts.cs @@ -0,0 +1,63 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Conflicts.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.IO; +using Neo.Json; +using Neo.Persistence; +using Neo.SmartContract.Native; +using System.IO; + +namespace Neo.Network.P2P.Payloads +{ + public class Conflicts : TransactionAttribute + { + /// + /// Indicates the conflict transaction hash. + /// + public UInt256 Hash; + + public override TransactionAttributeType Type => TransactionAttributeType.Conflicts; + + public override bool AllowMultiple => true; + + public override int Size => base.Size + Hash.Size; + + protected override void DeserializeWithoutType(ref MemoryReader reader) + { + Hash = reader.ReadSerializable(); + } + + protected override void SerializeWithoutType(BinaryWriter writer) + { + writer.Write(Hash); + } + + public override JObject ToJson() + { + JObject json = base.ToJson(); + json["hash"] = Hash.ToString(); + return json; + } + + public override bool Verify(DataCache snapshot, Transaction tx) + { + // Only check if conflicting transaction is on chain. It's OK if the + // conflicting transaction was in the Conflicts attribute of some other + // on-chain transaction. + return !NativeContract.Ledger.ContainsTransaction(snapshot, Hash); + } + + public override long CalculateNetworkFee(DataCache snapshot, Transaction tx) + { + return tx.Signers.Length * base.CalculateNetworkFee(snapshot, tx); + } + } +} diff --git a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs b/src/Neo/Network/P2P/Payloads/ExtensiblePayload.cs similarity index 85% rename from src/neo/Network/P2P/Payloads/ExtensiblePayload.cs rename to src/Neo/Network/P2P/Payloads/ExtensiblePayload.cs index bb52705000..306f6327ab 100644 --- a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs +++ b/src/Neo/Network/P2P/Payloads/ExtensiblePayload.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ExtensiblePayload.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. @@ -46,7 +47,7 @@ public class ExtensiblePayload : IInventory /// /// The data of the payload. /// - public byte[] Data; + public ReadOnlyMemory Data; /// /// The witness of the payload. It must match the . @@ -89,21 +90,21 @@ Witness[] IVerifiable.Witnesses } } - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { - ((IVerifiable)this).DeserializeUnsigned(reader); + ((IVerifiable)this).DeserializeUnsigned(ref reader); if (reader.ReadByte() != 1) throw new FormatException(); Witness = reader.ReadSerializable(); } - void IVerifiable.DeserializeUnsigned(BinaryReader reader) + void IVerifiable.DeserializeUnsigned(ref MemoryReader reader) { Category = reader.ReadVarString(32); ValidBlockStart = reader.ReadUInt32(); ValidBlockEnd = reader.ReadUInt32(); if (ValidBlockStart >= ValidBlockEnd) throw new FormatException(); Sender = reader.ReadSerializable(); - Data = reader.ReadVarBytes(Message.PayloadMaxSize); + Data = reader.ReadVarMemory(); } UInt160[] IVerifiable.GetScriptHashesForVerifying(DataCache snapshot) @@ -123,7 +124,7 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer) writer.Write(ValidBlockStart); writer.Write(ValidBlockEnd); writer.Write(Sender); - writer.WriteVarBytes(Data); + writer.WriteVarBytes(Data.Span); } internal bool Verify(ProtocolSettings settings, DataCache snapshot, ISet extensibleWitnessWhiteList) diff --git a/src/neo/Network/P2P/Payloads/FilterAddPayload.cs b/src/Neo/Network/P2P/Payloads/FilterAddPayload.cs similarity index 54% rename from src/neo/Network/P2P/Payloads/FilterAddPayload.cs rename to src/Neo/Network/P2P/Payloads/FilterAddPayload.cs index e2196422d5..ff4aa1287b 100644 --- a/src/neo/Network/P2P/Payloads/FilterAddPayload.cs +++ b/src/Neo/Network/P2P/Payloads/FilterAddPayload.cs @@ -1,15 +1,17 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// FilterAddPayload.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.Cryptography; using Neo.IO; +using System; using System.IO; namespace Neo.Network.P2P.Payloads @@ -22,18 +24,18 @@ public class FilterAddPayload : ISerializable /// /// The items to be added. /// - public byte[] Data; + public ReadOnlyMemory Data; public int Size => Data.GetVarSize(); - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { - Data = reader.ReadVarBytes(520); + Data = reader.ReadVarMemory(520); } void ISerializable.Serialize(BinaryWriter writer) { - writer.WriteVarBytes(Data); + writer.WriteVarBytes(Data.Span); } } } diff --git a/src/neo/Network/P2P/Payloads/FilterLoadPayload.cs b/src/Neo/Network/P2P/Payloads/FilterLoadPayload.cs similarity index 77% rename from src/neo/Network/P2P/Payloads/FilterLoadPayload.cs rename to src/Neo/Network/P2P/Payloads/FilterLoadPayload.cs index 5dc0ac13f6..2608e0e7b0 100644 --- a/src/neo/Network/P2P/Payloads/FilterLoadPayload.cs +++ b/src/Neo/Network/P2P/Payloads/FilterLoadPayload.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// FilterLoadPayload.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. @@ -23,7 +24,7 @@ public class FilterLoadPayload : ISerializable /// /// The data of the . /// - public byte[] Filter; + public ReadOnlyMemory Filter; /// /// The number of hash functions used by the . @@ -54,9 +55,9 @@ public static FilterLoadPayload Create(BloomFilter filter) }; } - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { - Filter = reader.ReadVarBytes(36000); + Filter = reader.ReadVarMemory(36000); K = reader.ReadByte(); if (K > 50) throw new FormatException(); Tweak = reader.ReadUInt32(); @@ -64,7 +65,7 @@ void ISerializable.Deserialize(BinaryReader reader) void ISerializable.Serialize(BinaryWriter writer) { - writer.WriteVarBytes(Filter); + writer.WriteVarBytes(Filter.Span); writer.Write(K); writer.Write(Tweak); } diff --git a/src/neo/Network/P2P/Payloads/GetBlockByIndexPayload.cs b/src/Neo/Network/P2P/Payloads/GetBlockByIndexPayload.cs similarity index 82% rename from src/neo/Network/P2P/Payloads/GetBlockByIndexPayload.cs rename to src/Neo/Network/P2P/Payloads/GetBlockByIndexPayload.cs index 99a03e01cc..9d8d2b6b6c 100644 --- a/src/neo/Network/P2P/Payloads/GetBlockByIndexPayload.cs +++ b/src/Neo/Network/P2P/Payloads/GetBlockByIndexPayload.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// GetBlockByIndexPayload.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. @@ -46,7 +47,7 @@ public static GetBlockByIndexPayload Create(uint index_start, short count = -1) }; } - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { IndexStart = reader.ReadUInt32(); Count = reader.ReadInt16(); diff --git a/src/neo/Network/P2P/Payloads/GetBlocksPayload.cs b/src/Neo/Network/P2P/Payloads/GetBlocksPayload.cs similarity index 81% rename from src/neo/Network/P2P/Payloads/GetBlocksPayload.cs rename to src/Neo/Network/P2P/Payloads/GetBlocksPayload.cs index 33c7c50443..d12c8ae2cd 100644 --- a/src/neo/Network/P2P/Payloads/GetBlocksPayload.cs +++ b/src/Neo/Network/P2P/Payloads/GetBlocksPayload.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// GetBlocksPayload.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. @@ -46,7 +47,7 @@ public static GetBlocksPayload Create(UInt256 hash_start, short count = -1) }; } - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { HashStart = reader.ReadSerializable(); Count = reader.ReadInt16(); diff --git a/src/neo/Network/P2P/Payloads/Header.cs b/src/Neo/Network/P2P/Payloads/Header.cs similarity index 92% rename from src/neo/Network/P2P/Payloads/Header.cs rename to src/Neo/Network/P2P/Payloads/Header.cs index 13efc5685f..9e1d77fb8a 100644 --- a/src/neo/Network/P2P/Payloads/Header.cs +++ b/src/Neo/Network/P2P/Payloads/Header.cs @@ -1,15 +1,16 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Header.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.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract; @@ -133,7 +134,7 @@ public UInt256 Hash sizeof(uint) + // Index sizeof(byte) + // PrimaryIndex UInt160.Length + // NextConsensus - 1 + Witness.Size; // Witness + 1 + Witness.Size; // Witness Witness[] IVerifiable.Witnesses { @@ -148,15 +149,15 @@ Witness[] IVerifiable.Witnesses } } - public void Deserialize(BinaryReader reader) + public void Deserialize(ref MemoryReader reader) { - ((IVerifiable)this).DeserializeUnsigned(reader); + ((IVerifiable)this).DeserializeUnsigned(ref reader); Witness[] witnesses = reader.ReadSerializableArray(1); if (witnesses.Length != 1) throw new FormatException(); Witness = witnesses[0]; } - void IVerifiable.DeserializeUnsigned(BinaryReader reader) + void IVerifiable.DeserializeUnsigned(ref MemoryReader reader) { _hash = null; version = reader.ReadUInt32(); @@ -242,6 +243,7 @@ internal bool Verify(ProtocolSettings settings, DataCache snapshot) TrimmedBlock prev = NativeContract.Ledger.GetTrimmedBlock(snapshot, prevHash); if (prev is null) return false; if (prev.Index + 1 != index) return false; + if (prev.Hash != prevHash) return false; if (prev.Header.timestamp >= timestamp) return false; if (!this.VerifyWitnesses(settings, snapshot, 3_00000000L)) return false; return true; diff --git a/src/neo/Network/P2P/Payloads/HeadersPayload.cs b/src/Neo/Network/P2P/Payloads/HeadersPayload.cs similarity index 79% rename from src/neo/Network/P2P/Payloads/HeadersPayload.cs rename to src/Neo/Network/P2P/Payloads/HeadersPayload.cs index 1a1df75f8e..152ba32857 100644 --- a/src/neo/Network/P2P/Payloads/HeadersPayload.cs +++ b/src/Neo/Network/P2P/Payloads/HeadersPayload.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// HeadersPayload.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. @@ -44,7 +45,7 @@ public static HeadersPayload Create(params Header[] headers) }; } - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { Headers = reader.ReadSerializableArray
(MaxHeadersCount); if (Headers.Length == 0) throw new FormatException(); diff --git a/src/neo/Network/P2P/Payloads/HighPriorityAttribute.cs b/src/Neo/Network/P2P/Payloads/HighPriorityAttribute.cs similarity index 69% rename from src/neo/Network/P2P/Payloads/HighPriorityAttribute.cs rename to src/Neo/Network/P2P/Payloads/HighPriorityAttribute.cs index 016550d514..cf4d3eb77e 100644 --- a/src/neo/Network/P2P/Payloads/HighPriorityAttribute.cs +++ b/src/Neo/Network/P2P/Payloads/HighPriorityAttribute.cs @@ -1,13 +1,15 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// HighPriorityAttribute.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.IO; using Neo.Persistence; using Neo.SmartContract.Native; using System.IO; @@ -23,7 +25,7 @@ public class HighPriorityAttribute : TransactionAttribute public override bool AllowMultiple => false; public override TransactionAttributeType Type => TransactionAttributeType.HighPriority; - protected override void DeserializeWithoutType(BinaryReader reader) + protected override void DeserializeWithoutType(ref MemoryReader reader) { } diff --git a/src/neo/Network/P2P/Payloads/IInventory.cs b/src/Neo/Network/P2P/Payloads/IInventory.cs similarity index 60% rename from src/neo/Network/P2P/Payloads/IInventory.cs rename to src/Neo/Network/P2P/Payloads/IInventory.cs index a3ed4e35a5..cab68da91e 100644 --- a/src/neo/Network/P2P/Payloads/IInventory.cs +++ b/src/Neo/Network/P2P/Payloads/IInventory.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// IInventory.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. diff --git a/src/neo/Network/P2P/Payloads/IVerifiable.cs b/src/Neo/Network/P2P/Payloads/IVerifiable.cs similarity index 78% rename from src/neo/Network/P2P/Payloads/IVerifiable.cs rename to src/Neo/Network/P2P/Payloads/IVerifiable.cs index 698fde914a..c39661bb61 100644 --- a/src/neo/Network/P2P/Payloads/IVerifiable.cs +++ b/src/Neo/Network/P2P/Payloads/IVerifiable.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// IVerifiable.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. @@ -32,8 +33,8 @@ public interface IVerifiable : ISerializable /// /// Deserializes the part of the object other than . /// - /// The for reading data. - void DeserializeUnsigned(BinaryReader reader); + /// The for reading data. + void DeserializeUnsigned(ref MemoryReader reader); /// /// Gets the script hashes that should be verified for this object. diff --git a/src/neo/Network/P2P/Payloads/InvPayload.cs b/src/Neo/Network/P2P/Payloads/InvPayload.cs similarity index 87% rename from src/neo/Network/P2P/Payloads/InvPayload.cs rename to src/Neo/Network/P2P/Payloads/InvPayload.cs index 61caa094e0..aa4d340d99 100644 --- a/src/neo/Network/P2P/Payloads/InvPayload.cs +++ b/src/Neo/Network/P2P/Payloads/InvPayload.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// InvPayload.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. @@ -72,7 +73,7 @@ public static IEnumerable CreateGroup(InventoryType type, UInt256[] } } - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { Type = (InventoryType)reader.ReadByte(); if (!Enum.IsDefined(typeof(InventoryType), Type)) diff --git a/src/neo/Network/P2P/Payloads/InventoryType.cs b/src/Neo/Network/P2P/Payloads/InventoryType.cs similarity index 71% rename from src/neo/Network/P2P/Payloads/InventoryType.cs rename to src/Neo/Network/P2P/Payloads/InventoryType.cs index f30bf679a6..e2aee0ca6f 100644 --- a/src/neo/Network/P2P/Payloads/InventoryType.cs +++ b/src/Neo/Network/P2P/Payloads/InventoryType.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// InventoryType.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. diff --git a/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs b/src/Neo/Network/P2P/Payloads/MerkleBlockPayload.cs similarity index 81% rename from src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs rename to src/Neo/Network/P2P/Payloads/MerkleBlockPayload.cs index 836a6e82d2..aebb17d5d9 100644 --- a/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs +++ b/src/Neo/Network/P2P/Payloads/MerkleBlockPayload.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// MerkleBlockPayload.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. @@ -40,7 +41,7 @@ public class MerkleBlockPayload : ISerializable /// /// The data in the that filtered the block. /// - public byte[] Flags; + public ReadOnlyMemory Flags; public int Size => Header.Size + sizeof(int) + Hashes.GetVarSize() + Flags.GetVarSize(); @@ -65,12 +66,12 @@ public static MerkleBlockPayload Create(Block block, BitArray flags) }; } - public void Deserialize(BinaryReader reader) + public void Deserialize(ref MemoryReader reader) { Header = reader.ReadSerializable
(); TxCount = (int)reader.ReadVarInt(ushort.MaxValue); Hashes = reader.ReadSerializableArray(TxCount); - Flags = reader.ReadVarBytes((Math.Max(TxCount, 1) + 7) / 8); + Flags = reader.ReadVarMemory((Math.Max(TxCount, 1) + 7) / 8); } public void Serialize(BinaryWriter writer) @@ -78,7 +79,7 @@ public void Serialize(BinaryWriter writer) writer.Write(Header); writer.WriteVarInt(TxCount); writer.Write(Hashes); - writer.WriteVarBytes(Flags); + writer.WriteVarBytes(Flags.Span); } } } diff --git a/src/neo/Network/P2P/Payloads/NetworkAddressWithTime.cs b/src/Neo/Network/P2P/Payloads/NetworkAddressWithTime.cs similarity index 84% rename from src/neo/Network/P2P/Payloads/NetworkAddressWithTime.cs rename to src/Neo/Network/P2P/Payloads/NetworkAddressWithTime.cs index 8d6b2b5a8c..d3cfd15f66 100644 --- a/src/neo/Network/P2P/Payloads/NetworkAddressWithTime.cs +++ b/src/Neo/Network/P2P/Payloads/NetworkAddressWithTime.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// NetworkAddressWithTime.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. @@ -61,18 +62,18 @@ public static NetworkAddressWithTime Create(IPAddress address, uint timestamp, p }; } - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { Timestamp = reader.ReadUInt32(); // Address - byte[] data = reader.ReadFixedBytes(16); - Address = new IPAddress(data).Unmap(); + ReadOnlyMemory data = reader.ReadMemory(16); + Address = new IPAddress(data.Span).Unmap(); // Capabilities Capabilities = new NodeCapability[reader.ReadVarInt(VersionPayload.MaxCapabilities)]; for (int x = 0, max = Capabilities.Length; x < max; x++) - Capabilities[x] = NodeCapability.DeserializeFrom(reader); + Capabilities[x] = NodeCapability.DeserializeFrom(ref reader); if (Capabilities.Select(p => p.Type).Distinct().Count() != Capabilities.Length) throw new FormatException(); } diff --git a/src/Neo/Network/P2P/Payloads/NotValidBefore.cs b/src/Neo/Network/P2P/Payloads/NotValidBefore.cs new file mode 100644 index 0000000000..382a60e8fc --- /dev/null +++ b/src/Neo/Network/P2P/Payloads/NotValidBefore.cs @@ -0,0 +1,57 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// NotValidBefore.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.IO; +using Neo.Json; +using Neo.Persistence; +using Neo.SmartContract.Native; +using System.IO; + +namespace Neo.Network.P2P.Payloads +{ + public class NotValidBefore : TransactionAttribute + { + /// + /// Indicates that the transaction is not valid before this height. + /// + public uint Height; + + public override TransactionAttributeType Type => TransactionAttributeType.NotValidBefore; + + public override bool AllowMultiple => false; + + public override int Size => base.Size + + sizeof(uint); // Height. + + protected override void DeserializeWithoutType(ref MemoryReader reader) + { + Height = reader.ReadUInt32(); + } + + protected override void SerializeWithoutType(BinaryWriter writer) + { + writer.Write(Height); + } + + public override JObject ToJson() + { + JObject json = base.ToJson(); + json["height"] = Height; + return json; + } + + public override bool Verify(DataCache snapshot, Transaction tx) + { + var block_height = NativeContract.Ledger.CurrentIndex(snapshot); + return block_height >= Height; + } + } +} diff --git a/src/neo/Network/P2P/Payloads/OracleResponse.cs b/src/Neo/Network/P2P/Payloads/OracleResponse.cs similarity index 82% rename from src/neo/Network/P2P/Payloads/OracleResponse.cs rename to src/Neo/Network/P2P/Payloads/OracleResponse.cs index 8df290d8f7..2770f556a8 100644 --- a/src/neo/Network/P2P/Payloads/OracleResponse.cs +++ b/src/Neo/Network/P2P/Payloads/OracleResponse.cs @@ -1,15 +1,16 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// OracleResponse.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.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; @@ -48,7 +49,7 @@ public class OracleResponse : TransactionAttribute /// /// The result for the oracle request. /// - public byte[] Result; + public ReadOnlyMemory Result; public override TransactionAttributeType Type => TransactionAttributeType.OracleResponse; public override bool AllowMultiple => false; @@ -65,13 +66,13 @@ static OracleResponse() FixedScript = sb.ToArray(); } - protected override void DeserializeWithoutType(BinaryReader reader) + protected override void DeserializeWithoutType(ref MemoryReader reader) { Id = reader.ReadUInt64(); Code = (OracleResponseCode)reader.ReadByte(); if (!Enum.IsDefined(typeof(OracleResponseCode), Code)) throw new FormatException(); - Result = reader.ReadVarBytes(MaxResultSize); + Result = reader.ReadVarMemory(MaxResultSize); if (Code != OracleResponseCode.Success && Result.Length > 0) throw new FormatException(); } @@ -80,7 +81,7 @@ protected override void SerializeWithoutType(BinaryWriter writer) { writer.Write(Id); writer.Write((byte)Code); - writer.WriteVarBytes(Result); + writer.WriteVarBytes(Result.Span); } public override JObject ToJson() @@ -88,14 +89,14 @@ public override JObject ToJson() JObject json = base.ToJson(); json["id"] = Id; json["code"] = Code; - json["result"] = Convert.ToBase64String(Result); + json["result"] = Convert.ToBase64String(Result.Span); return json; } public override bool Verify(DataCache snapshot, Transaction tx) { if (tx.Signers.Any(p => p.Scopes != WitnessScope.None)) return false; - if (!tx.Script.AsSpan().SequenceEqual(FixedScript)) return false; + if (!tx.Script.Span.SequenceEqual(FixedScript)) return false; OracleRequest request = NativeContract.Oracle.GetRequest(snapshot, Id); if (request is null) return false; if (tx.NetworkFee + tx.SystemFee != request.GasForResponse) return false; diff --git a/src/neo/Network/P2P/Payloads/OracleResponseCode.cs b/src/Neo/Network/P2P/Payloads/OracleResponseCode.cs similarity index 85% rename from src/neo/Network/P2P/Payloads/OracleResponseCode.cs rename to src/Neo/Network/P2P/Payloads/OracleResponseCode.cs index 9540786bcb..e94d9b1d96 100644 --- a/src/neo/Network/P2P/Payloads/OracleResponseCode.cs +++ b/src/Neo/Network/P2P/Payloads/OracleResponseCode.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// OracleResponseCode.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. diff --git a/src/neo/Network/P2P/Payloads/PingPayload.cs b/src/Neo/Network/P2P/Payloads/PingPayload.cs similarity index 80% rename from src/neo/Network/P2P/Payloads/PingPayload.cs rename to src/Neo/Network/P2P/Payloads/PingPayload.cs index 6160abfd5b..f6296a7713 100644 --- a/src/neo/Network/P2P/Payloads/PingPayload.cs +++ b/src/Neo/Network/P2P/Payloads/PingPayload.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// PingPayload.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. @@ -30,7 +31,8 @@ public class PingPayload : ISerializable public uint Timestamp; /// - /// A random number. This number must be the same in and messages. + /// A random number. This number must be the same in + /// and messages. /// public uint Nonce; @@ -66,7 +68,7 @@ public static PingPayload Create(uint height, uint nonce) }; } - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { LastBlockIndex = reader.ReadUInt32(); Timestamp = reader.ReadUInt32(); diff --git a/src/neo/Network/P2P/Payloads/Signer.cs b/src/Neo/Network/P2P/Payloads/Signer.cs similarity index 74% rename from src/neo/Network/P2P/Payloads/Signer.cs rename to src/Neo/Network/P2P/Payloads/Signer.cs index d9a0d422b8..2f96047323 100644 --- a/src/neo/Network/P2P/Payloads/Signer.cs +++ b/src/Neo/Network/P2P/Payloads/Signer.cs @@ -1,17 +1,20 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Signer.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.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Network.P2P.Payloads.Conditions; +using Neo.SmartContract; +using Neo.VM; using System; using System.Collections.Generic; using System.IO; @@ -22,7 +25,7 @@ namespace Neo.Network.P2P.Payloads /// /// Represents a signer of a . /// - public class Signer : ISerializable + public class Signer : IInteroperable, ISerializable { // This limits maximum number of AllowedContracts or AllowedGroups here private const int MaxSubitems = 16; @@ -38,17 +41,20 @@ public class Signer : ISerializable public WitnessScope Scopes; /// - /// The contracts that allowed by the witness. Only available when the flag is set. + /// The contracts that allowed by the witness. + /// Only available when the flag is set. /// public UInt160[] AllowedContracts; /// - /// The groups that allowed by the witness. Only available when the flag is set. + /// The groups that allowed by the witness. + /// Only available when the flag is set. /// public ECPoint[] AllowedGroups; /// - /// The rules that the witness must meet. Only available when the flag is set. + /// The rules that the witness must meet. + /// Only available when the flag is set. /// public WitnessRule[] Rules; @@ -59,7 +65,7 @@ public class Signer : ISerializable /*AllowedGroups*/ (Scopes.HasFlag(WitnessScope.CustomGroups) ? AllowedGroups.GetVarSize() : 0) + /*Rules*/ (Scopes.HasFlag(WitnessScope.WitnessRules) ? Rules.GetVarSize() : 0); - public void Deserialize(BinaryReader reader) + public void Deserialize(ref MemoryReader reader) { Account = reader.ReadSerializable(); Scopes = (WitnessScope)reader.ReadByte(); @@ -79,7 +85,7 @@ public void Deserialize(BinaryReader reader) } /// - /// Converts all rules contianed in the object to . + /// Converts all rules contained in the object to . /// /// The array used to represent the current signer. public IEnumerable GetAllRules() @@ -151,11 +157,11 @@ public static Signer FromJson(JObject json) signer.Account = UInt160.Parse(json["account"].GetString()); signer.Scopes = Enum.Parse(json["scopes"].GetString()); if (signer.Scopes.HasFlag(WitnessScope.CustomContracts)) - signer.AllowedContracts = json["allowedcontracts"].GetArray().Select(p => UInt160.Parse(p.GetString())).ToArray(); + signer.AllowedContracts = ((JArray)json["allowedcontracts"]).Select(p => UInt160.Parse(p.GetString())).ToArray(); if (signer.Scopes.HasFlag(WitnessScope.CustomGroups)) - signer.AllowedGroups = json["allowedgroups"].GetArray().Select(p => ECPoint.Parse(p.GetString(), ECCurve.Secp256r1)).ToArray(); + signer.AllowedGroups = ((JArray)json["allowedgroups"]).Select(p => ECPoint.Parse(p.GetString(), ECCurve.Secp256r1)).ToArray(); if (signer.Scopes.HasFlag(WitnessScope.WitnessRules)) - signer.Rules = json["rules"].GetArray().Select(WitnessRule.FromJson).ToArray(); + signer.Rules = ((JArray)json["rules"]).Select(p => WitnessRule.FromJson((JObject)p)).ToArray(); return signer; } @@ -169,12 +175,29 @@ public JObject ToJson() json["account"] = Account.ToString(); json["scopes"] = Scopes; if (Scopes.HasFlag(WitnessScope.CustomContracts)) - json["allowedcontracts"] = AllowedContracts.Select(p => (JObject)p.ToString()).ToArray(); + json["allowedcontracts"] = AllowedContracts.Select(p => (JToken)p.ToString()).ToArray(); if (Scopes.HasFlag(WitnessScope.CustomGroups)) - json["allowedgroups"] = AllowedGroups.Select(p => (JObject)p.ToString()).ToArray(); + json["allowedgroups"] = AllowedGroups.Select(p => (JToken)p.ToString()).ToArray(); if (Scopes.HasFlag(WitnessScope.WitnessRules)) json["rules"] = Rules.Select(p => p.ToJson()).ToArray(); return json; } + + void IInteroperable.FromStackItem(VM.Types.StackItem stackItem) + { + throw new NotSupportedException(); + } + + VM.Types.StackItem IInteroperable.ToStackItem(ReferenceCounter referenceCounter) + { + return new VM.Types.Array(referenceCounter, new VM.Types.StackItem[] + { + Account.ToArray(), + (byte)Scopes, + new VM.Types.Array(referenceCounter, AllowedContracts.Select(u => new VM.Types.ByteString(u.ToArray()))), + new VM.Types.Array(referenceCounter, AllowedGroups.Select(u => new VM.Types.ByteString(u.ToArray()))), + new VM.Types.Array(referenceCounter, Rules.Select(u => u.ToStackItem(referenceCounter))) + }); + } } } diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/Neo/Network/P2P/Payloads/Transaction.cs similarity index 81% rename from src/neo/Network/P2P/Payloads/Transaction.cs rename to src/Neo/Network/P2P/Payloads/Transaction.cs index e8249fb6e7..16417beb43 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/Neo/Network/P2P/Payloads/Transaction.cs @@ -1,17 +1,18 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Transaction.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.Cryptography; using Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract; @@ -50,7 +51,7 @@ public class Transaction : IEquatable, IInventory, IInteroperable private uint validUntilBlock; private Signer[] _signers; private TransactionAttribute[] attributes; - private byte[] script; + private ReadOnlyMemory script; private Witness[] witnesses; /// @@ -114,7 +115,7 @@ public uint Nonce /// /// The script of the transaction. /// - public byte[] Script + public ReadOnlyMemory Script { get => script; set { script = value; _hash = null; _size = 0; } @@ -185,45 +186,46 @@ public Witness[] Witnesses set { witnesses = value; _size = 0; } } - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { - int startPosition = -1; - if (reader.BaseStream.CanSeek) - startPosition = (int)reader.BaseStream.Position; - DeserializeUnsigned(reader); + int startPosition = reader.Position; + DeserializeUnsigned(ref reader); Witnesses = reader.ReadSerializableArray(Signers.Length); if (Witnesses.Length != Signers.Length) throw new FormatException(); - if (startPosition >= 0) - _size = (int)reader.BaseStream.Position - startPosition; + _size = reader.Position - startPosition; } - private static IEnumerable DeserializeAttributes(BinaryReader reader, int maxCount) + private static TransactionAttribute[] DeserializeAttributes(ref MemoryReader reader, int maxCount) { int count = (int)reader.ReadVarInt((ulong)maxCount); + TransactionAttribute[] attributes = new TransactionAttribute[count]; HashSet hashset = new(); - while (count-- > 0) + for (int i = 0; i < count; i++) { - TransactionAttribute attribute = TransactionAttribute.DeserializeFrom(reader); + TransactionAttribute attribute = TransactionAttribute.DeserializeFrom(ref reader); if (!attribute.AllowMultiple && !hashset.Add(attribute.Type)) throw new FormatException(); - yield return attribute; + attributes[i] = attribute; } + return attributes; } - private static IEnumerable DeserializeSigners(BinaryReader reader, int maxCount) + private static Signer[] DeserializeSigners(ref MemoryReader reader, int maxCount) { int count = (int)reader.ReadVarInt((ulong)maxCount); if (count == 0) throw new FormatException(); + Signer[] signers = new Signer[count]; HashSet hashset = new(); for (int i = 0; i < count; i++) { Signer signer = reader.ReadSerializable(); if (!hashset.Add(signer.Account)) throw new FormatException(); - yield return signer; + signers[i] = signer; } + return signers; } - public void DeserializeUnsigned(BinaryReader reader) + public void DeserializeUnsigned(ref MemoryReader reader) { Version = reader.ReadByte(); if (Version > 0) throw new FormatException(); @@ -234,9 +236,9 @@ public void DeserializeUnsigned(BinaryReader reader) if (NetworkFee < 0) throw new FormatException(); if (SystemFee + NetworkFee < SystemFee) throw new FormatException(); ValidUntilBlock = reader.ReadUInt32(); - Signers = DeserializeSigners(reader, MaxTransactionAttributes).ToArray(); - Attributes = DeserializeAttributes(reader, MaxTransactionAttributes - Signers.Length).ToArray(); - Script = reader.ReadVarBytes(ushort.MaxValue); + Signers = DeserializeSigners(ref reader, MaxTransactionAttributes); + Attributes = DeserializeAttributes(ref reader, MaxTransactionAttributes - Signers.Length); + Script = reader.ReadVarMemory(ushort.MaxValue); if (Script.Length == 0) throw new FormatException(); } @@ -305,7 +307,7 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer) writer.Write(ValidUntilBlock); writer.Write(Signers); writer.Write(Attributes); - writer.WriteVarBytes(Script); + writer.WriteVarBytes(Script.Span); } /// @@ -326,7 +328,7 @@ public JObject ToJson(ProtocolSettings settings) json["validuntilblock"] = ValidUntilBlock; json["signers"] = Signers.Select(p => p.ToJson()).ToArray(); json["attributes"] = Attributes.Select(p => p.ToJson()).ToArray(); - json["script"] = Convert.ToBase64String(Script); + json["script"] = Convert.ToBase64String(Script.Span); json["witnesses"] = Witnesses.Select(p => p.ToJson()).ToArray(); return json; } @@ -337,12 +339,13 @@ public JObject ToJson(ProtocolSettings settings) /// The used to verify the transaction. /// The snapshot used to verify the transaction. /// The used to verify the transaction. + /// The list of conflicting those fee should be excluded from sender's overall fee during -based verification in case of sender's match. /// The result of the verification. - public VerifyResult Verify(ProtocolSettings settings, DataCache snapshot, TransactionVerificationContext context) + public VerifyResult Verify(ProtocolSettings settings, DataCache snapshot, TransactionVerificationContext context, IEnumerable conflictsList) { VerifyResult result = VerifyStateIndependent(settings); if (result != VerifyResult.Succeed) return result; - return VerifyStateDependent(settings, snapshot, context); + return VerifyStateDependent(settings, snapshot, context, conflictsList); } /// @@ -351,8 +354,9 @@ public VerifyResult Verify(ProtocolSettings settings, DataCache snapshot, Transa /// The used to verify the transaction. /// The snapshot used to verify the transaction. /// The used to verify the transaction. + /// The list of conflicting those fee should be excluded from sender's overall fee during -based verification in case of sender's match. /// The result of the verification. - public virtual VerifyResult VerifyStateDependent(ProtocolSettings settings, DataCache snapshot, TransactionVerificationContext context) + public virtual VerifyResult VerifyStateDependent(ProtocolSettings settings, DataCache snapshot, TransactionVerificationContext context, IEnumerable conflictsList) { uint height = NativeContract.Ledger.CurrentIndex(snapshot); if (ValidUntilBlock <= height || ValidUntilBlock > height + settings.MaxValidUntilBlockIncrement) @@ -361,20 +365,23 @@ public virtual VerifyResult VerifyStateDependent(ProtocolSettings settings, Data foreach (UInt160 hash in hashes) if (NativeContract.Policy.IsBlocked(snapshot, hash)) return VerifyResult.PolicyFail; - if (!(context?.CheckTransaction(this, snapshot) ?? true)) return VerifyResult.InsufficientFunds; + if (!(context?.CheckTransaction(this, conflictsList, snapshot) ?? true)) return VerifyResult.InsufficientFunds; + long attributesFee = 0; foreach (TransactionAttribute attribute in Attributes) if (!attribute.Verify(snapshot, this)) return VerifyResult.InvalidAttribute; - long net_fee = NetworkFee - Size * NativeContract.Policy.GetFeePerByte(snapshot); + else + attributesFee += attribute.CalculateNetworkFee(snapshot, this); + long net_fee = NetworkFee - (Size * NativeContract.Policy.GetFeePerByte(snapshot)) - attributesFee; if (net_fee < 0) return VerifyResult.InsufficientFunds; if (net_fee > MaxVerificationGas) net_fee = MaxVerificationGas; uint execFeeFactor = NativeContract.Policy.GetExecFeeFactor(snapshot); for (int i = 0; i < hashes.Length; i++) { - if (witnesses[i].VerificationScript.IsSignatureContract()) + if (IsSignatureContract(witnesses[i].VerificationScript.Span)) net_fee -= execFeeFactor * SignatureContractCost(); - else if (witnesses[i].VerificationScript.IsMultiSigContract(out int m, out int n)) + else if (IsMultiSigContract(witnesses[i].VerificationScript.Span, out int m, out int n)) { net_fee -= execFeeFactor * MultiSignatureContractCost(m, n); } @@ -408,13 +415,13 @@ public virtual VerifyResult VerifyStateIndependent(ProtocolSettings settings) UInt160[] hashes = GetScriptHashesForVerifying(null); for (int i = 0; i < hashes.Length; i++) { - if (witnesses[i].VerificationScript.IsSignatureContract()) + if (IsSignatureContract(witnesses[i].VerificationScript.Span)) { if (hashes[i] != witnesses[i].ScriptHash) return VerifyResult.Invalid; - var pubkey = witnesses[i].VerificationScript.AsSpan(2..35); + var pubkey = witnesses[i].VerificationScript.Span[2..35]; try { - if (!Crypto.VerifySignature(this.GetSignData(settings.Network), witnesses[i].InvocationScript.AsSpan(2), pubkey, ECCurve.Secp256r1)) + if (!Crypto.VerifySignature(this.GetSignData(settings.Network), witnesses[i].InvocationScript.Span[2..], pubkey, ECCurve.Secp256r1)) return VerifyResult.InvalidSignature; } catch @@ -422,7 +429,7 @@ public virtual VerifyResult VerifyStateIndependent(ProtocolSettings settings) return VerifyResult.Invalid; } } - else if (witnesses[i].VerificationScript.IsMultiSigContract(out var m, out ECPoint[] points)) + else if (IsMultiSigContract(witnesses[i].VerificationScript.Span, out var m, out ECPoint[] points)) { if (hashes[i] != witnesses[i].ScriptHash) return VerifyResult.Invalid; var signatures = GetMultiSignatures(witnesses[i].InvocationScript); @@ -433,7 +440,7 @@ public virtual VerifyResult VerifyStateIndependent(ProtocolSettings settings) { for (int x = 0, y = 0; x < m && y < n;) { - if (Crypto.VerifySignature(message, signatures[x], points[y])) + if (Crypto.VerifySignature(message, signatures[x].Span, points[y])) x++; y++; if (m - x > n - y) @@ -451,6 +458,7 @@ public virtual VerifyResult VerifyStateIndependent(ProtocolSettings settings) public StackItem ToStackItem(ReferenceCounter referenceCounter) { + if (_signers == null || _signers.Length == 0) throw new ArgumentException("Sender is not specified in the transaction."); return new Array(referenceCounter, new StackItem[] { // Computed properties @@ -467,15 +475,16 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter) }); } - private static byte[][] GetMultiSignatures(byte[] script) + private static ReadOnlyMemory[] GetMultiSignatures(ReadOnlyMemory script) { + ReadOnlySpan span = script.Span; int i = 0; - var signatures = new List(); + var signatures = new List>(); while (i < script.Length) { - if (script[i++] != (byte)OpCode.PUSHDATA1) return null; + if (span[i++] != (byte)OpCode.PUSHDATA1) return null; if (i + 65 > script.Length) return null; - if (script[i++] != 64) return null; + if (span[i++] != 64) return null; signatures.Add(script[i..(i + 64)]); i += 64; } diff --git a/src/neo/Network/P2P/Payloads/TransactionAttribute.cs b/src/Neo/Network/P2P/Payloads/TransactionAttribute.cs similarity index 73% rename from src/neo/Network/P2P/Payloads/TransactionAttribute.cs rename to src/Neo/Network/P2P/Payloads/TransactionAttribute.cs index 31116c6e87..34af3453fe 100644 --- a/src/neo/Network/P2P/Payloads/TransactionAttribute.cs +++ b/src/Neo/Network/P2P/Payloads/TransactionAttribute.cs @@ -1,17 +1,19 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// TransactionAttribute.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.IO; using Neo.IO.Caching; -using Neo.IO.Json; +using Neo.Json; using Neo.Persistence; +using Neo.SmartContract.Native; using System; using System.IO; @@ -34,32 +36,32 @@ public abstract class TransactionAttribute : ISerializable public virtual int Size => sizeof(TransactionAttributeType); - public void Deserialize(BinaryReader reader) + public void Deserialize(ref MemoryReader reader) { if (reader.ReadByte() != (byte)Type) throw new FormatException(); - DeserializeWithoutType(reader); + DeserializeWithoutType(ref reader); } /// - /// Deserializes an object from a . + /// Deserializes an object from a . /// - /// The for reading data. + /// The for reading data. /// The deserialized attribute. - public static TransactionAttribute DeserializeFrom(BinaryReader reader) + public static TransactionAttribute DeserializeFrom(ref MemoryReader reader) { TransactionAttributeType type = (TransactionAttributeType)reader.ReadByte(); if (ReflectionCache.CreateInstance(type) is not TransactionAttribute attribute) throw new FormatException(); - attribute.DeserializeWithoutType(reader); + attribute.DeserializeWithoutType(ref reader); return attribute; } /// - /// Deserializes the object from a . + /// Deserializes the object from a . /// - /// The for reading data. - protected abstract void DeserializeWithoutType(BinaryReader reader); + /// The for reading data. + protected abstract void DeserializeWithoutType(ref MemoryReader reader); /// /// Converts the attribute to a JSON object. @@ -92,5 +94,7 @@ public void Serialize(BinaryWriter writer) /// The that contains the attribute. /// if the verification passes; otherwise, . public virtual bool Verify(DataCache snapshot, Transaction tx) => true; + + public virtual long CalculateNetworkFee(DataCache snapshot, Transaction tx) => NativeContract.Policy.GetAttributeFee(snapshot, (byte)Type); } } diff --git a/src/Neo/Network/P2P/Payloads/TransactionAttributeType.cs b/src/Neo/Network/P2P/Payloads/TransactionAttributeType.cs new file mode 100644 index 0000000000..116f136c07 --- /dev/null +++ b/src/Neo/Network/P2P/Payloads/TransactionAttributeType.cs @@ -0,0 +1,45 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TransactionAttributeType.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.IO.Caching; + +namespace Neo.Network.P2P.Payloads +{ + /// + /// Represents the type of a . + /// + public enum TransactionAttributeType : byte + { + /// + /// Indicates that the transaction is of high priority. + /// + [ReflectionCache(typeof(HighPriorityAttribute))] + HighPriority = 0x01, + + /// + /// Indicates that the transaction is an oracle response. + /// + [ReflectionCache(typeof(OracleResponse))] + OracleResponse = 0x11, + + /// + /// Indicates that the transaction is not valid before . + /// + [ReflectionCache(typeof(NotValidBefore))] + NotValidBefore = 0x20, + + /// + /// Indicates that the transaction conflicts with . + /// + [ReflectionCache(typeof(Conflicts))] + Conflicts = 0x21 + } +} diff --git a/src/neo/Network/P2P/Payloads/VersionPayload.cs b/src/Neo/Network/P2P/Payloads/VersionPayload.cs similarity index 90% rename from src/neo/Network/P2P/Payloads/VersionPayload.cs rename to src/Neo/Network/P2P/Payloads/VersionPayload.cs index b4115dfaa2..8cec6278e7 100644 --- a/src/neo/Network/P2P/Payloads/VersionPayload.cs +++ b/src/Neo/Network/P2P/Payloads/VersionPayload.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// VersionPayload.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. @@ -85,7 +86,7 @@ public static VersionPayload Create(uint network, uint nonce, string userAgent, }; } - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { Network = reader.ReadUInt32(); Version = reader.ReadUInt32(); @@ -96,7 +97,7 @@ void ISerializable.Deserialize(BinaryReader reader) // Capabilities Capabilities = new NodeCapability[reader.ReadVarInt(MaxCapabilities)]; for (int x = 0, max = Capabilities.Length; x < max; x++) - Capabilities[x] = NodeCapability.DeserializeFrom(reader); + Capabilities[x] = NodeCapability.DeserializeFrom(ref reader); if (Capabilities.Select(p => p.Type).Distinct().Count() != Capabilities.Length) throw new FormatException(); } diff --git a/src/neo/Network/P2P/Payloads/Witness.cs b/src/Neo/Network/P2P/Payloads/Witness.cs similarity index 69% rename from src/neo/Network/P2P/Payloads/Witness.cs rename to src/Neo/Network/P2P/Payloads/Witness.cs index f6cccd628c..34932cc7fb 100644 --- a/src/neo/Network/P2P/Payloads/Witness.cs +++ b/src/Neo/Network/P2P/Payloads/Witness.cs @@ -1,15 +1,16 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Witness.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.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using System; using System.IO; @@ -31,12 +32,12 @@ public class Witness : ISerializable /// /// The invocation script of the witness. Used to pass arguments for . /// - public byte[] InvocationScript; + public ReadOnlyMemory InvocationScript; /// /// The verification script of the witness. It can be empty if the contract is deployed. /// - public byte[] VerificationScript; + public ReadOnlyMemory VerificationScript; private UInt160 _scriptHash; /// @@ -48,7 +49,7 @@ public UInt160 ScriptHash { if (_scriptHash == null) { - _scriptHash = VerificationScript.ToScriptHash(); + _scriptHash = VerificationScript.Span.ToScriptHash(); } return _scriptHash; } @@ -56,16 +57,16 @@ public UInt160 ScriptHash public int Size => InvocationScript.GetVarSize() + VerificationScript.GetVarSize(); - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { - InvocationScript = reader.ReadVarBytes(MaxInvocationScript); - VerificationScript = reader.ReadVarBytes(MaxVerificationScript); + InvocationScript = reader.ReadVarMemory(MaxInvocationScript); + VerificationScript = reader.ReadVarMemory(MaxVerificationScript); } void ISerializable.Serialize(BinaryWriter writer) { - writer.WriteVarBytes(InvocationScript); - writer.WriteVarBytes(VerificationScript); + writer.WriteVarBytes(InvocationScript.Span); + writer.WriteVarBytes(VerificationScript.Span); } /// @@ -75,8 +76,8 @@ void ISerializable.Serialize(BinaryWriter writer) public JObject ToJson() { JObject json = new(); - json["invocation"] = Convert.ToBase64String(InvocationScript); - json["verification"] = Convert.ToBase64String(VerificationScript); + json["invocation"] = Convert.ToBase64String(InvocationScript.Span); + json["verification"] = Convert.ToBase64String(VerificationScript.Span); return json; } } diff --git a/src/neo/Network/P2P/Payloads/WitnessRule.cs b/src/Neo/Network/P2P/Payloads/WitnessRule.cs similarity index 57% rename from src/neo/Network/P2P/Payloads/WitnessRule.cs rename to src/Neo/Network/P2P/Payloads/WitnessRule.cs index acd40d5700..3bac09e7f5 100644 --- a/src/neo/Network/P2P/Payloads/WitnessRule.cs +++ b/src/Neo/Network/P2P/Payloads/WitnessRule.cs @@ -1,16 +1,20 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// WitnessRule.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.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Network.P2P.Payloads.Conditions; +using Neo.SmartContract; +using Neo.VM; +using Neo.VM.Types; using System; using System.IO; @@ -19,7 +23,7 @@ namespace Neo.Network.P2P.Payloads /// /// The rule used to describe the scope of the witness. /// - public class WitnessRule : ISerializable + public class WitnessRule : IInteroperable, ISerializable { /// /// Indicates the action to be taken if the current context meets with the rule. @@ -33,12 +37,12 @@ public class WitnessRule : ISerializable int ISerializable.Size => sizeof(WitnessRuleAction) + Condition.Size; - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { Action = (WitnessRuleAction)reader.ReadByte(); if (Action != WitnessRuleAction.Allow && Action != WitnessRuleAction.Deny) throw new FormatException(); - Condition = WitnessCondition.DeserializeFrom(reader, WitnessCondition.MaxNestingDepth); + Condition = WitnessCondition.DeserializeFrom(ref reader, WitnessCondition.MaxNestingDepth); } void ISerializable.Serialize(BinaryWriter writer) @@ -54,10 +58,15 @@ void ISerializable.Serialize(BinaryWriter writer) /// The converted . public static WitnessRule FromJson(JObject json) { + WitnessRuleAction action = Enum.Parse(json["action"].GetString()); + + if (action != WitnessRuleAction.Allow && action != WitnessRuleAction.Deny) + throw new FormatException(); + return new() { - Action = Enum.Parse(json["action"].GetString()), - Condition = WitnessCondition.FromJson(json["condition"]) + Action = action, + Condition = WitnessCondition.FromJson((JObject)json["condition"], WitnessCondition.MaxNestingDepth) }; } @@ -73,5 +82,19 @@ public JObject ToJson() ["condition"] = Condition.ToJson() }; } + + void IInteroperable.FromStackItem(StackItem stackItem) + { + throw new NotSupportedException(); + } + + public StackItem ToStackItem(ReferenceCounter referenceCounter) + { + return new VM.Types.Array(referenceCounter, new StackItem[] + { + (byte)Action, + Condition.ToStackItem(referenceCounter) + }); + } } } diff --git a/src/neo/Network/P2P/Payloads/WitnessRuleAction.cs b/src/Neo/Network/P2P/Payloads/WitnessRuleAction.cs similarity index 64% rename from src/neo/Network/P2P/Payloads/WitnessRuleAction.cs rename to src/Neo/Network/P2P/Payloads/WitnessRuleAction.cs index 577ea57f75..9b73879e69 100644 --- a/src/neo/Network/P2P/Payloads/WitnessRuleAction.cs +++ b/src/Neo/Network/P2P/Payloads/WitnessRuleAction.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// WitnessRuleAction.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. diff --git a/src/neo/Network/P2P/Payloads/WitnessScope.cs b/src/Neo/Network/P2P/Payloads/WitnessScope.cs similarity index 80% rename from src/neo/Network/P2P/Payloads/WitnessScope.cs rename to src/Neo/Network/P2P/Payloads/WitnessScope.cs index 1362643854..c43dfa0f22 100644 --- a/src/neo/Network/P2P/Payloads/WitnessScope.cs +++ b/src/Neo/Network/P2P/Payloads/WitnessScope.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// WitnessScope.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. @@ -36,7 +37,7 @@ public enum WitnessScope : byte CustomContracts = 0x10, /// - /// Custom pubkey for group members. + /// Custom pubkey for group members. /// CustomGroups = 0x20, diff --git a/src/neo/Network/P2P/Peer.cs b/src/Neo/Network/P2P/Peer.cs similarity index 84% rename from src/neo/Network/P2P/Peer.cs rename to src/Neo/Network/P2P/Peer.cs index 0a1bb36aae..767d747b1d 100644 --- a/src/neo/Network/P2P/Peer.cs +++ b/src/Neo/Network/P2P/Peer.cs @@ -1,18 +1,16 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Peer.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 Akka.Actor; using Akka.IO; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; using Neo.IO; using System; using System.Buffers.Binary; @@ -23,8 +21,6 @@ using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; -using System.Net.WebSockets; -using System.Threading.Tasks; namespace Neo.Network.P2P { @@ -61,7 +57,6 @@ public class Connect } private class Timer { } - private class WsConnected { public WebSocket Socket; public IPEndPoint Remote; public IPEndPoint Local; } /// /// The default minimum number of desired connections. @@ -75,7 +70,6 @@ private class WsConnected { public WebSocket Socket; public IPEndPoint Remote; p private static readonly IActorRef tcp_manager = Context.System.Tcp(); private IActorRef tcp_listener; - private IWebHost ws_host; private ICancelable timer; private static readonly HashSet localAddresses = new(); @@ -108,11 +102,6 @@ private class WsConnected { public WebSocket Socket; public IPEndPoint Remote; p /// public int ListenerTcpPort { get; private set; } - /// - /// The port listened by the local WebSocket server. - /// - public int ListenerWsPort { get; private set; } - /// /// Indicates the maximum number of connections with the same address. /// @@ -220,9 +209,6 @@ protected override void OnReceive(object message) case Connect connect: ConnectToPeer(connect.EndPoint, connect.IsTrusted); break; - case WsConnected ws: - OnWsConnected(ws.Socket, ws.Remote, ws.Local); - break; case Tcp.Connected connected: OnTcpConnected(((IPEndPoint)connected.RemoteAddress).Unmap(), ((IPEndPoint)connected.LocalAddress).Unmap()); break; @@ -241,7 +227,6 @@ protected override void OnReceive(object message) private void OnStart(ChannelsConfig config) { ListenerTcpPort = config.Tcp?.Port ?? 0; - ListenerWsPort = config.WebSocket?.Port ?? 0; MinDesiredConnections = config.MinDesiredConnections; MaxConnections = config.MaxConnections; @@ -249,7 +234,7 @@ private void OnStart(ChannelsConfig config) // schedule time to trigger `OnTimer` event every TimerMillisecondsInterval ms timer = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(0, 5000, Context.Self, new Timer(), ActorRefs.NoSender); - if ((ListenerTcpPort > 0 || ListenerWsPort > 0) + if ((ListenerTcpPort > 0) && localAddresses.All(p => !p.IsIPv4MappedToIPv6 || IsIntranetAddress(p)) && UPnP.Discover()) { @@ -258,7 +243,6 @@ private void OnStart(ChannelsConfig config) localAddresses.Add(UPnP.GetExternalIP()); if (ListenerTcpPort > 0) UPnP.ForwardPort(ListenerTcpPort, ProtocolType.Tcp, "NEO Tcp"); - if (ListenerWsPort > 0) UPnP.ForwardPort(ListenerWsPort, ProtocolType.Tcp, "NEO WebSocket"); } catch { } } @@ -266,19 +250,6 @@ private void OnStart(ChannelsConfig config) { tcp_manager.Tell(new Tcp.Bind(Self, config.Tcp, options: new[] { new Inet.SO.ReuseAddress(true) })); } - if (ListenerWsPort > 0) - { - var host = "*"; - - if (!config.WebSocket.Address.GetAddressBytes().SequenceEqual(IPAddress.Any.GetAddressBytes())) - { - // Is not for all interfaces - host = config.WebSocket.Address.ToString(); - } - - ws_host = new WebHostBuilder().UseKestrel().UseUrls($"http://{host}:{ListenerWsPort}").Configure(app => app.UseWebSockets().Run(ProcessWebSocketAsync)).Build(); - ws_host.Start(); - } } /// @@ -367,40 +338,13 @@ private void OnTimer() } } - private void OnWsConnected(WebSocket ws, IPEndPoint remote, IPEndPoint local) - { - ConnectedAddresses.TryGetValue(remote.Address, out int count); - if (count >= MaxConnectionsPerAddress) - { - ws.Abort(); - } - else - { - ConnectedAddresses[remote.Address] = count + 1; - Context.ActorOf(ProtocolProps(ws, remote, local), $"connection_{Guid.NewGuid()}"); - } - } - protected override void PostStop() { timer.CancelIfNotNull(); - ws_host?.Dispose(); tcp_listener?.Tell(Tcp.Unbind.Instance); base.PostStop(); } - private async Task ProcessWebSocketAsync(HttpContext context) - { - if (!context.WebSockets.IsWebSocketRequest) return; - WebSocket ws = await context.WebSockets.AcceptWebSocketAsync(); - Self.Tell(new WsConnected - { - Socket = ws, - Remote = new IPEndPoint(context.Connection.RemoteIpAddress, context.Connection.RemotePort), - Local = new IPEndPoint(context.Connection.LocalIpAddress, context.Connection.LocalPort) - }); - } - /// /// Gets a object used for creating the protocol actor. /// diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/Neo/Network/P2P/RemoteNode.ProtocolHandler.cs similarity index 93% rename from src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs rename to src/Neo/Network/P2P/RemoteNode.ProtocolHandler.cs index ad73c50456..4606723cd0 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/Neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// RemoteNode.ProtocolHandler.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. @@ -15,21 +16,21 @@ using Neo.Network.P2P.Capabilities; using Neo.Network.P2P.Payloads; using Neo.Persistence; -using Neo.Plugins; using Neo.SmartContract.Native; using System; using System.Collections; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; using System.Net; namespace Neo.Network.P2P { + public delegate bool MessageReceivedHandler(NeoSystem system, Message message); + partial class RemoteNode { private class Timer { } - private class PendingKnownHashesCollection : KeyedCollection + private class PendingKnownHashesCollection : KeyedCollectionSlim { protected override UInt256 GetKeyForItem((UInt256, DateTime) item) { @@ -37,6 +38,13 @@ protected override UInt256 GetKeyForItem((UInt256, DateTime) item) } } + public static event MessageReceivedHandler MessageReceived + { + add => handlers.Add(value); + remove => handlers.Remove(value); + } + + private static readonly List handlers = new(); private readonly PendingKnownHashesCollection pendingKnownHashes = new(); private readonly HashSetCache knownHashes; private readonly HashSetCache sentHashes; @@ -48,8 +56,8 @@ protected override UInt256 GetKeyForItem((UInt256, DateTime) item) private void OnMessage(Message msg) { - foreach (IP2PPlugin plugin in Plugin.P2PPlugins) - if (!plugin.OnP2PMessage(system, msg)) + foreach (MessageReceivedHandler handler in handlers) + if (!handler(system, msg)) return; if (Version == null) { @@ -225,7 +233,8 @@ private void OnGetBlockByIndexMessageReceived(GetBlockByIndexPayload payload) /// /// Will be triggered when a MessageCommand.GetData message is received. /// The payload includes an array of hash values. - /// For different payload.Type (Tx, Block, Consensus), get the corresponding (Txs, Blocks, Consensus) and tell them to RemoteNode actor. + /// For different payload.Type (Tx, Block, Consensus), + /// get the corresponding (Txs, Blocks, Consensus) and tell them to RemoteNode actor. /// /// The payload containing the requested information. private void OnGetDataMessageReceived(InvPayload payload) @@ -304,13 +313,13 @@ private void OnHeadersMessageReceived(HeadersPayload payload) private void OnInventoryReceived(IInventory inventory) { - knownHashes.Add(inventory.Hash); + if (!knownHashes.Add(inventory.Hash)) return; pendingKnownHashes.Remove(inventory.Hash); system.TaskManager.Tell(inventory); switch (inventory) { case Transaction transaction: - if (!system.ContainsTransaction(transaction.Hash)) + if (!(system.ContainsTransaction(transaction.Hash) != ContainsTransactionType.NotExist || system.ContainsConflictHash(transaction.Hash, transaction.Signers.Select(s => s.Account)))) system.TxRouter.Tell(new TransactionRouter.Preverify(transaction, true)); break; case Block block: @@ -403,9 +412,9 @@ private void OnTimer() DateTime oneMinuteAgo = TimeProvider.Current.UtcNow.AddMinutes(-1); while (pendingKnownHashes.Count > 0) { - var (_, time) = pendingKnownHashes[0]; + var (_, time) = pendingKnownHashes.First; if (oneMinuteAgo <= time) break; - pendingKnownHashes.RemoveAt(0); + pendingKnownHashes.RemoveFirst(); } if (oneMinuteAgo > lastSent) EnqueueMessage(Message.Create(MessageCommand.Ping, PingPayload.Create(NativeContract.Ledger.CurrentIndex(system.StoreView)))); diff --git a/src/neo/Network/P2P/RemoteNode.cs b/src/Neo/Network/P2P/RemoteNode.cs similarity index 96% rename from src/neo/Network/P2P/RemoteNode.cs rename to src/Neo/Network/P2P/RemoteNode.cs index e7444433c9..6aa2c742a8 100644 --- a/src/neo/Network/P2P/RemoteNode.cs +++ b/src/Neo/Network/P2P/RemoteNode.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// RemoteNode.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. @@ -208,7 +209,6 @@ private void OnStartProtocol() }; if (localNode.ListenerTcpPort > 0) capabilities.Add(new ServerCapability(NodeCapabilityType.TcpServer, (ushort)localNode.ListenerTcpPort)); - if (localNode.ListenerWsPort > 0) capabilities.Add(new ServerCapability(NodeCapabilityType.WsServer, (ushort)localNode.ListenerWsPort)); SendMessage(Message.Create(MessageCommand.Version, VersionPayload.Create(system.Settings.Network, LocalNode.Nonce, LocalNode.UserAgent, capabilities.ToArray()))); } diff --git a/src/neo/Network/P2P/TaskManager.cs b/src/Neo/Network/P2P/TaskManager.cs similarity index 96% rename from src/neo/Network/P2P/TaskManager.cs rename to src/Neo/Network/P2P/TaskManager.cs index 6825639171..1d24597154 100644 --- a/src/neo/Network/P2P/TaskManager.cs +++ b/src/Neo/Network/P2P/TaskManager.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// TaskManager.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. @@ -51,7 +52,7 @@ private class Timer { } private static readonly TimeSpan TaskTimeout = TimeSpan.FromMinutes(1); private static readonly UInt256 HeaderTaskHash = UInt256.Zero; - private const int MaxConncurrentTasks = 3; + private const int MaxConcurrentTasks = 3; private readonly NeoSystem system; /// @@ -278,7 +279,7 @@ private bool IncrementGlobalTask(UInt256 hash) globalInvTasks[hash] = 1; return true; } - if (value >= MaxConncurrentTasks) + if (value >= MaxConcurrentTasks) return false; globalInvTasks[hash] = value + 1; @@ -293,7 +294,7 @@ private bool IncrementGlobalTask(uint index) globalIndexTasks[index] = 1; return true; } - if (value >= MaxConncurrentTasks) + if (value >= MaxConcurrentTasks) return false; globalIndexTasks[index] = value + 1; @@ -379,9 +380,10 @@ private void RequestTasks(IActorRef remoteNode, TaskSession session) uint currentHeight = Math.Max(NativeContract.Ledger.CurrentIndex(snapshot), lastSeenPersistedIndex); uint headerHeight = system.HeaderCache.Last?.Index ?? currentHeight; - // When the number of AvailableTasks is no more than 0, no pending tasks of InventoryType.Block, it should process pending the tasks of headers + // When the number of AvailableTasks is no more than 0, + // no pending tasks of InventoryType.Block, it should process pending the tasks of headers // If not HeaderTask pending to be processed it should ask for more Blocks - if ((!HasHeaderTask || globalInvTasks[HeaderTaskHash] < MaxConncurrentTasks) && headerHeight < session.LastBlockIndex && !system.HeaderCache.Full) + if ((!HasHeaderTask || globalInvTasks[HeaderTaskHash] < MaxConcurrentTasks) && headerHeight < session.LastBlockIndex && !system.HeaderCache.Full) { session.InvTasks[HeaderTaskHash] = DateTime.UtcNow; IncrementGlobalTask(HeaderTaskHash); diff --git a/src/neo/Network/P2P/TaskSession.cs b/src/Neo/Network/P2P/TaskSession.cs similarity index 79% rename from src/neo/Network/P2P/TaskSession.cs rename to src/Neo/Network/P2P/TaskSession.cs index 33302b0403..a18db540af 100644 --- a/src/neo/Network/P2P/TaskSession.cs +++ b/src/Neo/Network/P2P/TaskSession.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// TaskSession.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. diff --git a/src/neo/Network/UPnP.cs b/src/Neo/Network/UPnP.cs similarity index 94% rename from src/neo/Network/UPnP.cs rename to src/Neo/Network/UPnP.cs index 6b9a8ed89b..a0cd0ce78a 100644 --- a/src/neo/Network/UPnP.cs +++ b/src/Neo/Network/UPnP.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// UPnP.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. @@ -182,8 +183,8 @@ private static XmlDocument SOAPRequest(string url, string soap, string function) request.Headers.Add("Content-Type", "text/xml; charset=\"utf-8\""); request.Content = new StringContent(req); using HttpClient http = new(); - using HttpResponseMessage response = http.Send(request); - using Stream stream = response.EnsureSuccessStatusCode().Content.ReadAsStream(); + using HttpResponseMessage response = http.SendAsync(request).GetAwaiter().GetResult(); + using Stream stream = response.EnsureSuccessStatusCode().Content.ReadAsStreamAsync().GetAwaiter().GetResult(); XmlDocument resp = new() { XmlResolver = null }; resp.Load(stream); return resp; diff --git a/src/neo/Persistence/ClonedCache.cs b/src/Neo/Persistence/ClonedCache.cs similarity index 83% rename from src/neo/Persistence/ClonedCache.cs rename to src/Neo/Persistence/ClonedCache.cs index ed25a957e4..e2629e385d 100644 --- a/src/neo/Persistence/ClonedCache.cs +++ b/src/Neo/Persistence/ClonedCache.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ClonedCache.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. diff --git a/src/neo/Persistence/DataCache.cs b/src/Neo/Persistence/DataCache.cs similarity index 81% rename from src/neo/Persistence/DataCache.cs rename to src/Neo/Persistence/DataCache.cs index 145b455299..86fbd69961 100644 --- a/src/neo/Persistence/DataCache.cs +++ b/src/Neo/Persistence/DataCache.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// DataCache.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. @@ -59,7 +60,7 @@ public StorageItem this[StorageKey key] { if (dictionary.TryGetValue(key, out Trackable trackable)) { - if (trackable.State == TrackState.Deleted) + if (trackable.State == TrackState.Deleted || trackable.State == TrackState.NotFound) throw new KeyNotFoundException(); } else @@ -88,14 +89,25 @@ public void Add(StorageKey key, StorageItem value) { lock (dictionary) { - if (dictionary.TryGetValue(key, out Trackable trackable) && trackable.State != TrackState.Deleted) - throw new ArgumentException(); - dictionary[key] = new Trackable + if (dictionary.TryGetValue(key, out Trackable trackable)) { - Key = key, - Item = value, - State = trackable == null ? TrackState.Added : TrackState.Changed - }; + trackable.Item = value; + trackable.State = trackable.State switch + { + TrackState.Deleted => TrackState.Changed, + TrackState.NotFound => TrackState.Added, + _ => throw new ArgumentException($"The element currently has state {trackable.State}") + }; + } + else + { + dictionary[key] = new Trackable + { + Key = key, + Item = value, + State = TrackState.Added + }; + } changeSet.Add(key); } } @@ -157,10 +169,10 @@ public void Delete(StorageKey key) { if (trackable.State == TrackState.Added) { - dictionary.Remove(key); + trackable.State = TrackState.NotFound; changeSet.Remove(key); } - else + else if (trackable.State != TrackState.NotFound) { trackable.State = TrackState.Deleted; changeSet.Add(key); @@ -191,13 +203,42 @@ public void Delete(StorageKey key) /// Finds the entries starting with the specified prefix. /// /// The prefix of the key. + /// The search direction. /// The entries found with the desired prefix. - public IEnumerable<(StorageKey Key, StorageItem Value)> Find(byte[] key_prefix = null) + public IEnumerable<(StorageKey Key, StorageItem Value)> Find(byte[] key_prefix = null, SeekDirection direction = SeekDirection.Forward) + { + var seek_prefix = key_prefix; + if (direction == SeekDirection.Backward) + { + if (key_prefix == null || key_prefix.Length == 0) + { // Backwards seek for zero prefix is not supported for now. + throw new ArgumentException(); + } + seek_prefix = null; + for (int i = key_prefix.Length - 1; i >= 0; i--) + { + if (key_prefix[i] < 0xff) + { + seek_prefix = key_prefix.Take(i + 1).ToArray(); + // The next key after the key_prefix. + seek_prefix[i]++; + break; + } + } + if (seek_prefix == null) + { + throw new ArgumentException(); + } + } + return FindInternal(key_prefix, seek_prefix, direction); + } + + private IEnumerable<(StorageKey Key, StorageItem Value)> FindInternal(byte[] key_prefix, byte[] seek_prefix, SeekDirection direction) { - foreach (var (key, value) in Seek(key_prefix, SeekDirection.Forward)) + foreach (var (key, value) in Seek(seek_prefix, direction)) if (key.ToArray().AsSpan().StartsWith(key_prefix)) yield return (key, value); - else + else if (direction == SeekDirection.Forward || !key.ToArray().SequenceEqual(seek_prefix)) yield break; } @@ -243,10 +284,7 @@ public bool Contains(StorageKey key) lock (dictionary) { if (dictionary.TryGetValue(key, out Trackable trackable)) - { - if (trackable.State == TrackState.Deleted) return false; - return true; - } + return trackable.State != TrackState.Deleted && trackable.State != TrackState.NotFound; return ContainsInternal(key); } } @@ -277,11 +315,19 @@ public StorageItem GetAndChange(StorageKey key, Func factory = null { if (dictionary.TryGetValue(key, out Trackable trackable)) { - if (trackable.State == TrackState.Deleted) + if (trackable.State == TrackState.Deleted || trackable.State == TrackState.NotFound) { if (factory == null) return null; trackable.Item = factory(); - trackable.State = TrackState.Changed; + if (trackable.State == TrackState.Deleted) + { + trackable.State = TrackState.Changed; + } + else + { + trackable.State = TrackState.Added; + changeSet.Add(key); + } } else if (trackable.State == TrackState.None) { @@ -325,10 +371,18 @@ public StorageItem GetOrAdd(StorageKey key, Func factory) { if (dictionary.TryGetValue(key, out Trackable trackable)) { - if (trackable.State == TrackState.Deleted) + if (trackable.State == TrackState.Deleted || trackable.State == TrackState.NotFound) { trackable.Item = factory(); - trackable.State = TrackState.Changed; + if (trackable.State == TrackState.Deleted) + { + trackable.State = TrackState.Changed; + } + else + { + trackable.State = TrackState.Added; + changeSet.Add(key); + } } } else @@ -368,7 +422,7 @@ public StorageItem GetOrAdd(StorageKey key, Func factory) lock (dictionary) { cached = dictionary - .Where(p => p.Value.State != TrackState.Deleted && (keyOrPrefix == null || comparer.Compare(p.Key.ToArray(), keyOrPrefix) >= 0)) + .Where(p => p.Value.State != TrackState.Deleted && p.Value.State != TrackState.NotFound && (keyOrPrefix == null || comparer.Compare(p.Key.ToArray(), keyOrPrefix) >= 0)) .Select(p => ( KeyBytes: p.Key.ToArray(), @@ -430,7 +484,8 @@ public StorageItem TryGet(StorageKey key) { if (dictionary.TryGetValue(key, out Trackable trackable)) { - if (trackable.State == TrackState.Deleted) return null; + if (trackable.State == TrackState.Deleted || trackable.State == TrackState.NotFound) + return null; return trackable.Item; } StorageItem value = TryGetInternal(key); diff --git a/src/neo/Persistence/IReadOnlyStore.cs b/src/Neo/Persistence/IReadOnlyStore.cs similarity index 82% rename from src/neo/Persistence/IReadOnlyStore.cs rename to src/Neo/Persistence/IReadOnlyStore.cs index 3efd8eaa63..4b6c3fe0ec 100644 --- a/src/neo/Persistence/IReadOnlyStore.cs +++ b/src/Neo/Persistence/IReadOnlyStore.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// IReadOnlyStore.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. diff --git a/src/neo/Persistence/ISnapshot.cs b/src/Neo/Persistence/ISnapshot.cs similarity index 75% rename from src/neo/Persistence/ISnapshot.cs rename to src/Neo/Persistence/ISnapshot.cs index 55d9224544..29f1577048 100644 --- a/src/neo/Persistence/ISnapshot.cs +++ b/src/Neo/Persistence/ISnapshot.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ISnapshot.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. diff --git a/src/neo/Persistence/IStore.cs b/src/Neo/Persistence/IStore.cs similarity index 82% rename from src/neo/Persistence/IStore.cs rename to src/Neo/Persistence/IStore.cs index d9b939f303..37ccdf2a83 100644 --- a/src/neo/Persistence/IStore.cs +++ b/src/Neo/Persistence/IStore.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// IStore.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. diff --git a/src/neo/Plugins/IStorageProvider.cs b/src/Neo/Persistence/IStoreProvider.cs similarity index 51% rename from src/neo/Plugins/IStorageProvider.cs rename to src/Neo/Persistence/IStoreProvider.cs index ba7a441d70..3714ef1e76 100644 --- a/src/neo/Plugins/IStorageProvider.cs +++ b/src/Neo/Persistence/IStoreProvider.cs @@ -1,22 +1,26 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// IStoreProvider.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.Persistence; - -namespace Neo.Plugins +namespace Neo.Persistence { /// /// A provider used to create instances. /// - public interface IStorageProvider + public interface IStoreProvider { + /// + /// Gets the name of the . + /// + string Name { get; } + /// /// Creates a new instance of the interface. /// diff --git a/src/neo/Persistence/MemorySnapshot.cs b/src/Neo/Persistence/MemorySnapshot.cs similarity index 77% rename from src/neo/Persistence/MemorySnapshot.cs rename to src/Neo/Persistence/MemorySnapshot.cs index e86435942b..756a4c83b2 100644 --- a/src/neo/Persistence/MemorySnapshot.cs +++ b/src/Neo/Persistence/MemorySnapshot.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// MemorySnapshot.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. @@ -40,7 +41,7 @@ public void Commit() public void Delete(byte[] key) { - writeBatch[key.EnsureNotNull()] = null; + writeBatch[key] = null; } public void Dispose() @@ -49,7 +50,7 @@ public void Dispose() public void Put(byte[] key, byte[] value) { - writeBatch[key.EnsureNotNull()] = value; + writeBatch[key[..]] = value[..]; } public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[] keyOrPrefix, SeekDirection direction = SeekDirection.Forward) @@ -59,18 +60,18 @@ public void Put(byte[] key, byte[] value) if (keyOrPrefix?.Length > 0) records = records.Where(p => comparer.Compare(p.Key, keyOrPrefix) >= 0); records = records.OrderBy(p => p.Key, comparer); - return records.Select(p => (p.Key, p.Value)); + return records.Select(p => (p.Key[..], p.Value[..])); } public byte[] TryGet(byte[] key) { - immutableData.TryGetValue(key.EnsureNotNull(), out byte[] value); - return value; + immutableData.TryGetValue(key, out byte[] value); + return value?[..]; } public bool Contains(byte[] key) { - return innerData.ContainsKey(key.EnsureNotNull()); + return immutableData.ContainsKey(key); } } } diff --git a/src/Neo/Persistence/MemoryStore.cs b/src/Neo/Persistence/MemoryStore.cs new file mode 100644 index 0000000000..191b89ea4f --- /dev/null +++ b/src/Neo/Persistence/MemoryStore.cs @@ -0,0 +1,81 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MemoryStore.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.IO; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Neo.Persistence +{ + /// + /// An in-memory implementation that uses ConcurrentDictionary as the underlying storage. + /// + public class MemoryStore : IStore + { + private readonly ConcurrentDictionary _innerData = new(ByteArrayEqualityComparer.Default); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Delete(byte[] key) + { + _innerData.TryRemove(key, out _); + } + + public void Dispose() + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ISnapshot GetSnapshot() + { + return new MemorySnapshot(_innerData); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Put(byte[] key, byte[] value) + { + _innerData[key[..]] = value[..]; + } + + public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[] keyOrPrefix, SeekDirection direction = SeekDirection.Forward) + { + if (direction == SeekDirection.Backward && keyOrPrefix?.Length == 0) yield break; + + var comparer = direction == SeekDirection.Forward ? ByteArrayComparer.Default : ByteArrayComparer.Reverse; + IEnumerable> records = _innerData; + if (keyOrPrefix?.Length > 0) + records = records.Where(p => comparer.Compare(p.Key, keyOrPrefix) >= 0); + records = records.OrderBy(p => p.Key, comparer); + foreach (var pair in records) + yield return (pair.Key[..], pair.Value[..]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte[] TryGet(byte[] key) + { + if (!_innerData.TryGetValue(key, out byte[] value)) return null; + return value[..]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(byte[] key) + { + return _innerData.ContainsKey(key); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void Reset() + { + _innerData.Clear(); + } + } +} diff --git a/src/Neo/Persistence/MemoryStoreProvider.cs b/src/Neo/Persistence/MemoryStoreProvider.cs new file mode 100644 index 0000000000..e72a13c899 --- /dev/null +++ b/src/Neo/Persistence/MemoryStoreProvider.cs @@ -0,0 +1,19 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MemoryStoreProvider.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.Persistence +{ + public class MemoryStoreProvider : IStoreProvider + { + public string Name => nameof(MemoryStore); + public IStore GetStore(string path) => new MemoryStore(); + } +} diff --git a/src/neo/Persistence/SeekDirection.cs b/src/Neo/Persistence/SeekDirection.cs similarity index 66% rename from src/neo/Persistence/SeekDirection.cs rename to src/Neo/Persistence/SeekDirection.cs index 55ec288811..4155ace94f 100644 --- a/src/neo/Persistence/SeekDirection.cs +++ b/src/Neo/Persistence/SeekDirection.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// SeekDirection.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. diff --git a/src/neo/Persistence/SnapshotCache.cs b/src/Neo/Persistence/SnapshotCache.cs similarity index 77% rename from src/neo/Persistence/SnapshotCache.cs rename to src/Neo/Persistence/SnapshotCache.cs index f3326b3d36..e441f977c0 100644 --- a/src/neo/Persistence/SnapshotCache.cs +++ b/src/Neo/Persistence/SnapshotCache.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// SnapshotCache.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. @@ -62,17 +63,21 @@ public void Dispose() protected override StorageItem GetInternal(StorageKey key) { - return store.TryGet(key.ToArray()).AsSerializable(); + byte[] value = store.TryGet(key.ToArray()); + if (value == null) throw new KeyNotFoundException(); + return new(value); } protected override IEnumerable<(StorageKey, StorageItem)> SeekInternal(byte[] keyOrPrefix, SeekDirection direction) { - return store.Seek(keyOrPrefix, direction).Select(p => (p.Key.AsSerializable(), p.Value.AsSerializable())); + return store.Seek(keyOrPrefix, direction).Select(p => (new StorageKey(p.Key), new StorageItem(p.Value))); } protected override StorageItem TryGetInternal(StorageKey key) { - return store.TryGet(key.ToArray())?.AsSerializable(); + byte[] value = store.TryGet(key.ToArray()); + if (value == null) return null; + return new(value); } protected override void UpdateInternal(StorageKey key, StorageItem value) diff --git a/src/Neo/Persistence/StoreFactory.cs b/src/Neo/Persistence/StoreFactory.cs new file mode 100644 index 0000000000..2764366f06 --- /dev/null +++ b/src/Neo/Persistence/StoreFactory.cs @@ -0,0 +1,59 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// StoreFactory.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.Collections.Generic; + +namespace Neo.Persistence; + +public static class StoreFactory +{ + private static readonly Dictionary providers = new(); + + static StoreFactory() + { + var memProvider = new MemoryStoreProvider(); + RegisterProvider(memProvider); + + // Default cases + providers.Add("", memProvider); + } + + public static void RegisterProvider(IStoreProvider provider) + { + providers.Add(provider.Name, provider); + } + + /// + /// Get store provider by name + /// + /// Name + /// Store provider + public static IStoreProvider? GetStoreProvider(string name) + { + if (providers.TryGetValue(name, out var provider)) + { + return provider; + } + + return null; + } + + /// + /// Get store from name + /// + /// The storage engine used to create the objects. If this parameter is , a default in-memory storage engine will be used. + /// The path of the storage. If is the default in-memory storage engine, this parameter is ignored. + /// The storage engine. + public static IStore GetStore(string storageProvider, string path) + { + return providers[storageProvider].GetStore(path); + } +} diff --git a/src/neo/Persistence/TrackState.cs b/src/Neo/Persistence/TrackState.cs similarity index 65% rename from src/neo/Persistence/TrackState.cs rename to src/Neo/Persistence/TrackState.cs index 688541759c..a2780d62a8 100644 --- a/src/neo/Persistence/TrackState.cs +++ b/src/Neo/Persistence/TrackState.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// TrackState.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. @@ -33,6 +34,11 @@ public enum TrackState : byte /// /// Indicates that the entry should be deleted from the underlying storage when committing. /// - Deleted + Deleted, + + /// + /// Indicates that the entry was not found in the underlying storage. + /// + NotFound } } diff --git a/src/neo/Plugins/Plugin.cs b/src/Neo/Plugins/Plugin.cs similarity index 63% rename from src/neo/Plugins/Plugin.cs rename to src/Neo/Plugins/Plugin.cs index 4aa8d48fd0..248301af56 100644 --- a/src/neo/Plugins/Plugin.cs +++ b/src/Neo/Plugins/Plugin.cs @@ -1,15 +1,15 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Plugin.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.Extensions.Configuration; -using Neo.SmartContract; using System; using System.Collections.Generic; using System.IO; @@ -20,7 +20,8 @@ namespace Neo.Plugins { /// - /// Represents the base class of all plugins. Any plugin should inherit this class. The plugins are automatically loaded when the process starts. + /// Represents the base class of all plugins. Any plugin should inherit this class. + /// The plugins are automatically loaded when the process starts. /// public abstract class Plugin : IDisposable { @@ -29,23 +30,22 @@ public abstract class Plugin : IDisposable /// public static readonly List Plugins = new(); - internal static readonly List Loggers = new(); - internal static readonly Dictionary Storages = new(); - internal static readonly List PersistencePlugins = new(); - internal static readonly List P2PPlugins = new(); - internal static readonly List TxObserverPlugins = new(); - /// - /// The directory containing the plugin dll files. Files can be contained in any subdirectory. + /// The directory containing the plugin folders. Files can be contained in any subdirectory. /// - public static readonly string PluginsDirectory = Combine(GetDirectoryName(Assembly.GetEntryAssembly().Location), "Plugins"); + public static readonly string PluginsDirectory = Combine(GetDirectoryName(System.AppContext.BaseDirectory), "Plugins"); private static readonly FileSystemWatcher configWatcher; + /// + /// Indicates the root path of the plugin. + /// + public string RootPath => Combine(PluginsDirectory, GetType().Assembly.GetName().Name); + /// /// Indicates the location of the plugin configuration file. /// - public virtual string ConfigFile => Combine(PluginsDirectory, GetType().Assembly.GetName().Name, "config.json"); + public virtual string ConfigFile => Combine(RootPath, "config.json"); /// /// Indicates the name of the plugin. @@ -60,7 +60,7 @@ public abstract class Plugin : IDisposable /// /// Indicates the location of the plugin dll file. /// - public virtual string Path => Combine(PluginsDirectory, GetType().Assembly.ManifestModule.ScopeName); + public virtual string Path => Combine(RootPath, GetType().Assembly.ManifestModule.ScopeName); /// /// Indicates the version of the plugin. @@ -69,18 +69,18 @@ public abstract class Plugin : IDisposable static Plugin() { - if (Directory.Exists(PluginsDirectory)) + if (!Directory.Exists(PluginsDirectory)) return; + configWatcher = new FileSystemWatcher(PluginsDirectory) { - configWatcher = new FileSystemWatcher(PluginsDirectory) - { - EnableRaisingEvents = true, - IncludeSubdirectories = true, - NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite | NotifyFilters.Size, - }; - configWatcher.Changed += ConfigWatcher_Changed; - configWatcher.Created += ConfigWatcher_Changed; - AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; - } + EnableRaisingEvents = true, + IncludeSubdirectories = true, + NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.CreationTime | NotifyFilters.LastWrite | NotifyFilters.Size, + }; + configWatcher.Changed += ConfigWatcher_Changed; + configWatcher.Created += ConfigWatcher_Changed; + configWatcher.Renamed += ConfigWatcher_Changed; + configWatcher.Deleted += ConfigWatcher_Changed; + AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; } /// @@ -89,19 +89,12 @@ static Plugin() protected Plugin() { Plugins.Add(this); - - if (this is ILogPlugin logger) Loggers.Add(logger); - if (this is IStorageProvider storage) Storages.Add(Name, storage); - if (this is IP2PPlugin p2p) P2PPlugins.Add(p2p); - if (this is IPersistencePlugin persistence) PersistencePlugins.Add(persistence); - if (this is IMemoryPoolTxObserverPlugin txObserver) TxObserverPlugins.Add(txObserver); - if (this is IApplicationEngineProvider provider) ApplicationEngine.SetApplicationEngineProvider(provider); - Configure(); } /// - /// Called when the plugin is loaded and need to load the configure file, or the configuration file has been modified and needs to be reconfigured. + /// Called when the plugin is loaded and need to load the configure file, + /// or the configuration file has been modified and needs to be reconfigured. /// protected virtual void Configure() { @@ -112,20 +105,8 @@ private static void ConfigWatcher_Changed(object sender, FileSystemEventArgs e) switch (GetExtension(e.Name)) { case ".json": - try - { - Plugins.FirstOrDefault(p => p.ConfigFile == e.FullPath)?.Configure(); - } - catch (FormatException) { } - break; case ".dll": - if (e.ChangeType != WatcherChangeTypes.Created) return; - if (GetDirectoryName(e.FullPath) != PluginsDirectory) return; - try - { - LoadPlugin(Assembly.Load(File.ReadAllBytes(e.FullPath))); - } - catch { } + Utility.Log(nameof(Plugin), LogLevel.Warning, $"File {e.Name} is {e.ChangeType}, please restart node."); break; } } @@ -137,14 +118,13 @@ private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEven AssemblyName an = new(args.Name); - Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name); - if (assembly is null) - assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == an.Name); + Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name) ?? + AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == an.Name); if (assembly != null) return assembly; string filename = an.Name + ".dll"; string path = filename; - if (!File.Exists(path)) path = Combine(GetDirectoryName(Assembly.GetEntryAssembly().Location), filename); + if (!File.Exists(path)) path = Combine(GetDirectoryName(System.AppContext.BaseDirectory), filename); if (!File.Exists(path)) path = Combine(PluginsDirectory, filename); if (!File.Exists(path)) path = Combine(PluginsDirectory, args.RequestingAssembly.GetName().Name, filename); if (!File.Exists(path)) return null; @@ -196,13 +176,16 @@ internal static void LoadPlugins() { if (!Directory.Exists(PluginsDirectory)) return; List assemblies = new(); - foreach (string filename in Directory.EnumerateFiles(PluginsDirectory, "*.dll", SearchOption.TopDirectoryOnly)) + foreach (string rootPath in Directory.GetDirectories(PluginsDirectory)) { - try + foreach (var filename in Directory.EnumerateFiles(rootPath, "*.dll", SearchOption.TopDirectoryOnly)) { - assemblies.Add(Assembly.Load(File.ReadAllBytes(filename))); + try + { + assemblies.Add(Assembly.Load(File.ReadAllBytes(filename))); + } + catch { } } - catch { } } foreach (Assembly assembly in assemblies) { @@ -221,7 +204,7 @@ protected void Log(object message, LogLevel level = LogLevel.Info) } /// - /// Called when a message to the plugins is received. The messnage is sent by calling . + /// Called when a message to the plugins is received. The message is sent by calling . /// /// The received message. /// if the has been handled; otherwise, . @@ -235,7 +218,7 @@ protected virtual bool OnMessage(object message) /// Called when a is loaded. /// /// The loaded . - internal protected virtual void OnSystemLoaded(NeoSystem system) + protected internal virtual void OnSystemLoaded(NeoSystem system) { } @@ -246,10 +229,7 @@ internal protected virtual void OnSystemLoaded(NeoSystem system) /// if the is handled by a plugin; otherwise, . public static bool SendMessage(object message) { - foreach (Plugin plugin in Plugins) - if (plugin.OnMessage(message)) - return true; - return false; + return Plugins.Any(plugin => plugin.OnMessage(message)); } } } diff --git a/src/Neo/Properties/AssemblyInfo.cs b/src/Neo/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..8d07419cae --- /dev/null +++ b/src/Neo/Properties/AssemblyInfo.cs @@ -0,0 +1,16 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// AssemblyInfo.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.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] +[assembly: InternalsVisibleTo("neo.UnitTests")] +[assembly: InternalsVisibleTo("neodebug-3-adapter")] diff --git a/src/neo/ProtocolSettings.cs b/src/Neo/ProtocolSettings.cs similarity index 58% rename from src/neo/ProtocolSettings.cs rename to src/Neo/ProtocolSettings.cs index 83d1f9c474..1b7367a6a2 100644 --- a/src/neo/ProtocolSettings.cs +++ b/src/Neo/ProtocolSettings.cs @@ -1,19 +1,20 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ProtocolSettings.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.Extensions.Configuration; using Neo.Cryptography.ECC; using Neo.Network.P2P.Payloads; -using Neo.SmartContract.Native; using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; namespace Neo @@ -23,6 +24,8 @@ namespace Neo /// public record ProtocolSettings { + private static readonly IList AllHardforks = Enum.GetValues(typeof(Hardfork)).Cast().ToArray(); + /// /// The magic number of the NEO network. /// @@ -84,9 +87,9 @@ public record ProtocolSettings public uint MaxTraceableBlocks { get; init; } /// - /// Contains the update history of all native contracts. + /// Sets the block height from which a hardfork is activated. /// - public IReadOnlyDictionary NativeUpdateHistory { get; init; } + public ImmutableDictionary Hardforks { get; init; } /// /// Indicates the amount of gas to distribute during initialization. @@ -102,64 +105,23 @@ public record ProtocolSettings /// /// The default protocol settings for NEO MainNet. /// - public static ProtocolSettings Default { get; } = new ProtocolSettings + public static ProtocolSettings Default { get; } = Custom ?? new ProtocolSettings { - Network = 0x334F454Eu, + Network = 0u, AddressVersion = 0x35, - StandbyCommittee = new[] - { - //Validators - ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", ECCurve.Secp256r1), - ECPoint.Parse("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", ECCurve.Secp256r1), - ECPoint.Parse("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", ECCurve.Secp256r1), - ECPoint.Parse("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", ECCurve.Secp256r1), - ECPoint.Parse("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", ECCurve.Secp256r1), - ECPoint.Parse("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", ECCurve.Secp256r1), - ECPoint.Parse("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", ECCurve.Secp256r1), - //Other Members - ECPoint.Parse("023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe", ECCurve.Secp256r1), - ECPoint.Parse("03708b860c1de5d87f5b151a12c2a99feebd2e8b315ee8e7cf8aa19692a9e18379", ECCurve.Secp256r1), - ECPoint.Parse("03c6aa6e12638b36e88adc1ccdceac4db9929575c3e03576c617c49cce7114a050", ECCurve.Secp256r1), - ECPoint.Parse("03204223f8c86b8cd5c89ef12e4f0dbb314172e9241e30c9ef2293790793537cf0", ECCurve.Secp256r1), - ECPoint.Parse("02a62c915cf19c7f19a50ec217e79fac2439bbaad658493de0c7d8ffa92ab0aa62", ECCurve.Secp256r1), - ECPoint.Parse("03409f31f0d66bdc2f70a9730b66fe186658f84a8018204db01c106edc36553cd0", ECCurve.Secp256r1), - ECPoint.Parse("0288342b141c30dc8ffcde0204929bb46aed5756b41ef4a56778d15ada8f0c6654", ECCurve.Secp256r1), - ECPoint.Parse("020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639", ECCurve.Secp256r1), - ECPoint.Parse("0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30", ECCurve.Secp256r1), - ECPoint.Parse("03d281b42002647f0113f36c7b8efb30db66078dfaaa9ab3ff76d043a98d512fde", ECCurve.Secp256r1), - ECPoint.Parse("02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad", ECCurve.Secp256r1), - ECPoint.Parse("0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d", ECCurve.Secp256r1), - ECPoint.Parse("03cdcea66032b82f5c30450e381e5295cae85c5e6943af716cc6b646352a6067dc", ECCurve.Secp256r1), - ECPoint.Parse("02cd5a5547119e24feaa7c2a0f37b8c9366216bab7054de0065c9be42084003c8a", ECCurve.Secp256r1) - }, - ValidatorsCount = 7, - SeedList = new[] - { - "seed1.neo.org:10333", - "seed2.neo.org:10333", - "seed3.neo.org:10333", - "seed4.neo.org:10333", - "seed5.neo.org:10333" - }, + StandbyCommittee = Array.Empty(), + ValidatorsCount = 0, + SeedList = Array.Empty(), MillisecondsPerBlock = 15000, MaxTransactionsPerBlock = 512, MemoryPoolMaxTransactions = 50_000, MaxTraceableBlocks = 2_102_400, InitialGasDistribution = 52_000_000_00000000, - NativeUpdateHistory = new Dictionary - { - [nameof(ContractManagement)] = new[] { 0u }, - [nameof(StdLib)] = new[] { 0u }, - [nameof(CryptoLib)] = new[] { 0u }, - [nameof(LedgerContract)] = new[] { 0u }, - [nameof(NeoToken)] = new[] { 0u }, - [nameof(GasToken)] = new[] { 0u }, - [nameof(PolicyContract)] = new[] { 0u }, - [nameof(RoleManagement)] = new[] { 0u }, - [nameof(OracleContract)] = new[] { 0u } - } + Hardforks = EnsureOmmitedHardforks(new Dictionary()).ToImmutableDictionary() }; + public static ProtocolSettings? Custom { get; set; } + /// /// Loads the at the specified path. /// @@ -170,7 +132,9 @@ public static ProtocolSettings Load(string path, bool optional = true) { IConfigurationRoot config = new ConfigurationBuilder().AddJsonFile(path, optional).Build(); IConfigurationSection section = config.GetSection("ProtocolConfiguration"); - return Load(section); + var settings = Load(section); + CheckingHardfork(settings); + return settings; } /// @@ -196,10 +160,78 @@ public static ProtocolSettings Load(IConfigurationSection section) MemoryPoolMaxTransactions = section.GetValue("MemoryPoolMaxTransactions", Default.MemoryPoolMaxTransactions), MaxTraceableBlocks = section.GetValue("MaxTraceableBlocks", Default.MaxTraceableBlocks), InitialGasDistribution = section.GetValue("InitialGasDistribution", Default.InitialGasDistribution), - NativeUpdateHistory = section.GetSection("NativeUpdateHistory").Exists() - ? section.GetSection("NativeUpdateHistory").GetChildren().ToDictionary(p => p.Key, p => p.GetChildren().Select(q => uint.Parse(q.Value)).ToArray()) - : Default.NativeUpdateHistory + Hardforks = section.GetSection("Hardforks").Exists() + ? EnsureOmmitedHardforks(section.GetSection("Hardforks").GetChildren().ToDictionary(p => Enum.Parse(p.Key, true), p => uint.Parse(p.Value))).ToImmutableDictionary() + : Default.Hardforks }; } + + /// + /// Explicitly set the height of all old omitted hardforks to 0 for proper IsHardforkEnabled behaviour. + /// + /// HardForks + /// Processed hardfork configuration + private static Dictionary EnsureOmmitedHardforks(Dictionary hardForks) + { + foreach (Hardfork hf in AllHardforks) + { + if (!hardForks.ContainsKey(hf)) + { + hardForks[hf] = 0; + } + else + { + break; + } + } + + return hardForks; + } + + private static void CheckingHardfork(ProtocolSettings settings) + { + var allHardforks = Enum.GetValues(typeof(Hardfork)).Cast().ToList(); + // Check for continuity in configured hardforks + var sortedHardforks = settings.Hardforks.Keys + .OrderBy(allHardforks.IndexOf) + .ToList(); + + for (int i = 0; i < sortedHardforks.Count - 1; i++) + { + int currentIndex = allHardforks.IndexOf(sortedHardforks[i]); + int nextIndex = allHardforks.IndexOf(sortedHardforks[i + 1]); + + // If they aren't consecutive, return false. + if (nextIndex - currentIndex > 1) + throw new ArgumentException("Hardfork configuration is not continuous."); + } + // Check that block numbers are not higher in earlier hardforks than in later ones + for (int i = 0; i < sortedHardforks.Count - 1; i++) + { + if (settings.Hardforks[sortedHardforks[i]] > settings.Hardforks[sortedHardforks[i + 1]]) + { + // This means the block number for the current hardfork is greater than the next one, which should not be allowed. + throw new ArgumentException($"The Hardfork configuration for {sortedHardforks[i]} is greater than for {sortedHardforks[i + 1]}"); + } + } + } + + /// + /// Check if the Hardfork is Enabled + /// + /// Hardfork + /// Block index + /// True if enabled + public bool IsHardforkEnabled(Hardfork hardfork, uint index) + { + if (Hardforks.TryGetValue(hardfork, out uint height)) + { + // If the hardfork has a specific height in the configuration, check the block height. + return index >= height; + } + + // If the hardfork isn't specified in the configuration, return false. + return false; + } } } diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/Neo/SmartContract/ApplicationEngine.Contract.cs similarity index 83% rename from src/neo/SmartContract/ApplicationEngine.Contract.cs rename to src/Neo/SmartContract/ApplicationEngine.Contract.cs index ed1ecde3b5..1677396f1c 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/Neo/SmartContract/ApplicationEngine.Contract.cs @@ -1,17 +1,18 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ApplicationEngine.Contract.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.Cryptography.ECC; using Neo.SmartContract.Manifest; using Neo.SmartContract.Native; -using Neo.VM.Types; +using Neo.VM; using System; using Array = Neo.VM.Types.Array; @@ -41,13 +42,13 @@ partial class ApplicationEngine /// The of System.Contract.CreateStandardAccount. /// Calculates corresponding account scripthash for the given public key. /// - public static readonly InteropDescriptor System_Contract_CreateStandardAccount = Register("System.Contract.CreateStandardAccount", nameof(CreateStandardAccount), 1 << 8, CallFlags.None); + public static readonly InteropDescriptor System_Contract_CreateStandardAccount = Register("System.Contract.CreateStandardAccount", nameof(CreateStandardAccount), 0, CallFlags.None); /// /// The of System.Contract.CreateMultisigAccount. /// Calculates corresponding multisig account scripthash for the given public keys. /// - public static readonly InteropDescriptor System_Contract_CreateMultisigAccount = Register("System.Contract.CreateMultisigAccount", nameof(CreateMultisigAccount), 1 << 8, CallFlags.None); + public static readonly InteropDescriptor System_Contract_CreateMultisigAccount = Register("System.Contract.CreateMultisigAccount", nameof(CreateMultisigAccount), 0, CallFlags.None); /// /// The of System.Contract.NativeOnPersist. @@ -76,13 +77,13 @@ protected internal void CallContract(UInt160 contractHash, string method, CallFl throw new ArgumentOutOfRangeException(nameof(callFlags)); ContractState contract = NativeContract.ContractManagement.GetContract(Snapshot, contractHash); - if (contract is null) throw new InvalidOperationException($"Called Contract Does Not Exist: {contractHash}"); + if (contract is null) throw new InvalidOperationException($"Called Contract Does Not Exist: {contractHash}.{method}"); ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method, args.Count); if (md is null) throw new InvalidOperationException($"Method \"{method}\" with {args.Count} parameter(s) doesn't exist in the contract {contractHash}."); bool hasReturnValue = md.ReturnType != ContractParameterType.Void; - if (!hasReturnValue) CurrentContext.EvaluationStack.Push(StackItem.Null); - CallContractInternal(contract, md, callFlags, hasReturnValue, args); + ExecutionContext context = CallContractInternal(contract, md, callFlags, hasReturnValue, args); + context.GetState().IsDynamicCall = true; } /// @@ -95,10 +96,7 @@ protected internal void CallNativeContract(byte version) NativeContract contract = NativeContract.GetContract(CurrentScriptHash); if (contract is null) throw new InvalidOperationException("It is not allowed to use \"System.Contract.CallNative\" directly."); - uint[] updates = ProtocolSettings.NativeUpdateHistory[contract.Name]; - if (updates.Length == 0) - throw new InvalidOperationException($"The native contract {contract.Name} is not active."); - if (updates[0] > NativeContract.Ledger.CurrentIndex(Snapshot)) + if (!contract.IsActive(ProtocolSettings, NativeContract.Ledger.CurrentIndex(Snapshot))) throw new InvalidOperationException($"The native contract {contract.Name} is not active."); contract.Invoke(this, version); } @@ -120,8 +118,12 @@ protected internal CallFlags GetCallFlags() /// /// The public key of the account. /// The hash of the account. - internal protected static UInt160 CreateStandardAccount(ECPoint pubKey) + internal protected UInt160 CreateStandardAccount(ECPoint pubKey) { + long fee = IsHardforkEnabled(Hardfork.HF_Aspidochelone) + ? CheckSigPrice + : 1 << 8; + AddGas(fee * ExecFeeFactor); return Contract.CreateSignatureRedeemScript(pubKey).ToScriptHash(); } @@ -132,8 +134,12 @@ internal protected static UInt160 CreateStandardAccount(ECPoint pubKey) /// The minimum number of correct signatures that need to be provided in order for the verification to pass. /// The public keys of the account. /// The hash of the account. - internal protected static UInt160 CreateMultisigAccount(int m, ECPoint[] pubKeys) + internal protected UInt160 CreateMultisigAccount(int m, ECPoint[] pubKeys) { + long fee = IsHardforkEnabled(Hardfork.HF_Aspidochelone) + ? CheckSigPrice * pubKeys.Length + : 1 << 8; + AddGas(fee * ExecFeeFactor); return Contract.CreateMultiSigRedeemScript(m, pubKeys).ToScriptHash(); } @@ -149,9 +155,7 @@ protected internal async void NativeOnPersist() throw new InvalidOperationException(); foreach (NativeContract contract in NativeContract.Contracts) { - uint[] updates = ProtocolSettings.NativeUpdateHistory[contract.Name]; - if (updates.Length == 0) continue; - if (updates[0] <= PersistingBlock.Index) + if (contract.IsActive(ProtocolSettings, PersistingBlock.Index)) await contract.OnPersist(this); } } @@ -173,9 +177,7 @@ protected internal async void NativePostPersist() throw new InvalidOperationException(); foreach (NativeContract contract in NativeContract.Contracts) { - uint[] updates = ProtocolSettings.NativeUpdateHistory[contract.Name]; - if (updates.Length == 0) continue; - if (updates[0] <= PersistingBlock.Index) + if (contract.IsActive(ProtocolSettings, PersistingBlock.Index)) await contract.PostPersist(this); } } diff --git a/src/neo/SmartContract/ApplicationEngine.Crypto.cs b/src/Neo/SmartContract/ApplicationEngine.Crypto.cs similarity index 89% rename from src/neo/SmartContract/ApplicationEngine.Crypto.cs rename to src/Neo/SmartContract/ApplicationEngine.Crypto.cs index f4d26d6046..1a8b2c7d6d 100644 --- a/src/neo/SmartContract/ApplicationEngine.Crypto.cs +++ b/src/Neo/SmartContract/ApplicationEngine.Crypto.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ApplicationEngine.Crypto.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. @@ -65,7 +66,7 @@ protected internal bool CheckMultisig(byte[][] pubkeys, byte[][] signatures) byte[] message = ScriptContainer.GetSignData(ProtocolSettings.Network); int m = signatures.Length, n = pubkeys.Length; if (n == 0 || m == 0 || m > n) throw new ArgumentException(); - AddGas(CheckSigPrice * n * exec_fee_factor); + AddGas(CheckSigPrice * n * ExecFeeFactor); try { for (int i = 0, j = 0; i < m && j < n;) diff --git a/src/neo/SmartContract/ApplicationEngine.Iterator.cs b/src/Neo/SmartContract/ApplicationEngine.Iterator.cs similarity index 81% rename from src/neo/SmartContract/ApplicationEngine.Iterator.cs rename to src/Neo/SmartContract/ApplicationEngine.Iterator.cs index 8c1a2a9048..2ac9df7474 100644 --- a/src/neo/SmartContract/ApplicationEngine.Iterator.cs +++ b/src/Neo/SmartContract/ApplicationEngine.Iterator.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ApplicationEngine.Iterator.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. @@ -44,9 +45,9 @@ internal protected static bool IteratorNext(IIterator iterator) /// /// The iterator to be used. /// The element in the collection at the current position of the iterator. - internal protected static StackItem IteratorValue(IIterator iterator) + internal protected StackItem IteratorValue(IIterator iterator) { - return iterator.Value(); + return iterator.Value(ReferenceCounter); } } } diff --git a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs b/src/Neo/SmartContract/ApplicationEngine.OpCodePrices.cs similarity index 87% rename from src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs rename to src/Neo/SmartContract/ApplicationEngine.OpCodePrices.cs index 12c83206b0..16a0a95944 100644 --- a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs +++ b/src/Neo/SmartContract/ApplicationEngine.OpCodePrices.cs @@ -1,14 +1,16 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ApplicationEngine.OpCodePrices.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.VM; +using System; using System.Collections.Generic; namespace Neo.SmartContract @@ -18,6 +20,7 @@ partial class ApplicationEngine /// /// The prices of all the opcodes. /// + [Obsolete("You should use OpCodePriceTable")] public static readonly IReadOnlyDictionary OpCodePrices = new Dictionary { [OpCode.PUSHINT8] = 1 << 0, @@ -26,6 +29,8 @@ partial class ApplicationEngine [OpCode.PUSHINT64] = 1 << 0, [OpCode.PUSHINT128] = 1 << 2, [OpCode.PUSHINT256] = 1 << 2, + [OpCode.PUSHT] = 1 << 0, + [OpCode.PUSHF] = 1 << 0, [OpCode.PUSHA] = 1 << 2, [OpCode.PUSHNULL] = 1 << 0, [OpCode.PUSHDATA1] = 1 << 3, @@ -73,7 +78,9 @@ partial class ApplicationEngine [OpCode.CALLA] = 1 << 9, [OpCode.CALLT] = 1 << 15, [OpCode.ABORT] = 0, + [OpCode.ABORTMSG] = 0, [OpCode.ASSERT] = 1 << 0, + [OpCode.ASSERTMSG] = 1 << 0, [OpCode.THROW] = 1 << 9, [OpCode.TRY] = 1 << 2, [OpCode.TRY_L] = 1 << 2, @@ -171,6 +178,8 @@ partial class ApplicationEngine [OpCode.MOD] = 1 << 3, [OpCode.POW] = 1 << 6, [OpCode.SQRT] = 1 << 6, + [OpCode.MODMUL] = 1 << 5, + [OpCode.MODPOW] = 1 << 11, [OpCode.SHL] = 1 << 3, [OpCode.SHR] = 1 << 3, [OpCode.NOT] = 1 << 2, @@ -211,5 +220,20 @@ partial class ApplicationEngine [OpCode.ISTYPE] = 1 << 1, [OpCode.CONVERT] = 1 << 13, }; + + public static readonly long[] OpCodePriceTable = new long[byte.MaxValue]; + + /// + /// Init OpCodePrices + /// + static ApplicationEngine() + { +#pragma warning disable CS0618 // Type or member is obsolete + foreach (var entry in OpCodePrices) +#pragma warning restore CS0618 // Type or member is obsolete + { + OpCodePriceTable[(byte)entry.Key] = entry.Value; + } + } } } diff --git a/src/neo/SmartContract/ApplicationEngine.Runtime.cs b/src/Neo/SmartContract/ApplicationEngine.Runtime.cs similarity index 59% rename from src/neo/SmartContract/ApplicationEngine.Runtime.cs rename to src/Neo/SmartContract/ApplicationEngine.Runtime.cs index 877343bdd5..293b245bd7 100644 --- a/src/neo/SmartContract/ApplicationEngine.Runtime.cs +++ b/src/Neo/SmartContract/ApplicationEngine.Runtime.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ApplicationEngine.Runtime.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. @@ -12,6 +13,7 @@ using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.SmartContract.Native; +using Neo.VM; using Neo.VM.Types; using System; using System.Collections.Generic; @@ -34,6 +36,8 @@ partial class ApplicationEngine /// public const int MaxNotificationSize = 1024; + private uint random_times = 0; + /// /// The of System.Runtime.Platform. /// Gets the name of the current platform. @@ -46,6 +50,12 @@ partial class ApplicationEngine /// public static readonly InteropDescriptor System_Runtime_GetNetwork = Register("System.Runtime.GetNetwork", nameof(GetNetwork), 1 << 3, CallFlags.None); + /// + /// The of System.Runtime.GetAddressVersion. + /// Gets the address version of the current network. + /// + public static readonly InteropDescriptor System_Runtime_GetAddressVersion = Register("System.Runtime.GetAddressVersion", nameof(GetAddressVersion), 1 << 3, CallFlags.None); + /// /// The of System.Runtime.GetTrigger. /// Gets the trigger of the execution. @@ -82,6 +92,12 @@ partial class ApplicationEngine /// public static readonly InteropDescriptor System_Runtime_GetEntryScriptHash = Register("System.Runtime.GetEntryScriptHash", nameof(EntryScriptHash), 1 << 4, CallFlags.None); + /// + /// The of System.Runtime.LoadScript. + /// Loads a script at rumtime. + /// + public static readonly InteropDescriptor System_Runtime_LoadScript = Register("System.Runtime.LoadScript", nameof(RuntimeLoadScript), 1 << 15, CallFlags.AllowCall); + /// /// The of System.Runtime.CheckWitness. /// Determines whether the specified account has witnessed the current transaction. @@ -98,7 +114,7 @@ partial class ApplicationEngine /// The of System.Runtime.GetRandom. /// Gets the random number generated from the VRF. /// - public static readonly InteropDescriptor System_Runtime_GetRandom = Register("System.Runtime.GetRandom", nameof(GetRandom), 1 << 4, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_GetRandom = Register("System.Runtime.GetRandom", nameof(GetRandom), 0, CallFlags.None); /// /// The of System.Runtime.Log. @@ -116,7 +132,7 @@ partial class ApplicationEngine /// The of System.Runtime.GetNotifications. /// Gets the notifications sent by the specified contract during the execution. /// - public static readonly InteropDescriptor System_Runtime_GetNotifications = Register("System.Runtime.GetNotifications", nameof(GetNotifications), 1 << 8, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_GetNotifications = Register("System.Runtime.GetNotifications", nameof(GetNotifications), 1 << 12, CallFlags.None); /// /// The of System.Runtime.GasLeft. @@ -130,6 +146,12 @@ partial class ApplicationEngine /// public static readonly InteropDescriptor System_Runtime_BurnGas = Register("System.Runtime.BurnGas", nameof(BurnGas), 1 << 4, CallFlags.None); + /// + /// The of System.Runtime.CurrentSigners. + /// Get the Signers of the current transaction. + /// + public static readonly InteropDescriptor System_Runtime_CurrentSigners = Register("System.Runtime.CurrentSigners", nameof(GetCurrentSigners), 1 << 4, CallFlags.None); + /// /// The implementation of System.Runtime.Platform. /// Gets the name of the current platform. @@ -150,6 +172,16 @@ internal protected uint GetNetwork() return ProtocolSettings.Network; } + /// + /// The implementation of System.Runtime.GetAddressVersion. + /// Gets the address version of the current network. + /// + /// The address version of the current network. + internal protected byte GetAddressVersion() + { + return ProtocolSettings.AddressVersion; + } + /// /// The implementation of System.Runtime.GetTime. /// Gets the timestamp of the current block. @@ -171,6 +203,27 @@ protected internal StackItem GetScriptContainer() return interop.ToStackItem(ReferenceCounter); } + /// + /// The implementation of System.Runtime.LoadScript. + /// Loads a script at rumtime. + /// + protected internal void RuntimeLoadScript(byte[] script, CallFlags callFlags, Array args) + { + if ((callFlags & ~CallFlags.All) != 0) + throw new ArgumentOutOfRangeException(nameof(callFlags)); + + ExecutionContextState state = CurrentContext.GetState(); + ExecutionContext context = LoadScript(new Script(script, true), configureState: p => + { + p.CallingContext = CurrentContext; + p.CallFlags = callFlags & state.CallFlags & CallFlags.ReadOnly; + p.IsDynamicCall = true; + }); + + for (int i = args.Count - 1; i >= 0; i--) + context.EvaluationStack.Push(args[i]); + } + /// /// The implementation of System.Runtime.CheckWitness. /// Determines whether the specified account has witnessed the current transaction. @@ -183,7 +236,7 @@ protected internal bool CheckWitness(byte[] hashOrPubkey) { 20 => new UInt160(hashOrPubkey), 33 => Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(hashOrPubkey, ECCurve.Secp256r1)).ToScriptHash(), - _ => throw new ArgumentException(null, nameof(hashOrPubkey)) + _ => throw new ArgumentException("Invalid hashOrPubkey.", nameof(hashOrPubkey)) }; return CheckWitnessInternal(hash); } @@ -220,14 +273,14 @@ protected internal bool CheckWitnessInternal(UInt160 hash) return false; } - // Check allow state callflag + // If we don't have the ScriptContainer, we consider that there are no script hashes for verifying + if (ScriptContainer is null) return false; + // Check allow state callflag ValidateCallFlags(CallFlags.ReadStates); // only for non-Transaction types (Block, etc) - - var hashes_for_verifying = ScriptContainer.GetScriptHashesForVerifying(Snapshot); - return hashes_for_verifying.Contains(hash); + return ScriptContainer.GetScriptHashesForVerifying(Snapshot).Contains(hash); } /// @@ -251,8 +304,20 @@ protected internal int GetInvocationCounter() /// The next random number. protected internal BigInteger GetRandom() { - nonceData = Cryptography.Helper.Murmur128(nonceData, ProtocolSettings.Network); - return new BigInteger(nonceData, isUnsigned: true); + byte[] buffer; + long price; + if (IsHardforkEnabled(Hardfork.HF_Aspidochelone)) + { + buffer = Cryptography.Helper.Murmur128(nonceData, ProtocolSettings.Network + random_times++); + price = 1 << 13; + } + else + { + buffer = nonceData = Cryptography.Helper.Murmur128(nonceData, ProtocolSettings.Network); + price = 1 << 4; + } + AddGas(price * ExecFeeFactor); + return new BigInteger(buffer, isUnsigned: true); } /// @@ -262,9 +327,16 @@ protected internal BigInteger GetRandom() /// The message of the log. protected internal void RuntimeLog(byte[] state) { - if (state.Length > MaxNotificationSize) throw new ArgumentException(null, nameof(state)); - string message = Utility.StrictUTF8.GetString(state); - Log?.Invoke(this, new LogEventArgs(ScriptContainer, CurrentScriptHash, message)); + if (state.Length > MaxNotificationSize) throw new ArgumentException("Message is too long.", nameof(state)); + try + { + string message = Utility.StrictUTF8.GetString(state); + Log?.Invoke(this, new LogEventArgs(ScriptContainer, CurrentScriptHash, message)); + } + catch + { + throw new ArgumentException("Failed to convert byte array to string: Invalid or non-printable UTF-8 sequence detected.", nameof(state)); + } } /// @@ -274,11 +346,42 @@ protected internal void RuntimeLog(byte[] state) /// The name of the event. /// The arguments of the event. protected internal void RuntimeNotify(byte[] eventName, Array state) + { + if (!IsHardforkEnabled(Hardfork.HF_Basilisk)) + { + RuntimeNotifyV1(eventName, state); + return; + } + if (eventName.Length > MaxEventName) throw new ArgumentException(null, nameof(eventName)); + string name = Utility.StrictUTF8.GetString(eventName); + ContractState contract = CurrentContext.GetState().Contract; + if (contract is null) + throw new InvalidOperationException("Notifications are not allowed in dynamic scripts."); + var @event = contract.Manifest.Abi.Events.FirstOrDefault(p => string.Equals(p.Name, name, StringComparison.Ordinal)); + if (@event is null) + throw new InvalidOperationException($"Event `{name}` does not exist."); + if (@event.Parameters.Length != state.Count) + throw new InvalidOperationException("The number of the arguments does not match the formal parameters of the event."); + for (int i = 0; i < @event.Parameters.Length; i++) + { + var p = @event.Parameters[i]; + if (!CheckItemType(state[i], p.Type)) + throw new InvalidOperationException($"The type of the argument `{p.Name}` does not match the formal parameter."); + } + using MemoryStream ms = new(MaxNotificationSize); + using BinaryWriter writer = new(ms, Utility.StrictUTF8, true); + BinarySerializer.Serialize(writer, state, MaxNotificationSize, Limits.MaxStackSize); + SendNotification(CurrentScriptHash, name, state); + } + + protected internal void RuntimeNotifyV1(byte[] eventName, Array state) { if (eventName.Length > MaxEventName) throw new ArgumentException(null, nameof(eventName)); + if (CurrentContext.GetState().Contract is null) + throw new InvalidOperationException("Notifications are not allowed in dynamic scripts."); using MemoryStream ms = new(MaxNotificationSize); using BinaryWriter writer = new(ms, Utility.StrictUTF8, true); - BinarySerializer.Serialize(writer, state, MaxNotificationSize); + BinarySerializer.Serialize(writer, state, MaxNotificationSize, Limits.MaxStackSize); SendNotification(CurrentScriptHash, Utility.StrictUTF8.GetString(eventName), state); } @@ -290,10 +393,11 @@ protected internal void RuntimeNotify(byte[] eventName, Array state) /// The arguments of the event. protected internal void SendNotification(UInt160 hash, string eventName, Array state) { - NotifyEventArgs notification = new(ScriptContainer, hash, eventName, (Array)state.DeepCopy()); + NotifyEventArgs notification = new(ScriptContainer, hash, eventName, (Array)state.DeepCopy(asImmutable: true)); Notify?.Invoke(this, notification); notifications ??= new List(); notifications.Add(notification); + CurrentContext.GetState().NotificationCount++; } /// @@ -323,5 +427,71 @@ protected internal void BurnGas(long gas) throw new InvalidOperationException("GAS must be positive."); AddGas(gas); } + + /// + /// Get the Signers of the current transaction. + /// + /// The signers of the current transaction, or null if is not related to a transaction execution. + protected internal Signer[] GetCurrentSigners() + { + if (ScriptContainer is Transaction tx) + return tx.Signers; + + return null; + } + + private static bool CheckItemType(StackItem item, ContractParameterType type) + { + StackItemType aType = item.Type; + if (aType == StackItemType.Pointer) return false; + switch (type) + { + case ContractParameterType.Any: + return true; + case ContractParameterType.Boolean: + return aType == StackItemType.Boolean; + case ContractParameterType.Integer: + return aType == StackItemType.Integer; + case ContractParameterType.ByteArray: + return aType is StackItemType.Any or StackItemType.ByteString or StackItemType.Buffer; + case ContractParameterType.String: + { + if (aType is StackItemType.ByteString or StackItemType.Buffer) + { + try + { + _ = Utility.StrictUTF8.GetString(item.GetSpan()); // Prevent any non-UTF8 string + return true; + } + catch { } + } + return false; + } + case ContractParameterType.Hash160: + if (aType == StackItemType.Any) return true; + if (aType != StackItemType.ByteString && aType != StackItemType.Buffer) return false; + return item.GetSpan().Length == UInt160.Length; + case ContractParameterType.Hash256: + if (aType == StackItemType.Any) return true; + if (aType != StackItemType.ByteString && aType != StackItemType.Buffer) return false; + return item.GetSpan().Length == UInt256.Length; + case ContractParameterType.PublicKey: + if (aType == StackItemType.Any) return true; + if (aType != StackItemType.ByteString && aType != StackItemType.Buffer) return false; + return item.GetSpan().Length == 33; + case ContractParameterType.Signature: + if (aType == StackItemType.Any) return true; + if (aType != StackItemType.ByteString && aType != StackItemType.Buffer) return false; + return item.GetSpan().Length == 64; + case ContractParameterType.Array: + return aType is StackItemType.Any or StackItemType.Array or StackItemType.Struct; + case ContractParameterType.Map: + return aType is StackItemType.Any or StackItemType.Map; + case ContractParameterType.InteropInterface: + return aType is StackItemType.Any or StackItemType.InteropInterface; + default: + return false; + } + } } } diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/Neo/SmartContract/ApplicationEngine.Storage.cs similarity index 88% rename from src/neo/SmartContract/ApplicationEngine.Storage.cs rename to src/Neo/SmartContract/ApplicationEngine.Storage.cs index 67f3645548..3a8bb9d122 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/Neo/SmartContract/ApplicationEngine.Storage.cs @@ -1,17 +1,18 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ApplicationEngine.Storage.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.Persistence; using Neo.SmartContract.Iterators; using Neo.SmartContract.Native; using System; -using System.Linq; namespace Neo.SmartContract { @@ -105,7 +106,7 @@ protected internal StorageContext GetReadOnlyContext() /// /// The storage context to convert. /// The readonly storage context. - internal protected static StorageContext AsReadOnly(StorageContext context) + protected internal static StorageContext AsReadOnly(StorageContext context) { if (!context.IsReadOnly) context = new StorageContext @@ -123,12 +124,12 @@ internal protected static StorageContext AsReadOnly(StorageContext context) /// The context of the storage. /// The key of the entry. /// The value of the entry. Or if the entry doesn't exist. - protected internal byte[] Get(StorageContext context, byte[] key) + protected internal ReadOnlyMemory? Get(StorageContext context, byte[] key) { return Snapshot.TryGet(new StorageKey { Id = context.Id, - Key = key.ToArray() + Key = key })?.Value; } @@ -153,7 +154,8 @@ protected internal IIterator Find(StorageContext context, byte[] prefix, FindOpt if ((options.HasFlag(FindOptions.PickField0) || options.HasFlag(FindOptions.PickField1)) && !options.HasFlag(FindOptions.DeserializeValues)) throw new ArgumentException(null, nameof(options)); byte[] prefix_key = StorageKey.CreateSearchPrefix(context.Id, prefix); - return new StorageIterator(Snapshot.Find(prefix_key).GetEnumerator(), prefix.Length, options, ReferenceCounter); + SeekDirection direction = options.HasFlag(FindOptions.Backwards) ? SeekDirection.Backward : SeekDirection.Forward; + return new StorageIterator(Snapshot.Find(prefix_key, direction).GetEnumerator(), prefix.Length, options); } /// @@ -165,8 +167,9 @@ protected internal IIterator Find(StorageContext context, byte[] prefix, FindOpt /// The value of the entry. protected internal void Put(StorageContext context, byte[] key, byte[] value) { - if (key.Length > MaxStorageKeySize || value.Length > MaxStorageValueSize || context.IsReadOnly) - throw new ArgumentException(); + if (key.Length > MaxStorageKeySize) throw new ArgumentException("Key length too big", nameof(key)); + if (value.Length > MaxStorageValueSize) throw new ArgumentException("Value length too big", nameof(value)); + if (context.IsReadOnly) throw new ArgumentException("StorageContext is readonly", nameof(context)); int newDataSize; StorageKey skey = new() diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/Neo/SmartContract/ApplicationEngine.cs similarity index 74% rename from src/neo/SmartContract/ApplicationEngine.cs rename to src/Neo/SmartContract/ApplicationEngine.cs index 79c48c1a44..2b78f62fe4 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/Neo/SmartContract/ApplicationEngine.cs @@ -1,19 +1,18 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ApplicationEngine.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.IO; -using Neo.IO.Caching; -using Neo.IO.Json; +using Neo.Json; using Neo.Network.P2P.Payloads; using Neo.Persistence; -using Neo.Plugins; using Neo.SmartContract.Manifest; using Neo.SmartContract.Native; using Neo.VM; @@ -23,7 +22,6 @@ using System.Linq; using System.Numerics; using System.Reflection; -using static System.Threading.Interlocked; using Array = System.Array; using VMArray = Neo.VM.Types.Array; @@ -34,6 +32,8 @@ namespace Neo.SmartContract /// public partial class ApplicationEngine : ExecutionEngine { + private static readonly JumpTable DefaultJumpTable = ComposeDefaultJumpTable(); + /// /// The maximum cost that can be spent when a contract is executed in test mode. /// @@ -49,19 +49,23 @@ public partial class ApplicationEngine : ExecutionEngine /// public static event EventHandler Log; - private static IApplicationEngineProvider applicationEngineProvider; private static Dictionary services; - private TreeNode currentNodeOfInvocationTree = null; private readonly long gas_amount; private Dictionary states; + private readonly DataCache originalSnapshot; private List notifications; private List disposables; private readonly Dictionary invocationCounter = new(); private readonly Dictionary contractTasks = new(); - private readonly uint exec_fee_factor; + internal readonly uint ExecFeeFactor; internal readonly uint StoragePrice; private byte[] nonceData; + /// + /// Gets or sets the provider used to create the . + /// + public static IApplicationEngineProvider Provider { get; set; } + /// /// Gets the descriptors of all interoperable services available in NEO. /// @@ -70,7 +74,7 @@ public partial class ApplicationEngine : ExecutionEngine /// /// The diagnostic used by the engine. This property can be . /// - public Diagnostic Diagnostic { get; } + public IDiagnostic Diagnostic { get; } private List Disposables => disposables ??= new List(); @@ -87,7 +91,7 @@ public partial class ApplicationEngine : ExecutionEngine /// /// The snapshot used to read or write data. /// - public DataCache Snapshot { get; } + public DataCache Snapshot => CurrentContext?.GetState().Snapshot ?? originalSnapshot; /// /// The block being persisted. This field could be if the is . @@ -122,12 +126,20 @@ public partial class ApplicationEngine : ExecutionEngine /// /// The script hash of the calling contract. This field could be if the current context is the entry context. /// - public UInt160 CallingScriptHash => CurrentContext?.GetState().CallingScriptHash; + public virtual UInt160 CallingScriptHash + { + get + { + if (CurrentContext is null) return null; + var state = CurrentContext.GetState(); + return state.NativeCallingScriptHash ?? state.CallingContext?.GetState().ScriptHash; + } + } /// /// The script hash of the entry context. This field could be if no context is loaded to the engine. /// - public UInt160 EntryScriptHash => EntryContext?.GetScriptHash(); + public virtual UInt160 EntryScriptHash => EntryContext?.GetScriptHash(); /// /// The notifications sent during the execution. @@ -144,17 +156,21 @@ public partial class ApplicationEngine : ExecutionEngine /// The used by the engine. /// The maximum gas used in this execution. The execution will fail when the gas is exhausted. /// The diagnostic to be used by the . - protected unsafe ApplicationEngine(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, ProtocolSettings settings, long gas, Diagnostic diagnostic) + /// The jump table to be used by the . + protected unsafe ApplicationEngine( + TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, + ProtocolSettings settings, long gas, IDiagnostic diagnostic, JumpTable jumpTable = null) + : base(jumpTable ?? DefaultJumpTable) { this.Trigger = trigger; this.ScriptContainer = container; - this.Snapshot = snapshot; + this.originalSnapshot = snapshot; this.PersistingBlock = persistingBlock; this.ProtocolSettings = settings; this.gas_amount = gas; this.Diagnostic = diagnostic; - this.exec_fee_factor = snapshot is null || persistingBlock?.Index == 0 ? PolicyContract.DefaultExecFeeFactor : NativeContract.Policy.GetExecFeeFactor(Snapshot); - this.StoragePrice = snapshot is null || persistingBlock?.Index == 0 ? PolicyContract.DefaultStoragePrice : NativeContract.Policy.GetStoragePrice(Snapshot); + this.ExecFeeFactor = snapshot is null || persistingBlock?.Index == 0 ? PolicyContract.DefaultExecFeeFactor : NativeContract.Policy.GetExecFeeFactor(snapshot); + this.StoragePrice = snapshot is null || persistingBlock?.Index == 0 ? PolicyContract.DefaultStoragePrice : NativeContract.Policy.GetStoragePrice(snapshot); this.nonceData = container is Transaction tx ? tx.Hash.ToArray()[..16] : new byte[16]; if (persistingBlock is not null) { @@ -163,8 +179,61 @@ protected unsafe ApplicationEngine(TriggerType trigger, IVerifiable container, D *(ulong*)p ^= persistingBlock.Nonce; } } + diagnostic?.Initialized(this); + } + + #region JumpTable + + private static JumpTable ComposeDefaultJumpTable() + { + var table = new JumpTable(); + + table[OpCode.SYSCALL] = OnSysCall; + table[OpCode.CALLT] = OnCallT; + + return table; + } + + private static void OnCallT(ExecutionEngine engine, Instruction instruction) + { + if (engine is ApplicationEngine app) + { + uint tokenId = instruction.TokenU16; + + app.ValidateCallFlags(CallFlags.ReadStates | CallFlags.AllowCall); + ContractState contract = app.CurrentContext.GetState().Contract; + if (contract is null || tokenId >= contract.Nef.Tokens.Length) + throw new InvalidOperationException(); + MethodToken token = contract.Nef.Tokens[tokenId]; + if (token.ParametersCount > app.CurrentContext.EvaluationStack.Count) + throw new InvalidOperationException(); + StackItem[] args = new StackItem[token.ParametersCount]; + for (int i = 0; i < token.ParametersCount; i++) + args[i] = app.Pop(); + app.CallContractInternal(token.Hash, token.Method, token.CallFlags, token.HasReturnValue, args); + } + else + { + throw new InvalidOperationException(); + } } + private static void OnSysCall(ExecutionEngine engine, Instruction instruction) + { + if (engine is ApplicationEngine app) + { + uint method = instruction.TokenU32; + + app.OnSysCall(services[method]); + } + else + { + throw new InvalidOperationException(); + } + } + + #endregion + /// /// Adds GAS to and checks if it has exceeded the maximum limit. /// @@ -179,6 +248,7 @@ protected internal void AddGas(long gas) protected override void OnFault(Exception ex) { FaultException = ex; + notifications = null; base.OnFault(ex); } @@ -209,7 +279,7 @@ private ExecutionContext CallContractInternal(ContractState contract, ContractMe { ContractState currentContract = NativeContract.ContractManagement.GetContract(Snapshot, CurrentScriptHash); if (currentContract?.CanCall(contract, method.Name) == false) - throw new InvalidOperationException($"Cannot Call Method {method} Of Contract {contract.Hash} From Contract {CurrentScriptHash}"); + throw new InvalidOperationException($"Cannot Call Method {method.Name} Of Contract {contract.Hash} From Contract {CurrentScriptHash}"); } if (invocationCounter.TryGetValue(contract.Hash, out var counter)) @@ -221,15 +291,15 @@ private ExecutionContext CallContractInternal(ContractState contract, ContractMe invocationCounter[contract.Hash] = 1; } - ExecutionContextState state = CurrentContext.GetState(); - UInt160 callingScriptHash = state.ScriptHash; + ExecutionContext currentContext = CurrentContext; + ExecutionContextState state = currentContext.GetState(); CallFlags callingFlags = state.CallFlags; if (args.Count != method.Parameters.Length) throw new InvalidOperationException($"Method {method} Expects {method.Parameters.Length} Arguments But Receives {args.Count} Arguments"); if (hasReturnValue ^ (method.ReturnType != ContractParameterType.Void)) throw new InvalidOperationException("The return value type does not match."); ExecutionContext context_new = LoadContract(contract, method, flags & callingFlags); state = context_new.GetState(); - state.CallingScriptHash = callingScriptHash; + state.CallingContext = currentContext; for (int i = args.Count - 1; i >= 0; i--) context_new.EvaluationStack.Push(args[i]); @@ -241,7 +311,7 @@ internal ContractTask CallFromNativeContract(UInt160 callingScriptHash, UInt160 { ExecutionContext context_new = CallContractInternal(hash, method, CallFlags.All, false, args); ExecutionContextState state = context_new.GetState(); - state.CallingScriptHash = callingScriptHash; + state.NativeCallingScriptHash = callingScriptHash; ContractTask task = new(); contractTasks.Add(context_new, task.GetAwaiter()); return task; @@ -251,21 +321,47 @@ internal ContractTask CallFromNativeContract(UInt160 callingScriptHash, UI { ExecutionContext context_new = CallContractInternal(hash, method, CallFlags.All, true, args); ExecutionContextState state = context_new.GetState(); - state.CallingScriptHash = callingScriptHash; + state.NativeCallingScriptHash = callingScriptHash; ContractTask task = new(); contractTasks.Add(context_new, task.GetAwaiter()); return task; } - protected override void ContextUnloaded(ExecutionContext context) + internal override void UnloadContext(ExecutionContext context) { - base.ContextUnloaded(context); - if (Diagnostic is not null) - currentNodeOfInvocationTree = currentNodeOfInvocationTree.Parent; - if (!contractTasks.Remove(context, out var awaiter)) return; - if (UncaughtException is not null) - throw new VMUnhandledException(UncaughtException); - awaiter.SetResult(this); + base.UnloadContext(context); + if (context.Script != CurrentContext?.Script) + { + ExecutionContextState state = context.GetState(); + if (UncaughtException is null) + { + state.Snapshot?.Commit(); + if (CurrentContext != null) + { + ExecutionContextState contextState = CurrentContext.GetState(); + contextState.NotificationCount += state.NotificationCount; + if (state.IsDynamicCall) + { + if (context.EvaluationStack.Count == 0) + Push(StackItem.Null); + else if (context.EvaluationStack.Count > 1) + throw new NotSupportedException("Multiple return values are not allowed in cross-contract calls."); + } + } + } + else + { + if (state.NotificationCount > 0) + notifications.RemoveRange(notifications.Count - state.NotificationCount, state.NotificationCount); + } + } + Diagnostic?.ContextUnloaded(context); + if (contractTasks.Remove(context, out var awaiter)) + { + if (UncaughtException is not null) + throw new VMUnhandledException(UncaughtException); + awaiter.SetResult(this); + } } /// @@ -279,28 +375,23 @@ protected override void ContextUnloaded(ExecutionContext context) /// The maximum gas used in this execution. The execution will fail when the gas is exhausted. /// The diagnostic to be used by the . /// The engine instance created. - public static ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock = null, ProtocolSettings settings = null, long gas = TestModeGas, Diagnostic diagnostic = null) + public static ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock = null, ProtocolSettings settings = null, long gas = TestModeGas, IDiagnostic diagnostic = null) { - return applicationEngineProvider?.Create(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic) - ?? new ApplicationEngine(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic); + // Adjust jump table according persistingBlock + var jumpTable = ApplicationEngine.DefaultJumpTable; + + return Provider?.Create(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic, jumpTable) + ?? new ApplicationEngine(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic, jumpTable); } - protected override void LoadContext(ExecutionContext context) + internal override void LoadContext(ExecutionContext context) { // Set default execution context state var state = context.GetState(); - state.ScriptHash ??= ((byte[])context.Script).ToScriptHash(); + state.ScriptHash ??= ((ReadOnlyMemory)context.Script).Span.ToScriptHash(); invocationCounter.TryAdd(state.ScriptHash, 1); - - if (Diagnostic is not null) - { - if (currentNodeOfInvocationTree is null) - currentNodeOfInvocationTree = Diagnostic.InvocationTree.AddRoot(state.ScriptHash); - else - currentNodeOfInvocationTree = currentNodeOfInvocationTree.AddChild(state.ScriptHash); - } - base.LoadContext(context); + Diagnostic?.ContextLoaded(context); } /// @@ -351,27 +442,15 @@ public ExecutionContext LoadScript(Script script, int rvcount = -1, int initialP { // Create and configure context ExecutionContext context = CreateContext(script, rvcount, initialPosition); - configureState?.Invoke(context.GetState()); + ExecutionContextState state = context.GetState(); + state.Snapshot = Snapshot?.CreateSnapshot(); + configureState?.Invoke(state); + // Load context LoadContext(context); return context; } - protected override ExecutionContext LoadToken(ushort tokenId) - { - ValidateCallFlags(CallFlags.ReadStates | CallFlags.AllowCall); - ContractState contract = CurrentContext.GetState().Contract; - if (contract is null || tokenId >= contract.Nef.Tokens.Length) - throw new InvalidOperationException(); - MethodToken token = contract.Nef.Tokens[tokenId]; - if (token.ParametersCount > CurrentContext.EvaluationStack.Count) - throw new InvalidOperationException(); - StackItem[] args = new StackItem[token.ParametersCount]; - for (int i = 0; i < token.ParametersCount; i++) - args[i] = Pop(); - return CallContractInternal(token.Hash, token.Method, token.CallFlags, token.HasReturnValue, args); - } - /// /// Converts an to a that used in the virtual machine. /// @@ -394,6 +473,7 @@ protected internal StackItem Convert(object value) ulong i => i, Enum e => Convert(System.Convert.ChangeType(e, e.GetTypeCode())), byte[] data => data, + ReadOnlyMemory m => m, string s => s, BigInteger i => i, JObject o => o.ToByteArray(false), @@ -447,6 +527,7 @@ protected internal object Convert(StackItem item, InteropParameterDescriptor des public override void Dispose() { + Diagnostic?.Disposed(); if (disposables != null) { foreach (IDisposable disposable in disposables) @@ -467,11 +548,6 @@ internal protected void ValidateCallFlags(CallFlags requiredCallFlags) throw new InvalidOperationException($"Cannot call this SYSCALL with the flag {state.CallFlags}."); } - protected override void OnSysCall(uint method) - { - OnSysCall(services[method]); - } - /// /// Invokes the specified interoperable service. /// @@ -479,7 +555,7 @@ protected override void OnSysCall(uint method) protected virtual void OnSysCall(InteropDescriptor descriptor) { ValidateCallFlags(descriptor.RequiredCallFlags); - AddGas(descriptor.FixedPrice * exec_fee_factor); + AddGas(descriptor.FixedPrice * ExecFeeFactor); object[] parameters = new object[descriptor.Parameters.Count]; for (int i = 0; i < parameters.Length; i++) @@ -490,10 +566,16 @@ protected virtual void OnSysCall(InteropDescriptor descriptor) Push(Convert(returnValue)); } - protected override void PreExecuteInstruction() + protected override void PreExecuteInstruction(Instruction instruction) + { + Diagnostic?.PreExecuteInstruction(instruction); + AddGas(ExecFeeFactor * OpCodePriceTable[(byte)instruction.OpCode]); + } + + protected override void PostExecuteInstruction(Instruction instruction) { - if (CurrentContext.InstructionPointer < CurrentContext.Script.Length) - AddGas(exec_fee_factor * OpCodePrices[CurrentContext.CurrentInstruction.OpCode]); + base.PostExecuteInstruction(instruction); + Diagnostic?.PostExecuteInstruction(instruction); } private static Block CreateDummyBlock(DataCache snapshot, ProtocolSettings settings) @@ -536,11 +618,6 @@ private static InteropDescriptor Register(string name, string handler, long fixe return descriptor; } - internal static void ResetApplicationEngineProvider() - { - Exchange(ref applicationEngineProvider, null); - } - /// /// Creates a new instance of the class, and use it to run the specified script. /// @@ -553,7 +630,7 @@ internal static void ResetApplicationEngineProvider() /// The maximum gas used in this execution. The execution will fail when the gas is exhausted. /// The diagnostic to be used by the . /// The engine instance created. - public static ApplicationEngine Run(byte[] script, DataCache snapshot, IVerifiable container = null, Block persistingBlock = null, ProtocolSettings settings = null, int offset = 0, long gas = TestModeGas, Diagnostic diagnostic = null) + public static ApplicationEngine Run(ReadOnlyMemory script, DataCache snapshot, IVerifiable container = null, Block persistingBlock = null, ProtocolSettings settings = null, int offset = 0, long gas = TestModeGas, IDiagnostic diagnostic = null) { persistingBlock ??= CreateDummyBlock(snapshot, settings ?? ProtocolSettings.Default); ApplicationEngine engine = Create(TriggerType.Application, container, snapshot, persistingBlock, settings, gas, diagnostic); @@ -562,11 +639,6 @@ public static ApplicationEngine Run(byte[] script, DataCache snapshot, IVerifiab return engine; } - internal static bool SetApplicationEngineProvider(IApplicationEngineProvider provider) - { - return CompareExchange(ref applicationEngineProvider, provider, null) is null; - } - public T GetState() { if (states is null) return default; @@ -574,10 +646,38 @@ public T GetState() return (T)state; } + public T GetState(Func factory) + { + if (states is null) + { + T state = factory(); + SetState(state); + return state; + } + else + { + if (!states.TryGetValue(typeof(T), out object state)) + { + state = factory(); + SetState(state); + } + return (T)state; + } + } + public void SetState(T state) { states ??= new Dictionary(); states[typeof(T)] = state; } + + public bool IsHardforkEnabled(Hardfork hardfork) + { + // Return true if PersistingBlock is null and Hardfork is enabled + if (PersistingBlock is null) + return ProtocolSettings.Hardforks.ContainsKey(hardfork); + + return ProtocolSettings.IsHardforkEnabled(hardfork, PersistingBlock.Index); + } } } diff --git a/src/neo/SmartContract/BinarySerializer.cs b/src/Neo/SmartContract/BinarySerializer.cs similarity index 74% rename from src/neo/SmartContract/BinarySerializer.cs rename to src/Neo/SmartContract/BinarySerializer.cs index 596cd57b3b..2bdeced678 100644 --- a/src/neo/SmartContract/BinarySerializer.cs +++ b/src/Neo/SmartContract/BinarySerializer.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// BinarySerializer.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. @@ -52,39 +53,33 @@ public ContainerPlaceholder(StackItemType type, int count) /// The limits for the deserialization. /// The used by the . /// The deserialized . - public static StackItem Deserialize(byte[] data, ExecutionEngineLimits limits, ReferenceCounter referenceCounter = null) + public static StackItem Deserialize(ReadOnlyMemory data, ExecutionEngineLimits limits, ReferenceCounter referenceCounter = null) { - using MemoryStream ms = new(data, false); - using BinaryReader reader = new(ms, Utility.StrictUTF8, true); - return Deserialize(reader, limits with { MaxItemSize = (uint)data.Length }, referenceCounter); + MemoryReader reader = new(data); + return Deserialize(ref reader, (uint)Math.Min(data.Length, limits.MaxItemSize), limits.MaxStackSize, referenceCounter); } /// - /// Deserializes a from byte array. + /// Deserializes a from . /// - /// The byte array to parse. + /// The for reading data. /// The limits for the deserialization. /// The used by the . /// The deserialized . - public static unsafe StackItem Deserialize(ReadOnlySpan data, ExecutionEngineLimits limits, ReferenceCounter referenceCounter = null) + public static StackItem Deserialize(ref MemoryReader reader, ExecutionEngineLimits limits, ReferenceCounter referenceCounter = null) { - if (data.IsEmpty) throw new FormatException(); - fixed (byte* pointer = data) - { - using UnmanagedMemoryStream ms = new(pointer, data.Length); - using BinaryReader reader = new(ms, Utility.StrictUTF8, true); - return Deserialize(reader, limits with { MaxItemSize = (uint)data.Length }, referenceCounter); - } + return Deserialize(ref reader, limits.MaxItemSize, limits.MaxStackSize, referenceCounter); } /// - /// Deserializes a from . + /// Deserializes a from . /// - /// The for reading data. - /// The limits for the deserialization. + /// The for reading data. + /// The maximum size of the result. + /// The max of items to serialize /// The used by the . /// The deserialized . - public static StackItem Deserialize(BinaryReader reader, ExecutionEngineLimits limits, ReferenceCounter referenceCounter) + public static StackItem Deserialize(ref MemoryReader reader, uint maxSize, uint maxItems, ReferenceCounter referenceCounter = null) { Stack deserialized = new(); int undeserialized = 1; @@ -100,27 +95,26 @@ public static StackItem Deserialize(BinaryReader reader, ExecutionEngineLimits l deserialized.Push(reader.ReadBoolean()); break; case StackItemType.Integer: - deserialized.Push(new BigInteger(reader.ReadVarBytes(Integer.MaxSize))); + deserialized.Push(new BigInteger(reader.ReadVarMemory(Integer.MaxSize).Span)); break; case StackItemType.ByteString: - deserialized.Push(reader.ReadVarBytes((int)limits.MaxItemSize)); + deserialized.Push(reader.ReadVarMemory((int)maxSize)); break; case StackItemType.Buffer: - Buffer buffer = new((int)reader.ReadVarInt(limits.MaxItemSize)); - reader.FillBuffer(buffer.InnerBuffer); - deserialized.Push(buffer); + ReadOnlyMemory memory = reader.ReadVarMemory((int)maxSize); + deserialized.Push(new Buffer(memory.Span)); break; case StackItemType.Array: case StackItemType.Struct: { - int count = (int)reader.ReadVarInt(limits.MaxStackSize); + int count = (int)reader.ReadVarInt(maxItems); deserialized.Push(new ContainerPlaceholder(type, count)); undeserialized += count; } break; case StackItemType.Map: { - int count = (int)reader.ReadVarInt(limits.MaxStackSize); + int count = (int)reader.ReadVarInt(maxItems); deserialized.Push(new ContainerPlaceholder(type, count)); undeserialized += count * 2; } @@ -128,7 +122,8 @@ public static StackItem Deserialize(BinaryReader reader, ExecutionEngineLimits l default: throw new FormatException(); } - if (deserialized.Count > limits.MaxStackSize) throw new FormatException(); + if (deserialized.Count > maxItems) + throw new FormatException(); } Stack stack_temp = new(); while (deserialized.Count > 0) @@ -167,17 +162,29 @@ public static StackItem Deserialize(BinaryReader reader, ExecutionEngineLimits l return stack_temp.Peek(); } + /// + /// Serializes a to byte array. + /// + /// The to be serialized. + /// The used to ensure the limits. + /// The serialized byte array. + public static byte[] Serialize(StackItem item, ExecutionEngineLimits limits) + { + return Serialize(item, limits.MaxItemSize, limits.MaxStackSize); + } + /// /// Serializes a to byte array. /// /// The to be serialized. /// The maximum size of the result. + /// The max of items to serialize /// The serialized byte array. - public static byte[] Serialize(StackItem item, uint maxSize) + public static byte[] Serialize(StackItem item, long maxSize, long maxItems) { using MemoryStream ms = new(); using BinaryWriter writer = new(ms, Utility.StrictUTF8, true); - Serialize(writer, item, maxSize); + Serialize(writer, item, maxSize, maxItems); writer.Flush(); return ms.ToArray(); } @@ -188,13 +195,16 @@ public static byte[] Serialize(StackItem item, uint maxSize) /// The for writing data. /// The to be serialized. /// The maximum size of the result. - public static void Serialize(BinaryWriter writer, StackItem item, uint maxSize) + /// The max of items to serialize + public static void Serialize(BinaryWriter writer, StackItem item, long maxSize, long maxItems) { HashSet serialized = new(ReferenceEqualityComparer.Instance); Stack unserialized = new(); unserialized.Push(item); while (unserialized.Count > 0) { + if (--maxItems < 0) + throw new FormatException(); item = unserialized.Pop(); writer.Write((byte)item.Type); switch (item) diff --git a/src/neo/SmartContract/CallFlags.cs b/src/Neo/SmartContract/CallFlags.cs similarity index 83% rename from src/neo/SmartContract/CallFlags.cs rename to src/Neo/SmartContract/CallFlags.cs index 55c07c4b44..eb0d0de92d 100644 --- a/src/neo/SmartContract/CallFlags.cs +++ b/src/Neo/SmartContract/CallFlags.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// CallFlags.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. diff --git a/src/neo/SmartContract/Contract.cs b/src/Neo/SmartContract/Contract.cs similarity index 94% rename from src/neo/SmartContract/Contract.cs rename to src/Neo/SmartContract/Contract.cs index c6923d7775..dc60c42d50 100644 --- a/src/neo/SmartContract/Contract.cs +++ b/src/Neo/SmartContract/Contract.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Contract.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. diff --git a/src/neo/SmartContract/ContractParameter.cs b/src/Neo/SmartContract/ContractParameter.cs similarity index 95% rename from src/neo/SmartContract/ContractParameter.cs rename to src/Neo/SmartContract/ContractParameter.cs index 0c4a79f518..027fa50e00 100644 --- a/src/neo/SmartContract/ContractParameter.cs +++ b/src/Neo/SmartContract/ContractParameter.cs @@ -1,15 +1,16 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractParameter.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.Cryptography.ECC; -using Neo.IO.Json; +using Neo.Json; using System; using System.Collections.Generic; using System.Linq; @@ -83,8 +84,8 @@ public static ContractParameter FromJson(JObject json) ContractParameterType.Hash256 => UInt256.Parse(json["value"].AsString()), ContractParameterType.PublicKey => ECPoint.Parse(json["value"].AsString(), ECCurve.Secp256r1), ContractParameterType.String => json["value"].AsString(), - ContractParameterType.Array => ((JArray)json["value"]).Select(p => FromJson(p)).ToList(), - ContractParameterType.Map => ((JArray)json["value"]).Select(p => new KeyValuePair(FromJson(p["key"]), FromJson(p["value"]))).ToList(), + ContractParameterType.Array => ((JArray)json["value"]).Select(p => FromJson((JObject)p)).ToList(), + ContractParameterType.Map => ((JArray)json["value"]).Select(p => new KeyValuePair(FromJson((JObject)p["key"]), FromJson((JObject)p["value"]))).ToList(), _ => throw new ArgumentException(null, nameof(json)), }; return parameter; diff --git a/src/neo/SmartContract/ContractParameterType.cs b/src/Neo/SmartContract/ContractParameterType.cs similarity index 86% rename from src/neo/SmartContract/ContractParameterType.cs rename to src/Neo/SmartContract/ContractParameterType.cs index 989fd0a7d1..037927a7f0 100644 --- a/src/neo/SmartContract/ContractParameterType.cs +++ b/src/Neo/SmartContract/ContractParameterType.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractParameterType.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. diff --git a/src/neo/SmartContract/ContractParametersContext.cs b/src/Neo/SmartContract/ContractParametersContext.cs similarity index 92% rename from src/neo/SmartContract/ContractParametersContext.cs rename to src/Neo/SmartContract/ContractParametersContext.cs index 0ff5da9f6f..f6a18e081d 100644 --- a/src/neo/SmartContract/ContractParametersContext.cs +++ b/src/Neo/SmartContract/ContractParametersContext.cs @@ -1,15 +1,17 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractParametersContext.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.Cryptography.ECC; -using Neo.IO.Json; +using Neo.IO; +using Neo.Json; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.VM; @@ -18,6 +20,7 @@ using System.IO; using System.Linq; using System.Reflection; +using static Neo.SmartContract.Helper; namespace Neo.SmartContract { @@ -42,8 +45,8 @@ public ContextItem(Contract contract) public ContextItem(JObject json) { this.Script = Convert.FromBase64String(json["script"].AsString()); - this.Parameters = ((JArray)json["parameters"]).Select(p => ContractParameter.FromJson(p)).ToArray(); - this.Signatures = json["signatures"].Properties.Select(p => new + this.Parameters = ((JArray)json["parameters"]).Select(p => ContractParameter.FromJson((JObject)p)).ToArray(); + this.Signatures = ((JObject)json["signatures"]).Properties.Select(p => new { PublicKey = ECPoint.Parse(p.Key, ECCurve.Secp256r1), Signature = Convert.FromBase64String(p.Value.AsString()) @@ -153,7 +156,7 @@ public bool Add(Contract contract, params object[] parameters) /// if the signature is added successfully; otherwise, . public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) { - if (contract.Script.IsMultiSigContract(out _, out ECPoint[] points)) + if (IsMultiSigContract(contract.Script, out _, out ECPoint[] points)) { if (!points.Contains(pubkey)) return false; ContextItem item = CreateItem(contract); @@ -191,7 +194,7 @@ public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) if (index == -1) { - // unable to find ContractParameterType.Signature in contract.ParameterList + // unable to find ContractParameterType.Signature in contract.ParameterList // return now to prevent array index out of bounds exception return false; } @@ -227,20 +230,18 @@ public static ContractParametersContext FromJson(JObject json, DataCache snapsho if (!typeof(IVerifiable).IsAssignableFrom(type)) throw new FormatException(); var verifiable = (IVerifiable)Activator.CreateInstance(type); - using (MemoryStream ms = new(Convert.FromBase64String(json["data"].AsString()), false)) - using (BinaryReader reader = new(ms, Utility.StrictUTF8)) - { - verifiable.DeserializeUnsigned(reader); - } + byte[] data = Convert.FromBase64String(json["data"].AsString()); + MemoryReader reader = new(data); + verifiable.DeserializeUnsigned(ref reader); if (json.ContainsProperty("hash")) { UInt256 hash = UInt256.Parse(json["hash"].GetString()); if (hash != verifiable.Hash) throw new FormatException(); } ContractParametersContext context = new(snapshot, verifiable, (uint)json["network"].GetInt32()); - foreach (var property in json["items"].Properties) + foreach (var (key, value) in ((JObject)json["items"]).Properties) { - context.ContextItems.Add(UInt160.Parse(property.Key), new ContextItem(property.Value)); + context.ContextItems.Add(UInt160.Parse(key), new ContextItem((JObject)value)); } return context; } @@ -326,7 +327,7 @@ public Witness[] GetWitnesses() /// The parsed context. public static ContractParametersContext Parse(string value, DataCache snapshot) { - return FromJson(JObject.Parse(value), snapshot); + return FromJson((JObject)JToken.Parse(value), snapshot); } /// diff --git a/src/neo/SmartContract/ContractState.cs b/src/Neo/SmartContract/ContractState.cs similarity index 72% rename from src/neo/SmartContract/ContractState.cs rename to src/Neo/SmartContract/ContractState.cs index 06ba271bba..83ac9d3353 100644 --- a/src/neo/SmartContract/ContractState.cs +++ b/src/Neo/SmartContract/ContractState.cs @@ -1,18 +1,20 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractState.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.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract.Manifest; using Neo.VM; using Neo.VM.Types; +using System; using System.Linq; using Array = Neo.VM.Types.Array; @@ -51,7 +53,29 @@ public class ContractState : IInteroperable /// /// The script of the contract. /// - public byte[] Script => Nef.Script; + public ReadOnlyMemory Script => Nef.Script; + + IInteroperable IInteroperable.Clone() + { + return new ContractState + { + Id = Id, + UpdateCounter = UpdateCounter, + Hash = Hash, + Nef = Nef, + Manifest = Manifest + }; + } + + void IInteroperable.FromReplica(IInteroperable replica) + { + ContractState from = (ContractState)replica; + Id = from.Id; + UpdateCounter = from.UpdateCounter; + Hash = from.Hash; + Nef = from.Nef; + Manifest = from.Manifest; + } void IInteroperable.FromStackItem(StackItem stackItem) { @@ -59,7 +83,7 @@ void IInteroperable.FromStackItem(StackItem stackItem) Id = (int)array[0].GetInteger(); UpdateCounter = (ushort)array[1].GetInteger(); Hash = new UInt160(array[2].GetSpan()); - Nef = array[3].GetSpan().AsSerializable(); + Nef = ((ByteString)array[3]).Memory.AsSerializable(); Manifest = array[4].ToInteroperable(); } diff --git a/src/neo/SmartContract/ContractTask.cs b/src/Neo/SmartContract/ContractTask.cs similarity index 61% rename from src/neo/SmartContract/ContractTask.cs rename to src/Neo/SmartContract/ContractTask.cs index 130dab87c0..6df3627c37 100644 --- a/src/neo/SmartContract/ContractTask.cs +++ b/src/Neo/SmartContract/ContractTask.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractTask.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. @@ -31,19 +32,15 @@ public ContractTask() } protected virtual ContractTaskAwaiter CreateAwaiter() => new(); - public virtual ContractTaskAwaiter GetAwaiter() => awaiter; - public virtual object GetResult() => null; } [AsyncMethodBuilder(typeof(ContractTaskMethodBuilder<>))] class ContractTask : ContractTask { - protected override ContractTaskAwaiter CreateAwaiter() => new(); - - public override ContractTaskAwaiter GetAwaiter() => (ContractTaskAwaiter)base.GetAwaiter(); - - public override object GetResult() => GetAwaiter().GetResult(); + protected override ContractTaskAwaiter CreateAwaiter() => new ContractTaskAwaiter(); + public override ContractTaskAwaiter GetAwaiter() => (ContractTaskAwaiter)base.GetAwaiter(); + public override object GetResult() => ((ContractTaskAwaiter)GetAwaiter()).GetResult(); } } diff --git a/src/neo/SmartContract/ContractTaskAwaiter.cs b/src/Neo/SmartContract/ContractTaskAwaiter.cs similarity index 84% rename from src/neo/SmartContract/ContractTaskAwaiter.cs rename to src/Neo/SmartContract/ContractTaskAwaiter.cs index f990b43316..a0a0c9a1bb 100644 --- a/src/neo/SmartContract/ContractTaskAwaiter.cs +++ b/src/Neo/SmartContract/ContractTaskAwaiter.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractTaskAwaiter.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. diff --git a/src/neo/SmartContract/ContractTaskMethodBuilder.cs b/src/Neo/SmartContract/ContractTaskMethodBuilder.cs similarity index 87% rename from src/neo/SmartContract/ContractTaskMethodBuilder.cs rename to src/Neo/SmartContract/ContractTaskMethodBuilder.cs index ba90455e6c..bae1146f25 100644 --- a/src/neo/SmartContract/ContractTaskMethodBuilder.cs +++ b/src/Neo/SmartContract/ContractTaskMethodBuilder.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractTaskMethodBuilder.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. @@ -70,7 +71,7 @@ public void SetException(Exception exception) public void SetResult(T result) { - Task.GetAwaiter().SetResult(result); + ((ContractTaskAwaiter)Task.GetAwaiter()).SetResult(result); } public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) diff --git a/src/neo/SmartContract/DeployedContract.cs b/src/Neo/SmartContract/DeployedContract.cs similarity index 80% rename from src/neo/SmartContract/DeployedContract.cs rename to src/Neo/SmartContract/DeployedContract.cs index 572ad60fca..0587a029e5 100644 --- a/src/neo/SmartContract/DeployedContract.cs +++ b/src/Neo/SmartContract/DeployedContract.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// DeployedContract.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. diff --git a/src/neo/SmartContract/ExecutionContextState.cs b/src/Neo/SmartContract/ExecutionContextState.cs similarity index 52% rename from src/neo/SmartContract/ExecutionContextState.cs rename to src/Neo/SmartContract/ExecutionContextState.cs index 84f414d100..8b61ff1197 100644 --- a/src/neo/SmartContract/ExecutionContextState.cs +++ b/src/Neo/SmartContract/ExecutionContextState.cs @@ -1,13 +1,15 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ExecutionContextState.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.Persistence; using Neo.VM; namespace Neo.SmartContract @@ -23,9 +25,14 @@ public class ExecutionContextState public UInt160 ScriptHash { get; set; } /// - /// The script hash of the calling contract. + /// The calling context. /// - public UInt160 CallingScriptHash { get; set; } + public ExecutionContext CallingContext { get; set; } + + /// + /// The script hash of the calling native contract. Used in native contracts only. + /// + internal UInt160 NativeCallingScriptHash { get; set; } /// /// The of the current context. @@ -36,5 +43,11 @@ public class ExecutionContextState /// The of the current context. /// public CallFlags CallFlags { get; set; } = CallFlags.All; + + public DataCache Snapshot { get; set; } + + public int NotificationCount { get; set; } + + public bool IsDynamicCall { get; set; } } } diff --git a/src/neo/SmartContract/FindOptions.cs b/src/Neo/SmartContract/FindOptions.cs similarity index 78% rename from src/neo/SmartContract/FindOptions.cs rename to src/Neo/SmartContract/FindOptions.cs index 5e3eb7a4fd..28445c9142 100644 --- a/src/neo/SmartContract/FindOptions.cs +++ b/src/Neo/SmartContract/FindOptions.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// FindOptions.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. @@ -53,9 +54,14 @@ public enum FindOptions : byte /// PickField1 = 1 << 5, + /// + /// Indicates that results should be returned in backwards (descending) order. + /// + Backwards = 1 << 7, + /// /// This value is only for internal use, and shouldn't be used in smart contracts. /// - All = KeysOnly | RemovePrefix | ValuesOnly | DeserializeValues | PickField0 | PickField1 + All = KeysOnly | RemovePrefix | ValuesOnly | DeserializeValues | PickField0 | PickField1 | Backwards } } diff --git a/src/neo/SmartContract/Helper.cs b/src/Neo/SmartContract/Helper.cs similarity index 88% rename from src/neo/SmartContract/Helper.cs rename to src/Neo/SmartContract/Helper.cs index be26452c45..333ef77d7b 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/Neo/SmartContract/Helper.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// 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. @@ -38,8 +39,8 @@ public static class Helper /// /// The calculated cost. public static long SignatureContractCost() => - ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * 2 + - ApplicationEngine.OpCodePrices[OpCode.SYSCALL] + + ApplicationEngine.OpCodePriceTable[(byte)OpCode.PUSHDATA1] * 2 + + ApplicationEngine.OpCodePriceTable[(byte)OpCode.SYSCALL] + ApplicationEngine.CheckSigPrice; /// @@ -50,12 +51,12 @@ public static long SignatureContractCost() => /// The calculated cost. public static long MultiSignatureContractCost(int m, int n) { - long fee = ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * (m + n); + long fee = ApplicationEngine.OpCodePriceTable[(byte)OpCode.PUSHDATA1] * (m + n); using (ScriptBuilder sb = new()) - fee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(m).ToArray()[0]]; + fee += ApplicationEngine.OpCodePriceTable[(byte)(OpCode)sb.EmitPush(m).ToArray()[0]]; using (ScriptBuilder sb = new()) - fee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]]; - fee += ApplicationEngine.OpCodePrices[OpCode.SYSCALL]; + fee += ApplicationEngine.OpCodePriceTable[(byte)(OpCode)sb.EmitPush(n).ToArray()[0]]; + fee += ApplicationEngine.OpCodePriceTable[(byte)OpCode.SYSCALL]; fee += ApplicationEngine.CheckSigPrice * n; return fee; } @@ -117,7 +118,7 @@ public static UInt160 GetScriptHash(this ExecutionContext context) /// /// The script of the contract. /// if the contract is a multi-signature contract; otherwise, . - public static bool IsMultiSigContract(this byte[] script) + public static bool IsMultiSigContract(ReadOnlySpan script) { return IsMultiSigContract(script, out _, out _, null); } @@ -129,7 +130,7 @@ public static bool IsMultiSigContract(this byte[] script) /// The minimum number of correct signatures that need to be provided in order for the verification to pass. /// The number of public keys in the account. /// if the contract is a multi-signature contract; otherwise, . - public static bool IsMultiSigContract(this byte[] script, out int m, out int n) + public static bool IsMultiSigContract(ReadOnlySpan script, out int m, out int n) { return IsMultiSigContract(script, out m, out n, null); } @@ -141,7 +142,7 @@ public static bool IsMultiSigContract(this byte[] script, out int m, out int n) /// The minimum number of correct signatures that need to be provided in order for the verification to pass. /// The public keys in the account. /// if the contract is a multi-signature contract; otherwise, . - public static bool IsMultiSigContract(this byte[] script, out int m, out ECPoint[] points) + public static bool IsMultiSigContract(ReadOnlySpan script, out int m, out ECPoint[] points) { List list = new(); if (IsMultiSigContract(script, out m, out _, list)) @@ -156,7 +157,7 @@ public static bool IsMultiSigContract(this byte[] script, out int m, out ECPoint } } - private static bool IsMultiSigContract(byte[] script, out int m, out int n, List points) + private static bool IsMultiSigContract(ReadOnlySpan script, out int m, out int n, List points) { m = 0; n = 0; int i = 0; @@ -168,7 +169,7 @@ private static bool IsMultiSigContract(byte[] script, out int m, out int n, List ++i; break; case (byte)OpCode.PUSHINT16: - m = BinaryPrimitives.ReadUInt16LittleEndian(script.AsSpan(++i)); + m = BinaryPrimitives.ReadUInt16LittleEndian(script[++i..]); i += 2; break; case byte b when b >= (byte)OpCode.PUSH1 && b <= (byte)OpCode.PUSH16: @@ -183,7 +184,7 @@ private static bool IsMultiSigContract(byte[] script, out int m, out int n, List { if (script.Length <= i + 35) return false; if (script[++i] != 33) return false; - points?.Add(ECPoint.DecodePoint(script.AsSpan(i + 1, 33), ECCurve.Secp256r1)); + points?.Add(ECPoint.DecodePoint(script.Slice(i + 1, 33), ECCurve.Secp256r1)); i += 34; ++n; } @@ -195,7 +196,7 @@ private static bool IsMultiSigContract(byte[] script, out int m, out int n, List ++i; break; case (byte)OpCode.PUSHINT16: - if (script.Length < i + 3 || n != BinaryPrimitives.ReadUInt16LittleEndian(script.AsSpan(++i))) return false; + if (script.Length < i + 3 || n != BinaryPrimitives.ReadUInt16LittleEndian(script[++i..])) return false; i += 2; break; case byte b when b >= (byte)OpCode.PUSH1 && b <= (byte)OpCode.PUSH16: @@ -207,7 +208,7 @@ private static bool IsMultiSigContract(byte[] script, out int m, out int n, List } if (script.Length != i + 5) return false; if (script[i++] != (byte)OpCode.SYSCALL) return false; - if (BinaryPrimitives.ReadUInt32LittleEndian(script.AsSpan(i)) != ApplicationEngine.System_Crypto_CheckMultisig) + if (BinaryPrimitives.ReadUInt32LittleEndian(script[i..]) != ApplicationEngine.System_Crypto_CheckMultisig) return false; return true; } @@ -217,13 +218,13 @@ private static bool IsMultiSigContract(byte[] script, out int m, out int n, List /// /// The script of the contract. /// if the contract is a signature contract; otherwise, . - public static bool IsSignatureContract(this byte[] script) + public static bool IsSignatureContract(ReadOnlySpan script) { if (script.Length != 40) return false; if (script[0] != (byte)OpCode.PUSHDATA1 || script[1] != 33 || script[35] != (byte)OpCode.SYSCALL - || BinaryPrimitives.ReadUInt32LittleEndian(script.AsSpan(36)) != ApplicationEngine.System_Crypto_CheckSig) + || BinaryPrimitives.ReadUInt32LittleEndian(script[36..]) != ApplicationEngine.System_Crypto_CheckSig) return false; return true; } @@ -233,9 +234,9 @@ public static bool IsSignatureContract(this byte[] script) /// /// The script of the contract. /// if the contract is a standard contract; otherwise, . - public static bool IsStandardContract(this byte[] script) + public static bool IsStandardContract(ReadOnlySpan script) { - return script.IsSignatureContract() || script.IsMultiSigContract(); + return IsSignatureContract(script) || IsMultiSigContract(script); } /// diff --git a/src/neo/Plugins/IApplicationEngineProvider.cs b/src/Neo/SmartContract/IApplicationEngineProvider.cs similarity index 73% rename from src/neo/Plugins/IApplicationEngineProvider.cs rename to src/Neo/SmartContract/IApplicationEngineProvider.cs index 2e15d87a10..ba7866739c 100644 --- a/src/neo/Plugins/IApplicationEngineProvider.cs +++ b/src/Neo/SmartContract/IApplicationEngineProvider.cs @@ -1,18 +1,19 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// IApplicationEngineProvider.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.Network.P2P.Payloads; using Neo.Persistence; -using Neo.SmartContract; +using Neo.VM; -namespace Neo.Plugins +namespace Neo.SmartContract { /// /// A provider for creating instances. @@ -29,7 +30,8 @@ public interface IApplicationEngineProvider /// The used by the engine. /// The maximum gas used in this execution. The execution will fail when the gas is exhausted. /// The diagnostic to be used by the . + /// The jump table to be used by the . /// The engine instance created. - ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, ProtocolSettings settings, long gas, Diagnostic diagnostic); + ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, ProtocolSettings settings, long gas, IDiagnostic diagnostic, JumpTable jumpTable); } } diff --git a/src/Neo/SmartContract/IDiagnostic.cs b/src/Neo/SmartContract/IDiagnostic.cs new file mode 100644 index 0000000000..4c888b805a --- /dev/null +++ b/src/Neo/SmartContract/IDiagnostic.cs @@ -0,0 +1,25 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// IDiagnostic.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.VM; + +namespace Neo.SmartContract +{ + public interface IDiagnostic + { + void Initialized(ApplicationEngine engine); + void Disposed(); + void ContextLoaded(ExecutionContext context); + void ContextUnloaded(ExecutionContext context); + void PreExecuteInstruction(Instruction instruction); + void PostExecuteInstruction(Instruction instruction); + } +} diff --git a/src/neo/SmartContract/IInteroperable.cs b/src/Neo/SmartContract/IInteroperable.cs similarity index 57% rename from src/neo/SmartContract/IInteroperable.cs rename to src/Neo/SmartContract/IInteroperable.cs index b669887da1..5254898efe 100644 --- a/src/neo/SmartContract/IInteroperable.cs +++ b/src/Neo/SmartContract/IInteroperable.cs @@ -1,15 +1,17 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// IInteroperable.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.VM; using Neo.VM.Types; +using System.Runtime.Serialization; namespace Neo.SmartContract { @@ -30,5 +32,17 @@ public interface IInteroperable /// The used by the . /// The converted . StackItem ToStackItem(ReferenceCounter referenceCounter); + + public IInteroperable Clone() + { + IInteroperable result = (IInteroperable)FormatterServices.GetUninitializedObject(GetType()); + result.FromStackItem(ToStackItem(null)); + return result; + } + + public void FromReplica(IInteroperable replica) + { + FromStackItem(replica.ToStackItem(null)); + } } } diff --git a/src/neo/SmartContract/InteropDescriptor.cs b/src/Neo/SmartContract/InteropDescriptor.cs similarity index 86% rename from src/neo/SmartContract/InteropDescriptor.cs rename to src/Neo/SmartContract/InteropDescriptor.cs index be9805b35d..29c88f46ed 100644 --- a/src/neo/SmartContract/InteropDescriptor.cs +++ b/src/Neo/SmartContract/InteropDescriptor.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// InteropDescriptor.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. diff --git a/src/neo/SmartContract/InteropParameterDescriptor.cs b/src/Neo/SmartContract/InteropParameterDescriptor.cs similarity index 92% rename from src/neo/SmartContract/InteropParameterDescriptor.cs rename to src/Neo/SmartContract/InteropParameterDescriptor.cs index a58900fd58..6a65beb93f 100644 --- a/src/neo/SmartContract/InteropParameterDescriptor.cs +++ b/src/Neo/SmartContract/InteropParameterDescriptor.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// InteropParameterDescriptor.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. diff --git a/src/neo/SmartContract/Iterators/IIterator.cs b/src/Neo/SmartContract/Iterators/IIterator.cs similarity index 70% rename from src/neo/SmartContract/Iterators/IIterator.cs rename to src/Neo/SmartContract/Iterators/IIterator.cs index 4461937400..78c42f1abd 100644 --- a/src/neo/SmartContract/Iterators/IIterator.cs +++ b/src/Neo/SmartContract/Iterators/IIterator.cs @@ -1,13 +1,15 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// IIterator.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.VM; using Neo.VM.Types; using System; @@ -28,6 +30,6 @@ public interface IIterator : IDisposable /// Gets the element in the collection at the current position of the iterator. /// /// The element in the collection at the current position of the iterator. - StackItem Value(); + StackItem Value(ReferenceCounter referenceCounter); } } diff --git a/src/neo/SmartContract/Iterators/StorageIterator.cs b/src/Neo/SmartContract/Iterators/StorageIterator.cs similarity index 69% rename from src/neo/SmartContract/Iterators/StorageIterator.cs rename to src/Neo/SmartContract/Iterators/StorageIterator.cs index 628719a1e3..397333ad7c 100644 --- a/src/neo/SmartContract/Iterators/StorageIterator.cs +++ b/src/Neo/SmartContract/Iterators/StorageIterator.cs @@ -1,15 +1,17 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// StorageIterator.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.VM; using Neo.VM.Types; +using System; using System.Collections.Generic; namespace Neo.SmartContract.Iterators @@ -19,14 +21,12 @@ internal class StorageIterator : IIterator private readonly IEnumerator<(StorageKey Key, StorageItem Value)> enumerator; private readonly int prefixLength; private readonly FindOptions options; - private readonly ReferenceCounter referenceCounter; - public StorageIterator(IEnumerator<(StorageKey, StorageItem)> enumerator, int prefixLength, FindOptions options, ReferenceCounter referenceCounter) + public StorageIterator(IEnumerator<(StorageKey, StorageItem)> enumerator, int prefixLength, FindOptions options) { this.enumerator = enumerator; this.prefixLength = prefixLength; this.options = options; - this.referenceCounter = referenceCounter; } public void Dispose() @@ -39,10 +39,10 @@ public bool Next() return enumerator.MoveNext(); } - public StackItem Value() + public StackItem Value(ReferenceCounter referenceCounter) { - byte[] key = enumerator.Current.Key.Key; - byte[] value = enumerator.Current.Value.Value; + ReadOnlyMemory key = enumerator.Current.Key.Key; + ReadOnlyMemory value = enumerator.Current.Value.Value; if (options.HasFlag(FindOptions.RemovePrefix)) key = key[prefixLength..]; @@ -52,9 +52,9 @@ public StackItem Value() : value; if (options.HasFlag(FindOptions.PickField0)) - item = ((Array)item)[0]; + item = ((VM.Types.Array)item)[0]; else if (options.HasFlag(FindOptions.PickField1)) - item = ((Array)item)[1]; + item = ((VM.Types.Array)item)[1]; if (options.HasFlag(FindOptions.KeysOnly)) return key; diff --git a/src/neo/SmartContract/JsonSerializer.cs b/src/Neo/SmartContract/JsonSerializer.cs similarity index 82% rename from src/neo/SmartContract/JsonSerializer.cs rename to src/Neo/SmartContract/JsonSerializer.cs index bb494f3c32..4d77104a6e 100644 --- a/src/neo/SmartContract/JsonSerializer.cs +++ b/src/Neo/SmartContract/JsonSerializer.cs @@ -1,19 +1,21 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// JsonSerializer.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.IO.Json; +using Neo.Json; using Neo.VM; using Neo.VM.Types; using System; using System.Collections; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Numerics; @@ -30,11 +32,11 @@ namespace Neo.SmartContract public static class JsonSerializer { /// - /// Serializes a to a . + /// Serializes a to a . /// /// The to serialize. /// The serialized object. - public static JObject Serialize(StackItem item) + public static JToken Serialize(StackItem item) { switch (item) { @@ -76,7 +78,7 @@ public static JObject Serialize(StackItem item) } case Null _: { - return JObject.Null; + return JToken.Null; } default: throw new FormatException(); } @@ -156,19 +158,20 @@ public static byte[] SerializeToByteArray(StackItem item, uint maxSize) } /// - /// Deserializes a from . + /// Deserializes a from . /// - /// The to deserialize. + /// The used. + /// The to deserialize. /// The limits for the deserialization. /// The used by the . /// The deserialized . - public static StackItem Deserialize(JObject json, ExecutionEngineLimits limits, ReferenceCounter referenceCounter = null) + public static StackItem Deserialize(ApplicationEngine engine, JToken json, ExecutionEngineLimits limits, ReferenceCounter referenceCounter = null) { uint maxStackSize = limits.MaxStackSize; - return Deserialize(json, ref maxStackSize, referenceCounter); + return Deserialize(engine, json, ref maxStackSize, referenceCounter); } - private static StackItem Deserialize(JObject json, ref uint maxStackSize, ReferenceCounter referenceCounter) + private static StackItem Deserialize(ApplicationEngine engine, JToken json, ref uint maxStackSize, ReferenceCounter referenceCounter) { if (maxStackSize-- == 0) throw new FormatException(); switch (json) @@ -180,8 +183,8 @@ private static StackItem Deserialize(JObject json, ref uint maxStackSize, Refere case JArray array: { List list = new(array.Count); - foreach (JObject obj in array) - list.Add(Deserialize(obj, ref maxStackSize, referenceCounter)); + foreach (JToken obj in array) + list.Add(Deserialize(engine, obj, ref maxStackSize, referenceCounter)); return new Array(referenceCounter, list); } case JString str: @@ -191,12 +194,15 @@ private static StackItem Deserialize(JObject json, ref uint maxStackSize, Refere case JNumber num: { if ((num.Value % 1) != 0) throw new FormatException("Decimal value is not allowed"); - + if (engine.IsHardforkEnabled(Hardfork.HF_Basilisk)) + { + return BigInteger.Parse(num.Value.ToString(CultureInfo.InvariantCulture), NumberStyles.Float, CultureInfo.InvariantCulture); + } return (BigInteger)num.Value; } case JBoolean boolean: { - return new Boolean(boolean.Value); + return boolean.Value ? StackItem.True : StackItem.False; } case JObject obj: { @@ -207,7 +213,7 @@ private static StackItem Deserialize(JObject json, ref uint maxStackSize, Refere if (maxStackSize-- == 0) throw new FormatException(); var key = entry.Key; - var value = Deserialize(entry.Value, ref maxStackSize, referenceCounter); + var value = Deserialize(engine, entry.Value, ref maxStackSize, referenceCounter); item[key] = value; } diff --git a/src/neo/SmartContract/KeyBuilder.cs b/src/Neo/SmartContract/KeyBuilder.cs similarity index 51% rename from src/neo/SmartContract/KeyBuilder.cs rename to src/Neo/SmartContract/KeyBuilder.cs index 167de85bab..cfd27cc6f8 100644 --- a/src/neo/SmartContract/KeyBuilder.cs +++ b/src/Neo/SmartContract/KeyBuilder.cs @@ -1,15 +1,17 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// KeyBuilder.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.IO; using System; +using System.Buffers.Binary; using System.IO; namespace Neo.SmartContract @@ -19,7 +21,6 @@ namespace Neo.SmartContract /// public class KeyBuilder { - private readonly int id; private readonly MemoryStream stream = new(); /// @@ -29,8 +30,22 @@ public class KeyBuilder /// The prefix of the key. public KeyBuilder(int id, byte prefix) { - this.id = id; - this.stream.WriteByte(prefix); + var data = new byte[sizeof(int)]; + BinaryPrimitives.WriteInt32LittleEndian(data, id); + + stream.Write(data); + stream.WriteByte(prefix); + } + + /// + /// Adds part of the key to the builder. + /// + /// Part of the key. + /// A reference to this instance after the add operation has completed. + public KeyBuilder Add(byte key) + { + stream.WriteByte(key); + return this; } /// @@ -60,28 +75,55 @@ public KeyBuilder Add(ISerializable key) } /// - /// Adds part of the key to the builder. + /// Adds part of the key to the builder in BigEndian. /// - /// The type of the parameter. /// Part of the key. /// A reference to this instance after the add operation has completed. - unsafe public KeyBuilder Add(T key) where T : unmanaged + public KeyBuilder AddBigEndian(int key) { - return Add(new ReadOnlySpan(&key, sizeof(T))); + var data = new byte[sizeof(int)]; + BinaryPrimitives.WriteInt32BigEndian(data, key); + + return Add(data); } /// - /// Adds part of the key to the builder with big-endian. + /// Adds part of the key to the builder in BigEndian. /// - /// The type of the parameter. /// Part of the key. /// A reference to this instance after the add operation has completed. - unsafe public KeyBuilder AddBigEndian(T key) where T : unmanaged + public KeyBuilder AddBigEndian(uint key) { - ReadOnlySpan buffer = new(&key, sizeof(T)); - for (int i = buffer.Length - 1; i >= 0; i--) - stream.WriteByte(buffer[i]); - return this; + var data = new byte[sizeof(uint)]; + BinaryPrimitives.WriteUInt32BigEndian(data, key); + + return Add(data); + } + + /// + /// Adds part of the key to the builder in BigEndian. + /// + /// Part of the key. + /// A reference to this instance after the add operation has completed. + public KeyBuilder AddBigEndian(long key) + { + var data = new byte[sizeof(long)]; + BinaryPrimitives.WriteInt64BigEndian(data, key); + + return Add(data); + } + + /// + /// Adds part of the key to the builder in BigEndian. + /// + /// Part of the key. + /// A reference to this instance after the add operation has completed. + public KeyBuilder AddBigEndian(ulong key) + { + var data = new byte[sizeof(ulong)]; + BinaryPrimitives.WriteUInt64BigEndian(data, key); + + return Add(data); } /// @@ -92,7 +134,7 @@ public byte[] ToArray() { using (stream) { - return StorageKey.CreateSearchPrefix(id, stream.ToArray()); + return stream.ToArray(); } } @@ -100,11 +142,7 @@ public static implicit operator StorageKey(KeyBuilder builder) { using (builder.stream) { - return new StorageKey - { - Id = builder.id, - Key = builder.stream.ToArray() - }; + return new StorageKey(builder.stream.ToArray()); } } } diff --git a/src/neo/SmartContract/LogEventArgs.cs b/src/Neo/SmartContract/LogEventArgs.cs similarity index 82% rename from src/neo/SmartContract/LogEventArgs.cs rename to src/Neo/SmartContract/LogEventArgs.cs index 35e5a3bc06..1614438dea 100644 --- a/src/neo/SmartContract/LogEventArgs.cs +++ b/src/Neo/SmartContract/LogEventArgs.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// LogEventArgs.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. diff --git a/src/neo/SmartContract/Manifest/ContractAbi.cs b/src/Neo/SmartContract/Manifest/ContractAbi.cs similarity index 89% rename from src/neo/SmartContract/Manifest/ContractAbi.cs rename to src/Neo/SmartContract/Manifest/ContractAbi.cs index 278ac28cda..3660d1a99e 100644 --- a/src/neo/SmartContract/Manifest/ContractAbi.cs +++ b/src/Neo/SmartContract/Manifest/ContractAbi.cs @@ -1,14 +1,15 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractAbi.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.IO.Json; +using Neo.Json; using Neo.VM; using Neo.VM.Types; using System; @@ -61,8 +62,8 @@ public static ContractAbi FromJson(JObject json) { ContractAbi abi = new() { - Methods = ((JArray)json["methods"]).Select(u => ContractMethodDescriptor.FromJson(u)).ToArray(), - Events = ((JArray)json["events"]).Select(u => ContractEventDescriptor.FromJson(u)).ToArray() + Methods = ((JArray)json["methods"]).Select(u => ContractMethodDescriptor.FromJson((JObject)u)).ToArray(), + Events = ((JArray)json["events"]).Select(u => ContractEventDescriptor.FromJson((JObject)u)).ToArray() }; if (abi.Methods.Length == 0) throw new FormatException(); return abi; diff --git a/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs b/src/Neo/SmartContract/Manifest/ContractEventDescriptor.cs similarity index 85% rename from src/neo/SmartContract/Manifest/ContractEventDescriptor.cs rename to src/Neo/SmartContract/Manifest/ContractEventDescriptor.cs index eaf8123a70..90227dd78d 100644 --- a/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs +++ b/src/Neo/SmartContract/Manifest/ContractEventDescriptor.cs @@ -1,14 +1,15 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractEventDescriptor.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.IO.Json; +using Neo.Json; using Neo.VM; using Neo.VM.Types; using System; @@ -58,7 +59,7 @@ public static ContractEventDescriptor FromJson(JObject json) ContractEventDescriptor descriptor = new() { Name = json["name"].GetString(), - Parameters = ((JArray)json["parameters"]).Select(u => ContractParameterDefinition.FromJson(u)).ToArray(), + Parameters = ((JArray)json["parameters"]).Select(u => ContractParameterDefinition.FromJson((JObject)u)).ToArray(), }; if (string.IsNullOrEmpty(descriptor.Name)) throw new FormatException(); _ = descriptor.Parameters.ToDictionary(p => p.Name); diff --git a/src/neo/SmartContract/Manifest/ContractGroup.cs b/src/Neo/SmartContract/Manifest/ContractGroup.cs similarity index 87% rename from src/neo/SmartContract/Manifest/ContractGroup.cs rename to src/Neo/SmartContract/Manifest/ContractGroup.cs index df3717ec79..231354327c 100644 --- a/src/neo/SmartContract/Manifest/ContractGroup.cs +++ b/src/Neo/SmartContract/Manifest/ContractGroup.cs @@ -1,17 +1,18 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractGroup.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.Cryptography; using Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.VM; using Neo.VM.Types; using System; @@ -38,7 +39,7 @@ public class ContractGroup : IInteroperable void IInteroperable.FromStackItem(StackItem stackItem) { Struct @struct = (Struct)stackItem; - PubKey = @struct[0].GetSpan().AsSerializable(); + PubKey = ECPoint.DecodePoint(@struct[0].GetSpan(), ECCurve.Secp256r1); Signature = @struct[1].GetSpan().ToArray(); } diff --git a/src/neo/SmartContract/Manifest/ContractManifest.cs b/src/Neo/SmartContract/Manifest/ContractManifest.cs similarity index 80% rename from src/neo/SmartContract/Manifest/ContractManifest.cs rename to src/Neo/SmartContract/Manifest/ContractManifest.cs index b440813330..078e8fd35e 100644 --- a/src/neo/SmartContract/Manifest/ContractManifest.cs +++ b/src/Neo/SmartContract/Manifest/ContractManifest.cs @@ -1,15 +1,16 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractManifest.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.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.VM; using Neo.VM.Types; using System; @@ -79,11 +80,12 @@ void IInteroperable.FromStackItem(StackItem stackItem) Permissions = ((Array)@struct[5]).Select(p => p.ToInteroperable()).ToArray(); Trusts = @struct[6] switch { - Null => WildcardContainer.CreateWildcard(), - Array array => WildcardContainer.Create(array.Select(p => new ContractPermissionDescriptor(p.GetSpan())).ToArray()), + Null _ => WildcardContainer.CreateWildcard(), + // Array array when array.Any(p => ((ByteString)p).Size == 0) => WildcardContainer.CreateWildcard(), + Array array => WildcardContainer.Create(array.Select(ContractPermissionDescriptor.Create).ToArray()), _ => throw new ArgumentException(null, nameof(stackItem)) }; - Extra = JObject.Parse(@struct[7].GetSpan()); + Extra = (JObject)JToken.Parse(@struct[7].GetSpan()); } public StackItem ToStackItem(ReferenceCounter referenceCounter) @@ -96,7 +98,7 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter) new Array(referenceCounter, SupportedStandards.Select(p => (StackItem)p)), Abi.ToStackItem(referenceCounter), new Array(referenceCounter, Permissions.Select(p => p.ToStackItem(referenceCounter))), - Trusts.IsWildcard ? StackItem.Null : new Array(referenceCounter, Trusts.Select(p => (StackItem)p.ToArray())), + Trusts.IsWildcard ? StackItem.Null : new Array(referenceCounter, Trusts.Select(p => p.ToArray()?? StackItem.Null)), Extra is null ? "null" : Extra.ToByteArray(false) }; } @@ -111,17 +113,17 @@ public static ContractManifest FromJson(JObject json) ContractManifest manifest = new() { Name = json["name"].GetString(), - Groups = ((JArray)json["groups"]).Select(u => ContractGroup.FromJson(u)).ToArray(), + Groups = ((JArray)json["groups"]).Select(u => ContractGroup.FromJson((JObject)u)).ToArray(), SupportedStandards = ((JArray)json["supportedstandards"]).Select(u => u.GetString()).ToArray(), - Abi = ContractAbi.FromJson(json["abi"]), - Permissions = ((JArray)json["permissions"]).Select(u => ContractPermission.FromJson(u)).ToArray(), - Trusts = WildcardContainer.FromJson(json["trusts"], u => ContractPermissionDescriptor.FromJson(u)), - Extra = json["extra"] + Abi = ContractAbi.FromJson((JObject)json["abi"]), + Permissions = ((JArray)json["permissions"]).Select(u => ContractPermission.FromJson((JObject)u)).ToArray(), + Trusts = WildcardContainer.FromJson(json["trusts"], u => ContractPermissionDescriptor.FromJson((JString)u)), + Extra = (JObject)json["extra"] }; if (string.IsNullOrEmpty(manifest.Name)) throw new FormatException(); _ = manifest.Groups.ToDictionary(p => p.PubKey); - if (json["features"].Properties.Count != 0) + if (json["features"] is not JObject features || features.Count != 0) throw new FormatException(); if (manifest.SupportedStandards.Any(p => string.IsNullOrEmpty(p))) throw new FormatException(); @@ -139,7 +141,7 @@ public static ContractManifest FromJson(JObject json) public static ContractManifest Parse(ReadOnlySpan json) { if (json.Length > MaxLength) throw new ArgumentException(null, nameof(json)); - return FromJson(JObject.Parse(json)); + return FromJson((JObject)JToken.Parse(json)); } /// @@ -171,10 +173,21 @@ public JObject ToJson() /// /// Determines whether the manifest is valid. /// + /// The used for test serialization. /// The hash of the contract. /// if the manifest is valid; otherwise, . - public bool IsValid(UInt160 hash) + public bool IsValid(ExecutionEngineLimits limits, UInt160 hash) { + // Ensure that is serializable + try + { + _ = BinarySerializer.Serialize(ToStackItem(null), limits); + } + catch + { + return false; + } + // Check groups return Groups.All(u => u.IsValid(hash)); } } diff --git a/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs b/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs similarity index 85% rename from src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs rename to src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs index ae7df246e4..cc8d0a16d5 100644 --- a/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs +++ b/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs @@ -1,14 +1,15 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractMethodDescriptor.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.IO.Json; +using Neo.Json; using Neo.VM; using Neo.VM.Types; using System; @@ -65,14 +66,14 @@ public override StackItem ToStackItem(ReferenceCounter referenceCounter) ContractMethodDescriptor descriptor = new() { Name = json["name"].GetString(), - Parameters = ((JArray)json["parameters"]).Select(u => ContractParameterDefinition.FromJson(u)).ToArray(), + Parameters = ((JArray)json["parameters"]).Select(u => ContractParameterDefinition.FromJson((JObject)u)).ToArray(), ReturnType = Enum.Parse(json["returntype"].GetString()), Offset = json["offset"].GetInt32(), Safe = json["safe"].GetBoolean() }; if (string.IsNullOrEmpty(descriptor.Name)) throw new FormatException(); _ = descriptor.Parameters.ToDictionary(p => p.Name); - if (!Enum.IsDefined(descriptor.ReturnType)) throw new FormatException(); + if (!Enum.IsDefined(typeof(ContractParameterType), descriptor.ReturnType)) throw new FormatException(); if (descriptor.Offset < 0) throw new FormatException(); return descriptor; } diff --git a/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs b/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs similarity index 83% rename from src/neo/SmartContract/Manifest/ContractParameterDefinition.cs rename to src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs index 15cda61b11..61906558d0 100644 --- a/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs +++ b/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs @@ -1,14 +1,15 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractParameterDefinition.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.IO.Json; +using Neo.Json; using Neo.VM; using Neo.VM.Types; using System; @@ -56,7 +57,7 @@ public static ContractParameterDefinition FromJson(JObject json) }; if (string.IsNullOrEmpty(parameter.Name)) throw new FormatException(); - if (!Enum.IsDefined(parameter.Type) || parameter.Type == ContractParameterType.Void) + if (!Enum.IsDefined(typeof(ContractParameterType), parameter.Type) || parameter.Type == ContractParameterType.Void) throw new FormatException(); return parameter; } diff --git a/src/neo/SmartContract/Manifest/ContractPermission.cs b/src/Neo/SmartContract/Manifest/ContractPermission.cs similarity index 85% rename from src/neo/SmartContract/Manifest/ContractPermission.cs rename to src/Neo/SmartContract/Manifest/ContractPermission.cs index 035e15beb3..44c67fd9a1 100644 --- a/src/neo/SmartContract/Manifest/ContractPermission.cs +++ b/src/Neo/SmartContract/Manifest/ContractPermission.cs @@ -1,15 +1,16 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractPermission.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.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.VM; using Neo.VM.Types; using System; @@ -19,21 +20,26 @@ namespace Neo.SmartContract.Manifest { /// - /// Represents a permission of a contract. It describes which contracts may be invoked and which methods are called. - /// If a contract invokes a contract or method that is not declared in the manifest at runtime, the invocation will fail. + /// Represents a permission of a contract. It describes which contracts may be + /// invoked and which methods are called. + /// If a contract invokes a contract or method that is not declared in the manifest + /// at runtime, the invocation will fail. /// public class ContractPermission : IInteroperable { /// /// Indicates which contract to be invoked. /// It can be a hash of a contract, a public key of a group, or a wildcard *. - /// If it specifies a hash of a contract, then the contract will be invoked; If it specifies a public key of a group, then any contract in this group may be invoked; If it specifies a wildcard *, then any contract may be invoked. + /// If it specifies a hash of a contract, then the contract will be invoked; + /// If it specifies a public key of a group, then any contract in this group + /// may be invoked; If it specifies a wildcard *, then any contract may be invoked. /// public ContractPermissionDescriptor Contract { get; set; } /// /// Indicates which methods to be called. - /// It can also be assigned with a wildcard *. If it is a wildcard *, then it means that any method can be called. + /// It can also be assigned with a wildcard *. If it is a wildcard *, + /// then it means that any method can be called. /// public WildcardContainer Methods { get; set; } @@ -80,7 +86,7 @@ public static ContractPermission FromJson(JObject json) { ContractPermission permission = new() { - Contract = ContractPermissionDescriptor.FromJson(json["contract"]), + Contract = ContractPermissionDescriptor.FromJson((JString)json["contract"]), Methods = WildcardContainer.FromJson(json["methods"], u => u.GetString()), }; if (permission.Methods.Any(p => string.IsNullOrEmpty(p))) diff --git a/src/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs b/src/Neo/SmartContract/Manifest/ContractPermissionDescriptor.cs similarity index 87% rename from src/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs rename to src/Neo/SmartContract/Manifest/ContractPermissionDescriptor.cs index c2b2786a01..ce85889423 100644 --- a/src/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs +++ b/src/Neo/SmartContract/Manifest/ContractPermissionDescriptor.cs @@ -1,16 +1,18 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractPermissionDescriptor.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.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; +using Neo.VM.Types; using System; namespace Neo.SmartContract.Manifest @@ -59,13 +61,18 @@ internal ContractPermissionDescriptor(ReadOnlySpan span) Hash = new UInt160(span); break; case 33: - Group = span.AsSerializable(); + Group = ECPoint.DecodePoint(span, ECCurve.Secp256r1); break; default: throw new ArgumentException(null, nameof(span)); } } + public static ContractPermissionDescriptor Create(StackItem item) + { + return item.Equals(StackItem.Null) ? CreateWildcard() : new ContractPermissionDescriptor(item.GetSpan()); + } + /// /// Creates a new instance of the class with the specified contract hash. /// @@ -120,7 +127,7 @@ public override int GetHashCode() /// /// The permission descriptor represented by a JSON object. /// The converted permission descriptor. - public static ContractPermissionDescriptor FromJson(JObject json) + public static ContractPermissionDescriptor FromJson(JString json) { string str = json.GetString(); if (str.Length == 42) @@ -136,7 +143,7 @@ public static ContractPermissionDescriptor FromJson(JObject json) /// Converts the permission descriptor to a JSON object. /// /// The permission descriptor represented by a JSON object. - public JObject ToJson() + public JString ToJson() { if (IsHash) return Hash.ToString(); if (IsGroup) return Group.ToString(); diff --git a/src/neo/SmartContract/Manifest/WildCardContainer.cs b/src/Neo/SmartContract/Manifest/WildCardContainer.cs similarity index 85% rename from src/neo/SmartContract/Manifest/WildCardContainer.cs rename to src/Neo/SmartContract/Manifest/WildCardContainer.cs index 135afb5408..3002042d04 100644 --- a/src/neo/SmartContract/Manifest/WildCardContainer.cs +++ b/src/Neo/SmartContract/Manifest/WildCardContainer.cs @@ -1,14 +1,15 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// WildCardContainer.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.IO.Json; +using Neo.Json; using System; using System.Collections; using System.Collections.Generic; @@ -57,7 +58,7 @@ private WildcardContainer(T[] data) /// The list represented by a JSON object. /// A converter for elements. /// The converted list. - public static WildcardContainer FromJson(JObject json, Func elementSelector) + public static WildcardContainer FromJson(JToken json, Func elementSelector) { switch (json) { @@ -84,7 +85,7 @@ public IEnumerator GetEnumerator() /// Converts the list to a JSON object. /// /// The list represented by a JSON object. - public JObject ToJson(Func elementSelector) + public JToken ToJson(Func elementSelector) { if (IsWildcard) return "*"; return _data.Select(p => elementSelector(p)).ToArray(); diff --git a/src/neo/SmartContract/MaxLengthAttribute.cs b/src/Neo/SmartContract/MaxLengthAttribute.cs similarity index 66% rename from src/neo/SmartContract/MaxLengthAttribute.cs rename to src/Neo/SmartContract/MaxLengthAttribute.cs index 01efd8545f..9ab982e5e2 100644 --- a/src/neo/SmartContract/MaxLengthAttribute.cs +++ b/src/Neo/SmartContract/MaxLengthAttribute.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// MaxLengthAttribute.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. diff --git a/src/neo/SmartContract/MethodToken.cs b/src/Neo/SmartContract/MethodToken.cs similarity index 87% rename from src/neo/SmartContract/MethodToken.cs rename to src/Neo/SmartContract/MethodToken.cs index 48ae74e1a5..1b391edd32 100644 --- a/src/neo/SmartContract/MethodToken.cs +++ b/src/Neo/SmartContract/MethodToken.cs @@ -1,15 +1,16 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// MethodToken.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.IO; -using Neo.IO.Json; +using Neo.Json; using System; using System.IO; @@ -52,7 +53,7 @@ public class MethodToken : ISerializable sizeof(bool) + // HasReturnValue sizeof(CallFlags); // CallFlags - void ISerializable.Deserialize(BinaryReader reader) + void ISerializable.Deserialize(ref MemoryReader reader) { Hash = reader.ReadSerializable(); Method = reader.ReadVarString(32); diff --git a/src/neo/SmartContract/Native/AccountState.cs b/src/Neo/SmartContract/Native/AccountState.cs similarity index 72% rename from src/neo/SmartContract/Native/AccountState.cs rename to src/Neo/SmartContract/Native/AccountState.cs index 1bed596e0e..031b37f8eb 100644 --- a/src/neo/SmartContract/Native/AccountState.cs +++ b/src/Neo/SmartContract/Native/AccountState.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// AccountState.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. diff --git a/src/Neo/SmartContract/Native/ContractEventAttribute.cs b/src/Neo/SmartContract/Native/ContractEventAttribute.cs new file mode 100644 index 0000000000..656ecef725 --- /dev/null +++ b/src/Neo/SmartContract/Native/ContractEventAttribute.cs @@ -0,0 +1,165 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractEventAttribute.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.SmartContract.Manifest; +using System; +using System.Diagnostics; + +namespace Neo.SmartContract.Native +{ + [DebuggerDisplay("{Descriptor.Name}")] + [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = true)] + internal class ContractEventAttribute : Attribute + { + public int Order { get; init; } + public ContractEventDescriptor Descriptor { get; set; } + public Hardfork? ActiveIn { get; init; } = null; + + public ContractEventAttribute(Hardfork activeIn, int order, string name, + string arg1Name, ContractParameterType arg1Value) : this(order, name, arg1Name, arg1Value) + { + ActiveIn = activeIn; + } + + public ContractEventAttribute(int order, string name, string arg1Name, ContractParameterType arg1Value) + { + Order = order; + Descriptor = new ContractEventDescriptor() + { + Name = name, + Parameters = new ContractParameterDefinition[] + { + new ContractParameterDefinition() + { + Name = arg1Name, + Type = arg1Value + } + } + }; + } + + public ContractEventAttribute(Hardfork activeIn, int order, string name, + string arg1Name, ContractParameterType arg1Value, + string arg2Name, ContractParameterType arg2Value) : this(order, name, arg1Name, arg1Value, arg2Name, arg2Value) + { + ActiveIn = activeIn; + } + + public ContractEventAttribute(int order, string name, + string arg1Name, ContractParameterType arg1Value, + string arg2Name, ContractParameterType arg2Value) + { + Order = order; + Descriptor = new ContractEventDescriptor() + { + Name = name, + Parameters = new ContractParameterDefinition[] + { + new ContractParameterDefinition() + { + Name = arg1Name, + Type = arg1Value + }, + new ContractParameterDefinition() + { + Name = arg2Name, + Type = arg2Value + } + } + }; + } + + public ContractEventAttribute(Hardfork activeIn, int order, string name, + string arg1Name, ContractParameterType arg1Value, + string arg2Name, ContractParameterType arg2Value, + string arg3Name, ContractParameterType arg3Value) : this(order, name, arg1Name, arg1Value, arg2Name, arg2Value, arg3Name, arg3Value) + { + ActiveIn = activeIn; + } + + public ContractEventAttribute(int order, string name, + string arg1Name, ContractParameterType arg1Value, + string arg2Name, ContractParameterType arg2Value, + string arg3Name, ContractParameterType arg3Value + ) + { + Order = order; + Descriptor = new ContractEventDescriptor() + { + Name = name, + Parameters = new ContractParameterDefinition[] + { + new ContractParameterDefinition() + { + Name = arg1Name, + Type = arg1Value + }, + new ContractParameterDefinition() + { + Name = arg2Name, + Type = arg2Value + }, + new ContractParameterDefinition() + { + Name = arg3Name, + Type = arg3Value + } + } + }; + } + + public ContractEventAttribute(Hardfork activeIn, int order, string name, + string arg1Name, ContractParameterType arg1Value, + string arg2Name, ContractParameterType arg2Value, + string arg3Name, ContractParameterType arg3Value, + string arg4Name, ContractParameterType arg4Value) : this(order, name, arg1Name, arg1Value, arg2Name, arg2Value, arg3Name, arg3Value, arg4Name, arg4Value) + { + ActiveIn = activeIn; + } + + public ContractEventAttribute(int order, string name, + string arg1Name, ContractParameterType arg1Value, + string arg2Name, ContractParameterType arg2Value, + string arg3Name, ContractParameterType arg3Value, + string arg4Name, ContractParameterType arg4Value + ) + { + Order = order; + Descriptor = new ContractEventDescriptor() + { + Name = name, + Parameters = new ContractParameterDefinition[] + { + new ContractParameterDefinition() + { + Name = arg1Name, + Type = arg1Value + }, + new ContractParameterDefinition() + { + Name = arg2Name, + Type = arg2Value + }, + new ContractParameterDefinition() + { + Name = arg3Name, + Type = arg3Value + }, + new ContractParameterDefinition() + { + Name = arg4Name, + Type = arg4Value + } + } + }; + } + } +} diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/Neo/SmartContract/Native/ContractManagement.cs similarity index 57% rename from src/neo/SmartContract/Native/ContractManagement.cs rename to src/Neo/SmartContract/Native/ContractManagement.cs index 351da9b2eb..3347163a6c 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/Neo/SmartContract/Native/ContractManagement.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractManagement.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. @@ -13,9 +14,11 @@ using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.Persistence; +using Neo.SmartContract.Iterators; using Neo.SmartContract.Manifest; using Neo.VM.Types; using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.Linq; using System.Numerics; @@ -30,51 +33,12 @@ public sealed class ContractManagement : NativeContract private const byte Prefix_MinimumDeploymentFee = 20; private const byte Prefix_NextAvailableId = 15; private const byte Prefix_Contract = 8; + private const byte Prefix_ContractHash = 12; - internal ContractManagement() - { - var events = new List(Manifest.Abi.Events) - { - new ContractEventDescriptor - { - Name = "Deploy", - Parameters = new ContractParameterDefinition[] - { - new ContractParameterDefinition() - { - Name = "Hash", - Type = ContractParameterType.Hash160 - } - } - }, - new ContractEventDescriptor - { - Name = "Update", - Parameters = new ContractParameterDefinition[] - { - new ContractParameterDefinition() - { - Name = "Hash", - Type = ContractParameterType.Hash160 - } - } - }, - new ContractEventDescriptor - { - Name = "Destroy", - Parameters = new ContractParameterDefinition[] - { - new ContractParameterDefinition() - { - Name = "Hash", - Type = ContractParameterType.Hash160 - } - } - } - }; - - Manifest.Abi.Events = events.ToArray(); - } + [ContractEvent(0, name: "Deploy", "Hash", ContractParameterType.Hash160)] + [ContractEvent(1, name: "Update", "Hash", ContractParameterType.Hash160)] + [ContractEvent(2, name: "Destroy", "Hash", ContractParameterType.Hash160)] + internal ContractManagement() : base() { } private int GetNextAvailableId(DataCache snapshot) { @@ -84,10 +48,13 @@ private int GetNextAvailableId(DataCache snapshot) return value; } - internal override ContractTask Initialize(ApplicationEngine engine) + internal override ContractTask Initialize(ApplicationEngine engine, Hardfork? hardfork) { - engine.Snapshot.Add(CreateStorageKey(Prefix_MinimumDeploymentFee), new StorageItem(10_00000000)); - engine.Snapshot.Add(CreateStorageKey(Prefix_NextAvailableId), new StorageItem(1)); + if (hardfork == ActiveIn) + { + engine.Snapshot.Add(CreateStorageKey(Prefix_MinimumDeploymentFee), new StorageItem(10_00000000)); + engine.Snapshot.Add(CreateStorageKey(Prefix_NextAvailableId), new StorageItem(1)); + } return ContractTask.CompletedTask; } @@ -96,24 +63,39 @@ private async ContractTask OnDeploy(ApplicationEngine engine, ContractState cont ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod("_deploy", 2); if (md is not null) await engine.CallFromNativeContract(Hash, contract.Hash, md.Name, data, update); - engine.SendNotification(Hash, update ? "Update" : "Deploy", new VM.Types.Array { contract.Hash.ToArray() }); + engine.SendNotification(Hash, update ? "Update" : "Deploy", new VM.Types.Array(engine.ReferenceCounter) { contract.Hash.ToArray() }); } internal override async ContractTask OnPersist(ApplicationEngine engine) { foreach (NativeContract contract in Contracts) { - uint[] updates = engine.ProtocolSettings.NativeUpdateHistory[contract.Name]; - if (updates.Length == 0 || updates[0] != engine.PersistingBlock.Index) - continue; - engine.Snapshot.Add(CreateStorageKey(Prefix_Contract).Add(contract.Hash), new StorageItem(new ContractState + if (contract.IsInitializeBlock(engine.ProtocolSettings, engine.PersistingBlock.Index, out Hardfork? hf)) { - Id = contract.Id, - Nef = contract.Nef, - Hash = contract.Hash, - Manifest = contract.Manifest - })); - await contract.Initialize(engine); + ContractState contractState = contract.GetContractState(engine.ProtocolSettings, engine.PersistingBlock.Index); + StorageItem state = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Contract).Add(contract.Hash)); + + if (state is null) + { + // Create the contract state + engine.Snapshot.Add(CreateStorageKey(Prefix_Contract).Add(contract.Hash), new StorageItem(contractState)); + engine.Snapshot.Add(CreateStorageKey(Prefix_ContractHash).AddBigEndian(contract.Id), new StorageItem(contract.Hash.ToArray())); + } + else + { + // Parse old contract + var oldContract = state.GetInteroperable(); + // Increase the update counter + oldContract.UpdateCounter++; + // Modify nef and manifest + oldContract.Nef = contractState.Nef; + oldContract.Manifest = contractState.Manifest; + } + + await contract.Initialize(engine, hf); + // Emit native contract notification + engine.SendNotification(Hash, state is null ? "Deploy" : "Update", new VM.Types.Array(engine.ReferenceCounter) { contract.Hash.ToArray() }); + } } } @@ -143,6 +125,56 @@ public ContractState GetContract(DataCache snapshot, UInt160 hash) return snapshot.TryGet(CreateStorageKey(Prefix_Contract).Add(hash))?.GetInteroperable(); } + /// + /// Maps specified ID to deployed contract. + /// + /// The snapshot used to read data. + /// Contract ID. + /// The deployed contract. + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] + public ContractState GetContractById(DataCache snapshot, int id) + { + StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_ContractHash).AddBigEndian(id)); + if (item is null) return null; + var hash = new UInt160(item.Value.Span); + return GetContract(snapshot, hash); + } + + /// + /// Gets hashes of all non native deployed contracts. + /// + /// The snapshot used to read data. + /// Iterator with hashes of all deployed contracts. + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] + private IIterator GetContractHashes(DataCache snapshot) + { + const FindOptions options = FindOptions.RemovePrefix; + byte[] prefix_key = CreateStorageKey(Prefix_ContractHash).ToArray(); + var enumerator = snapshot.Find(prefix_key) + .Select(p => (p.Key, p.Value, Id: BinaryPrimitives.ReadInt32BigEndian(p.Key.Key.Span[1..]))) + .Where(p => p.Id >= 0) + .Select(p => (p.Key, p.Value)) + .GetEnumerator(); + return new StorageIterator(enumerator, 1, options); + } + + /// + /// Check if a method exists in a contract. + /// + /// The snapshot used to read data. + /// The hash of the deployed contract. + /// The name of the method + /// The number of parameters + /// True if the method exists. + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] + public bool HasMethod(DataCache snapshot, UInt160 hash, string method, int pcount) + { + var contract = GetContract(snapshot, hash); + if (contract is null) return false; + var methodDescriptor = contract.Manifest.Abi.GetMethod(method, pcount); + return methodDescriptor is not null; + } + /// /// Gets all deployed contracts. /// @@ -177,8 +209,12 @@ private async ContractTask Deploy(ApplicationEngine engine, byte[ NefFile nef = nefFile.AsSerializable(); ContractManifest parsedManifest = ContractManifest.Parse(manifest); - Helper.Check(nef.Script, parsedManifest.Abi); + Helper.Check(new VM.Script(nef.Script, engine.IsHardforkEnabled(Hardfork.HF_Basilisk)), parsedManifest.Abi); UInt160 hash = Helper.GetContractHash(tx.Sender, nef.CheckSum, parsedManifest.Name); + + if (Policy.IsBlocked(engine.Snapshot, hash)) + throw new InvalidOperationException($"The contract {hash} has been blocked."); + StorageKey key = CreateStorageKey(Prefix_Contract).Add(hash); if (engine.Snapshot.Contains(key)) throw new InvalidOperationException($"Contract Already Exists: {hash}"); @@ -191,9 +227,10 @@ private async ContractTask Deploy(ApplicationEngine engine, byte[ Manifest = parsedManifest }; - if (!contract.Manifest.IsValid(hash)) throw new InvalidOperationException($"Invalid Manifest Hash: {hash}"); + if (!contract.Manifest.IsValid(engine.Limits, hash)) throw new InvalidOperationException($"Invalid Manifest: {hash}"); engine.Snapshot.Add(key, new StorageItem(contract)); + engine.Snapshot.Add(CreateStorageKey(Prefix_ContractHash).AddBigEndian(contract.Id), new StorageItem(hash.ToArray())); await OnDeploy(engine, contract, data, false); @@ -215,6 +252,7 @@ private ContractTask Update(ApplicationEngine engine, byte[] nefFile, byte[] man var contract = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Contract).Add(engine.CallingScriptHash))?.GetInteroperable(); if (contract is null) throw new InvalidOperationException($"Updating Contract Does Not Exist: {engine.CallingScriptHash}"); + if (contract.UpdateCounter == ushort.MaxValue) throw new InvalidOperationException($"The contract reached the maximum number of updates."); if (nefFile != null) { @@ -231,11 +269,11 @@ private ContractTask Update(ApplicationEngine engine, byte[] nefFile, byte[] man ContractManifest manifest_new = ContractManifest.Parse(manifest); if (manifest_new.Name != contract.Manifest.Name) throw new InvalidOperationException("The name of the contract can't be changed."); - if (!manifest_new.IsValid(contract.Hash)) - throw new InvalidOperationException($"Invalid Manifest Hash: {contract.Hash}"); + if (!manifest_new.IsValid(engine.Limits, contract.Hash)) + throw new InvalidOperationException($"Invalid Manifest: {contract.Hash}"); contract.Manifest = manifest_new; } - Helper.Check(contract.Nef.Script, contract.Manifest.Abi); + Helper.Check(new VM.Script(contract.Nef.Script, engine.IsHardforkEnabled(Hardfork.HF_Basilisk)), contract.Manifest.Abi); contract.UpdateCounter++; // Increase update counter return OnDeploy(engine, contract, data, true); } @@ -248,9 +286,13 @@ private void Destroy(ApplicationEngine engine) ContractState contract = engine.Snapshot.TryGet(ckey)?.GetInteroperable(); if (contract is null) return; engine.Snapshot.Delete(ckey); + engine.Snapshot.Delete(CreateStorageKey(Prefix_ContractHash).AddBigEndian(contract.Id)); foreach (var (key, _) in engine.Snapshot.Find(StorageKey.CreateSearchPrefix(contract.Id, ReadOnlySpan.Empty))) engine.Snapshot.Delete(key); - engine.SendNotification(Hash, "Destroy", new VM.Types.Array { hash.ToArray() }); + // lock contract + Policy.BlockAccount(engine.Snapshot, hash); + // emit event + engine.SendNotification(Hash, "Destroy", new VM.Types.Array(engine.ReferenceCounter) { hash.ToArray() }); } } } diff --git a/src/Neo/SmartContract/Native/ContractMethodAttribute.cs b/src/Neo/SmartContract/Native/ContractMethodAttribute.cs new file mode 100644 index 0000000000..cd8f7de59e --- /dev/null +++ b/src/Neo/SmartContract/Native/ContractMethodAttribute.cs @@ -0,0 +1,34 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractMethodAttribute.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; +using System.Diagnostics; + +namespace Neo.SmartContract.Native +{ + [DebuggerDisplay("{Name}")] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false)] + internal class ContractMethodAttribute : Attribute + { + public string Name { get; init; } + public CallFlags RequiredCallFlags { get; init; } + public long CpuFee { get; init; } + public long StorageFee { get; init; } + public Hardfork? ActiveIn { get; init; } = null; + + public ContractMethodAttribute() { } + + public ContractMethodAttribute(Hardfork activeIn) + { + ActiveIn = activeIn; + } + } +} diff --git a/src/neo/SmartContract/Native/ContractMethodMetadata.cs b/src/Neo/SmartContract/Native/ContractMethodMetadata.cs similarity index 91% rename from src/neo/SmartContract/Native/ContractMethodMetadata.cs rename to src/Neo/SmartContract/Native/ContractMethodMetadata.cs index da8bb2312b..9f83eb20fc 100644 --- a/src/neo/SmartContract/Native/ContractMethodMetadata.cs +++ b/src/Neo/SmartContract/Native/ContractMethodMetadata.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractMethodMetadata.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. @@ -14,6 +15,7 @@ using Neo.SmartContract.Manifest; using Neo.VM.Types; using System; +using System.Diagnostics; using System.Linq; using System.Numerics; using System.Reflection; @@ -21,6 +23,7 @@ namespace Neo.SmartContract.Native { + [DebuggerDisplay("{Name}")] internal class ContractMethodMetadata { public string Name { get; } @@ -32,6 +35,7 @@ internal class ContractMethodMetadata public long StorageFee { get; } public CallFlags RequiredCallFlags { get; } public ContractMethodDescriptor Descriptor { get; } + public Hardfork? ActiveIn { get; init; } = null; public ContractMethodMetadata(MemberInfo member, ContractMethodAttribute attribute) { @@ -55,6 +59,7 @@ public ContractMethodMetadata(MemberInfo member, ContractMethodAttribute attribu this.CpuFee = attribute.CpuFee; this.StorageFee = attribute.StorageFee; this.RequiredCallFlags = attribute.RequiredCallFlags; + this.ActiveIn = attribute.ActiveIn; this.Descriptor = new ContractMethodDescriptor { Name = Name, diff --git a/src/Neo/SmartContract/Native/CryptoLib.BLS12_381.cs b/src/Neo/SmartContract/Native/CryptoLib.BLS12_381.cs new file mode 100644 index 0000000000..f966a73fd1 --- /dev/null +++ b/src/Neo/SmartContract/Native/CryptoLib.BLS12_381.cs @@ -0,0 +1,145 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// CryptoLib.BLS12_381.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.Cryptography.BLS12_381; +using Neo.VM.Types; +using System; + +namespace Neo.SmartContract.Native; + +partial class CryptoLib +{ + /// + /// Serialize a bls12381 point. + /// + /// The point to be serialized. + /// + [ContractMethod(CpuFee = 1 << 19)] + public static byte[] Bls12381Serialize(InteropInterface g) + { + return g.GetInterface() switch + { + G1Affine p => p.ToCompressed(), + G1Projective p => new G1Affine(p).ToCompressed(), + G2Affine p => p.ToCompressed(), + G2Projective p => new G2Affine(p).ToCompressed(), + Gt p => p.ToArray(), + _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:type mismatch") + }; + } + + /// + /// Deserialize a bls12381 point. + /// + /// The point as byte array. + /// + [ContractMethod(CpuFee = 1 << 19)] + public static InteropInterface Bls12381Deserialize(byte[] data) + { + return data.Length switch + { + 48 => new InteropInterface(G1Affine.FromCompressed(data)), + 96 => new InteropInterface(G2Affine.FromCompressed(data)), + 576 => new InteropInterface(Gt.FromBytes(data)), + _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:valid point length"), + }; + } + + /// + /// Determines whether the specified points are equal. + /// + /// The first point. + /// Teh second point. + /// true if the specified points are equal; otherwise, false. + [ContractMethod(CpuFee = 1 << 5)] + public static bool Bls12381Equal(InteropInterface x, InteropInterface y) + { + return (x.GetInterface(), y.GetInterface()) switch + { + (G1Affine p1, G1Affine p2) => p1.Equals(p2), + (G1Projective p1, G1Projective p2) => p1.Equals(p2), + (G2Affine p1, G2Affine p2) => p1.Equals(p2), + (G2Projective p1, G2Projective p2) => p1.Equals(p2), + (Gt p1, Gt p2) => p1.Equals(p2), + _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:type mismatch") + }; + } + + /// + /// Add operation of two points. + /// + /// The first point. + /// The second point. + /// + [ContractMethod(CpuFee = 1 << 19)] + public static InteropInterface Bls12381Add(InteropInterface x, InteropInterface y) + { + return (x.GetInterface(), y.GetInterface()) switch + { + (G1Affine p1, G1Affine p2) => new(new G1Projective(p1) + p2), + (G1Affine p1, G1Projective p2) => new(p1 + p2), + (G1Projective p1, G1Affine p2) => new(p1 + p2), + (G1Projective p1, G1Projective p2) => new(p1 + p2), + (G2Affine p1, G2Affine p2) => new(new G2Projective(p1) + p2), + (G2Affine p1, G2Projective p2) => new(p1 + p2), + (G2Projective p1, G2Affine p2) => new(p1 + p2), + (G2Projective p1, G2Projective p2) => new(p1 + p2), + (Gt p1, Gt p2) => new(p1 + p2), + _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:type mismatch") + }; + } + + /// + /// Mul operation of gt point and multiplier + /// + /// The point + /// Multiplier,32 bytes,little-endian + /// negative number + /// + [ContractMethod(CpuFee = 1 << 21)] + public static InteropInterface Bls12381Mul(InteropInterface x, byte[] mul, bool neg) + { + Scalar X = neg ? -Scalar.FromBytes(mul) : Scalar.FromBytes(mul); + return x.GetInterface() switch + { + G1Affine p => new(p * X), + G1Projective p => new(p * X), + G2Affine p => new(p * X), + G2Projective p => new(p * X), + Gt p => new(p * X), + _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:type mismatch") + }; + } + + /// + /// Pairing operation of g1 and g2 + /// + /// The g1 point. + /// The g2 point. + /// + [ContractMethod(CpuFee = 1 << 23)] + public static InteropInterface Bls12381Pairing(InteropInterface g1, InteropInterface g2) + { + G1Affine g1a = g1.GetInterface() switch + { + G1Affine g => g, + G1Projective g => new(g), + _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:type mismatch") + }; + G2Affine g2a = g2.GetInterface() switch + { + G2Affine g => g, + G2Projective g => new(g), + _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:type mismatch") + }; + return new(Bls12.Pairing(in g1a, in g2a)); + } +} diff --git a/src/neo/SmartContract/Native/CryptoLib.cs b/src/Neo/SmartContract/Native/CryptoLib.cs similarity index 59% rename from src/neo/SmartContract/Native/CryptoLib.cs rename to src/Neo/SmartContract/Native/CryptoLib.cs index 5b06de66eb..a7b822e39c 100644 --- a/src/neo/SmartContract/Native/CryptoLib.cs +++ b/src/Neo/SmartContract/Native/CryptoLib.cs @@ -1,15 +1,17 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// CryptoLib.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.Cryptography; using Neo.Cryptography.ECC; +using Org.BouncyCastle.Crypto.Digests; using System; using System.Collections.Generic; @@ -18,7 +20,7 @@ namespace Neo.SmartContract.Native /// /// A native contract library that provides cryptographic algorithms. /// - public sealed class CryptoLib : NativeContract + public sealed partial class CryptoLib : NativeContract { private static readonly Dictionary curves = new() { @@ -26,7 +28,7 @@ public sealed class CryptoLib : NativeContract [NamedCurve.secp256r1] = ECCurve.Secp256r1 }; - internal CryptoLib() { } + internal CryptoLib() : base() { } /// /// Computes the hash value for the specified byte array using the ripemd160 algorithm. @@ -50,6 +52,34 @@ public static byte[] Sha256(byte[] data) return data.Sha256(); } + /// + /// Computes the hash value for the specified byte array using the murmur32 algorithm. + /// + /// The input to compute the hash code for. + /// The seed of the murmur32 hash function + /// The computed hash code. + [ContractMethod(CpuFee = 1 << 13)] + public static byte[] Murmur32(byte[] data, uint seed) + { + using Murmur32 murmur = new(seed); + return murmur.ComputeHash(data); + } + + /// + /// Computes the hash value for the specified byte array using the keccak256 algorithm. + /// + /// The input to compute the hash code for. + /// Computed hash + [ContractMethod(Hardfork.HF_Cockatrice, CpuFee = 1 << 15)] + public static byte[] Keccak256(byte[] data) + { + KeccakDigest keccak = new(256); + keccak.BlockUpdate(data, 0, data.Length); + byte[] result = new byte[keccak.GetDigestSize()]; + keccak.DoFinal(result, 0); + return result; + } + /// /// Verifies that a digital signature is appropriate for the provided key and message using the ECDSA algorithm. /// diff --git a/src/neo/SmartContract/Native/FungibleToken.cs b/src/Neo/SmartContract/Native/FungibleToken.cs similarity index 75% rename from src/neo/SmartContract/Native/FungibleToken.cs rename to src/Neo/SmartContract/Native/FungibleToken.cs index b680a27a3a..6499c60c19 100644 --- a/src/neo/SmartContract/Native/FungibleToken.cs +++ b/src/Neo/SmartContract/Native/FungibleToken.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// FungibleToken.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. @@ -13,7 +14,6 @@ using Neo.SmartContract.Manifest; using Neo.VM.Types; using System; -using System.Collections.Generic; using System.Numerics; using Array = Neo.VM.Types.Array; @@ -56,39 +56,18 @@ public abstract class FungibleToken : NativeContract /// /// Initializes a new instance of the class. /// - protected FungibleToken() + [ContractEvent(0, name: "Transfer", + "from", ContractParameterType.Hash160, + "to", ContractParameterType.Hash160, + "amount", ContractParameterType.Integer)] + protected FungibleToken() : base() { this.Factor = BigInteger.Pow(10, Decimals); + } - Manifest.SupportedStandards = new[] { "NEP-17" }; - - var events = new List(Manifest.Abi.Events) - { - new ContractEventDescriptor - { - Name = "Transfer", - Parameters = new ContractParameterDefinition[] - { - new ContractParameterDefinition() - { - Name = "from", - Type = ContractParameterType.Hash160 - }, - new ContractParameterDefinition() - { - Name = "to", - Type = ContractParameterType.Hash160 - }, - new ContractParameterDefinition() - { - Name = "amount", - Type = ContractParameterType.Integer - } - } - } - }; - - Manifest.Abi.Events = events.ToArray(); + protected override void OnManifestCompose(ContractManifest manifest) + { + manifest.SupportedStandards = new[] { "NEP-17" }; } internal async ContractTask Mint(ApplicationEngine engine, UInt160 account, BigInteger amount, bool callOnPayment) @@ -97,7 +76,7 @@ internal async ContractTask Mint(ApplicationEngine engine, UInt160 account, BigI if (amount.IsZero) return; StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Account).Add(account), () => new StorageItem(new TState())); TState state = storage.GetInteroperable(); - await OnBalanceChanging(engine, account, state, amount); + OnBalanceChanging(engine, account, state, amount); state.Balance += amount; storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_TotalSupply), () => new StorageItem(BigInteger.Zero)); storage.Add(amount); @@ -112,7 +91,7 @@ internal async ContractTask Burn(ApplicationEngine engine, UInt160 account, BigI StorageItem storage = engine.Snapshot.GetAndChange(key); TState state = storage.GetInteroperable(); if (state.Balance < amount) throw new InvalidOperationException(); - await OnBalanceChanging(engine, account, state, -amount); + OnBalanceChanging(engine, account, state, -amount); if (state.Balance == amount) engine.Snapshot.Delete(key); else @@ -164,7 +143,7 @@ private protected async ContractTask Transfer(ApplicationEngine engine, UI if (storage_from != null) { TState state_from = storage_from.GetInteroperable(); - await OnBalanceChanging(engine, from, state_from, amount); + OnBalanceChanging(engine, from, state_from, amount); } } else @@ -174,11 +153,11 @@ private protected async ContractTask Transfer(ApplicationEngine engine, UI if (state_from.Balance < amount) return false; if (from.Equals(to)) { - await OnBalanceChanging(engine, from, state_from, BigInteger.Zero); + OnBalanceChanging(engine, from, state_from, BigInteger.Zero); } else { - await OnBalanceChanging(engine, from, state_from, -amount); + OnBalanceChanging(engine, from, state_from, -amount); if (state_from.Balance == amount) engine.Snapshot.Delete(key_from); else @@ -186,7 +165,7 @@ private protected async ContractTask Transfer(ApplicationEngine engine, UI StorageKey key_to = CreateStorageKey(Prefix_Account).Add(to); StorageItem storage_to = engine.Snapshot.GetAndChange(key_to, () => new StorageItem(new TState())); TState state_to = storage_to.GetInteroperable(); - await OnBalanceChanging(engine, to, state_to, amount); + OnBalanceChanging(engine, to, state_to, amount); state_to.Balance += amount; } } @@ -194,17 +173,16 @@ private protected async ContractTask Transfer(ApplicationEngine engine, UI return true; } - internal virtual ContractTask OnBalanceChanging(ApplicationEngine engine, UInt160 account, TState state, BigInteger amount) + internal virtual void OnBalanceChanging(ApplicationEngine engine, UInt160 account, TState state, BigInteger amount) { - return ContractTask.CompletedTask; } - private async ContractTask PostTransfer(ApplicationEngine engine, UInt160 from, UInt160 to, BigInteger amount, StackItem data, bool callOnPayment) + private protected virtual async ContractTask PostTransfer(ApplicationEngine engine, UInt160 from, UInt160 to, BigInteger amount, StackItem data, bool callOnPayment) { // Send notification engine.SendNotification(Hash, "Transfer", - new Array { from?.ToArray() ?? StackItem.Null, to?.ToArray() ?? StackItem.Null, amount }); + new Array(engine.ReferenceCounter) { from?.ToArray() ?? StackItem.Null, to?.ToArray() ?? StackItem.Null, amount }); // Check if it's a wallet or smart contract diff --git a/src/neo/SmartContract/Native/GasToken.cs b/src/Neo/SmartContract/Native/GasToken.cs similarity index 66% rename from src/neo/SmartContract/Native/GasToken.cs rename to src/Neo/SmartContract/Native/GasToken.cs index f199a879e5..ce2f77ad2c 100644 --- a/src/neo/SmartContract/Native/GasToken.cs +++ b/src/Neo/SmartContract/Native/GasToken.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// GasToken.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. @@ -25,10 +26,14 @@ internal GasToken() { } - internal override ContractTask Initialize(ApplicationEngine engine) + internal override ContractTask Initialize(ApplicationEngine engine, Hardfork? hardfork) { - UInt160 account = Contract.GetBFTAddress(engine.ProtocolSettings.StandbyValidators); - return Mint(engine, account, engine.ProtocolSettings.InitialGasDistribution, false); + if (hardfork == ActiveIn) + { + UInt160 account = Contract.GetBFTAddress(engine.ProtocolSettings.StandbyValidators); + return Mint(engine, account, engine.ProtocolSettings.InitialGasDistribution, false); + } + return ContractTask.CompletedTask; } internal override async ContractTask OnPersist(ApplicationEngine engine) diff --git a/src/neo/SmartContract/Native/HashIndexState.cs b/src/Neo/SmartContract/Native/HashIndexState.cs similarity index 70% rename from src/neo/SmartContract/Native/HashIndexState.cs rename to src/Neo/SmartContract/Native/HashIndexState.cs index 7dc8ba113e..229458ffdd 100644 --- a/src/neo/SmartContract/Native/HashIndexState.cs +++ b/src/Neo/SmartContract/Native/HashIndexState.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// HashIndexState.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. diff --git a/src/Neo/SmartContract/Native/InteroperableList.cs b/src/Neo/SmartContract/Native/InteroperableList.cs new file mode 100644 index 0000000000..e118db7621 --- /dev/null +++ b/src/Neo/SmartContract/Native/InteroperableList.cs @@ -0,0 +1,59 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// InteroperableList.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.VM; +using Neo.VM.Types; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace Neo.SmartContract.Native +{ + abstract class InteroperableList : IList, IInteroperable + { + private List list; + private List List => list ??= new(); + + public T this[int index] { get => List[index]; set => List[index] = value; } + public int Count => List.Count; + public bool IsReadOnly => false; + + public void Add(T item) => List.Add(item); + public void AddRange(IEnumerable collection) => List.AddRange(collection); + public void Clear() => List.Clear(); + public bool Contains(T item) => List.Contains(item); + public void CopyTo(T[] array, int arrayIndex) => List.CopyTo(array, arrayIndex); + IEnumerator IEnumerable.GetEnumerator() => List.GetEnumerator(); + public IEnumerator GetEnumerator() => List.GetEnumerator(); + public int IndexOf(T item) => List.IndexOf(item); + public void Insert(int index, T item) => List.Insert(index, item); + public bool Remove(T item) => List.Remove(item); + public void RemoveAt(int index) => List.RemoveAt(index); + public void Sort() => List.Sort(); + + protected abstract T ElementFromStackItem(StackItem item); + protected abstract StackItem ElementToStackItem(T element, ReferenceCounter referenceCounter); + + public void FromStackItem(StackItem stackItem) + { + List.Clear(); + foreach (StackItem item in (Array)stackItem) + { + Add(ElementFromStackItem(item)); + } + } + + public StackItem ToStackItem(ReferenceCounter referenceCounter) + { + return new Array(referenceCounter, this.Select(p => ElementToStackItem(p, referenceCounter))); + } + } +} diff --git a/src/neo/SmartContract/Native/LedgerContract.cs b/src/Neo/SmartContract/Native/LedgerContract.cs similarity index 74% rename from src/neo/SmartContract/Native/LedgerContract.cs rename to src/Neo/SmartContract/Native/LedgerContract.cs index c3900709d6..d72f07c022 100644 --- a/src/neo/SmartContract/Native/LedgerContract.cs +++ b/src/Neo/SmartContract/Native/LedgerContract.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// LedgerContract.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. @@ -15,6 +16,7 @@ using Neo.Persistence; using Neo.VM; using System; +using System.Collections.Generic; using System.Linq; using System.Numerics; @@ -30,9 +32,7 @@ public sealed class LedgerContract : NativeContract private const byte Prefix_Block = 5; private const byte Prefix_Transaction = 11; - internal LedgerContract() - { - } + internal LedgerContract() : base() { } internal override ContractTask OnPersist(ApplicationEngine engine) { @@ -46,7 +46,20 @@ internal override ContractTask OnPersist(ApplicationEngine engine) engine.Snapshot.Add(CreateStorageKey(Prefix_Block).Add(engine.PersistingBlock.Hash), new StorageItem(Trim(engine.PersistingBlock).ToArray())); foreach (TransactionState tx in transactions) { - engine.Snapshot.Add(CreateStorageKey(Prefix_Transaction).Add(tx.Transaction.Hash), new StorageItem(tx)); + // It's possible that there are previously saved malicious conflict records for this transaction. + // If so, then remove it and store the relevant transaction itself. + engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Transaction).Add(tx.Transaction.Hash), () => new StorageItem(new TransactionState())).FromReplica(new StorageItem(tx)); + + // Store transaction's conflicits. + var conflictingSigners = tx.Transaction.Signers.Select(s => s.Account); + foreach (var attr in tx.Transaction.GetAttributes()) + { + engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Transaction).Add(attr.Hash), () => new StorageItem(new TransactionState())).FromReplica(new StorageItem(new TransactionState() { BlockIndex = engine.PersistingBlock.Index })); + foreach (var signer in conflictingSigners) + { + engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Transaction).Add(attr.Hash).Add(signer), () => new StorageItem(new TransactionState())).FromReplica(new StorageItem(new TransactionState() { BlockIndex = engine.PersistingBlock.Index })); + } + } } engine.SetState(transactions); return ContractTask.CompletedTask; @@ -82,7 +95,7 @@ public UInt256 GetBlockHash(DataCache snapshot, uint index) { StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_BlockHash).AddBigEndian(index)); if (item is null) return null; - return new UInt256(item.Value); + return new UInt256(item.Value.Span); } /// @@ -126,7 +139,35 @@ public bool ContainsBlock(DataCache snapshot, UInt256 hash) /// if the blockchain contains the transaction; otherwise, . public bool ContainsTransaction(DataCache snapshot, UInt256 hash) { - return snapshot.Contains(CreateStorageKey(Prefix_Transaction).Add(hash)); + var txState = GetTransactionState(snapshot, hash); + return txState != null; + } + + /// + /// Determine whether the specified transaction hash is contained in the blockchain + /// as the hash of conflicting transaction. + /// + /// The snapshot used to read data. + /// The hash of the conflicting transaction. + /// The list of signer accounts of the conflicting transaction. + /// MaxTraceableBlocks protocol setting. + /// if the blockchain contains the hash of the conflicting transaction; otherwise, . + public bool ContainsConflictHash(DataCache snapshot, UInt256 hash, IEnumerable signers, uint maxTraceableBlocks) + { + // Check the dummy stub firstly to define whether there's exist at least one conflict record. + var stub = snapshot.TryGet(CreateStorageKey(Prefix_Transaction).Add(hash))?.GetInteroperable(); + if (stub is null || stub.Transaction is not null || !IsTraceableBlock(snapshot, stub.BlockIndex, maxTraceableBlocks)) + return false; + + // At least one conflict record is found, then need to check signers intersection. + foreach (var signer in signers) + { + var state = snapshot.TryGet(CreateStorageKey(Prefix_Transaction).Add(hash).Add(signer))?.GetInteroperable(); + if (state is not null && IsTraceableBlock(snapshot, state.BlockIndex, maxTraceableBlocks)) + return true; + } + + return false; } /// @@ -220,7 +261,9 @@ public Header GetHeader(DataCache snapshot, uint index) /// The with the specified hash. public TransactionState GetTransactionState(DataCache snapshot, UInt256 hash) { - return snapshot.TryGet(CreateStorageKey(Prefix_Transaction).Add(hash))?.GetInteroperable(); + var state = snapshot.TryGet(CreateStorageKey(Prefix_Transaction).Add(hash))?.GetInteroperable(); + if (state?.Transaction is null) return null; + return state; } /// @@ -242,6 +285,14 @@ private Transaction GetTransactionForContract(ApplicationEngine engine, UInt256 return state.Transaction; } + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] + private Signer[] GetTransactionSigners(ApplicationEngine engine, UInt256 hash) + { + TransactionState state = GetTransactionState(engine.Snapshot, hash); + if (state is null || !IsTraceableBlock(engine.Snapshot, state.BlockIndex, engine.ProtocolSettings.MaxTraceableBlocks)) return null; + return state.Transaction.Signers; + } + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] private VMState GetTransactionVMState(ApplicationEngine engine, UInt256 hash) { diff --git a/src/neo/SmartContract/Native/NamedCurve.cs b/src/Neo/SmartContract/Native/NamedCurve.cs similarity index 67% rename from src/neo/SmartContract/Native/NamedCurve.cs rename to src/Neo/SmartContract/Native/NamedCurve.cs index 70c1cce9a6..9e97472cfc 100644 --- a/src/neo/SmartContract/Native/NamedCurve.cs +++ b/src/Neo/SmartContract/Native/NamedCurve.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// NamedCurve.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. diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/Neo/SmartContract/Native/NativeContract.cs similarity index 50% rename from src/neo/SmartContract/Native/NativeContract.cs rename to src/Neo/SmartContract/Native/NativeContract.cs index b19e5fe349..8d5155ff92 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/Neo/SmartContract/Native/NativeContract.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// NativeContract.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. @@ -13,6 +14,8 @@ using Neo.VM; using System; using System.Collections.Generic; +using System.Collections.Immutable; +using System.Collections.ObjectModel; using System.Linq; using System.Reflection; @@ -23,9 +26,32 @@ namespace Neo.SmartContract.Native /// public abstract class NativeContract { + private class NativeContractsCache + { + public class CacheEntry + { + public Dictionary Methods { get; set; } + public byte[] Script { get; set; } + } + + internal Dictionary NativeContracts { get; set; } = new Dictionary(); + + public CacheEntry GetAllowedMethods(NativeContract native, ApplicationEngine engine) + { + if (NativeContracts.TryGetValue(native.Id, out var value)) return value; + + uint index = engine.PersistingBlock is null ? Ledger.CurrentIndex(engine.Snapshot) : engine.PersistingBlock.Index; + CacheEntry methods = native.GetAllowedMethods(engine.ProtocolSettings, index); + NativeContracts[native.Id] = methods; + return methods; + } + } + private static readonly List contractsList = new(); private static readonly Dictionary contractsDictionary = new(); - private readonly Dictionary methods = new(); + private readonly ImmutableHashSet usedHardforks; + private readonly ReadOnlyCollection methodDescriptors; + private readonly ReadOnlyCollection eventsDescriptors; private static int id_counter = 0; #region Named Native Contracts @@ -88,9 +114,9 @@ public abstract class NativeContract public string Name => GetType().Name; /// - /// The nef of the native contract. + /// Since Hardfork has to start having access to the native contract. /// - public NefFile Nef { get; } + public virtual Hardfork? ActiveIn { get; } = null; /// /// The hash of the native contract. @@ -102,28 +128,58 @@ public abstract class NativeContract /// public int Id { get; } = --id_counter; - /// - /// The manifest of the native contract. - /// - public ContractManifest Manifest { get; } - /// /// Initializes a new instance of the class. /// protected NativeContract() { - List descriptors = new(); + this.Hash = Helper.GetContractHash(UInt160.Zero, 0, Name); + + // Reflection to get the methods + + List listMethods = new(); foreach (MemberInfo member in GetType().GetMembers(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)) { ContractMethodAttribute attribute = member.GetCustomAttribute(); if (attribute is null) continue; - descriptors.Add(new ContractMethodMetadata(member, attribute)); + listMethods.Add(new ContractMethodMetadata(member, attribute)); } - descriptors = descriptors.OrderBy(p => p.Name).ThenBy(p => p.Parameters.Length).ToList(); + methodDescriptors = listMethods.OrderBy(p => p.Name, StringComparer.Ordinal).ThenBy(p => p.Parameters.Length).ToList().AsReadOnly(); + + // Reflection to get the events + eventsDescriptors = + GetType().GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, Array.Empty(), null)?. + GetCustomAttributes(). + OrderBy(p => p.Order).ToList().AsReadOnly(); + + // Calculate the initializations forks + usedHardforks = + methodDescriptors.Select(u => u.ActiveIn) + .Concat(eventsDescriptors.Select(u => u.ActiveIn)) + .Concat(new Hardfork?[] { ActiveIn }) + .Where(u => u is not null) + .OrderBy(u => (byte)u) + .Cast().ToImmutableHashSet(); + + contractsList.Add(this); + contractsDictionary.Add(Hash, this); + } + + /// + /// The allowed methods and his offsets. + /// + /// The where the HardForks are configured. + /// Block index + /// The . + private NativeContractsCache.CacheEntry GetAllowedMethods(ProtocolSettings settings, uint index) + { + Dictionary methods = new(); + + // Reflection to get the ContractMethods byte[] script; using (ScriptBuilder sb = new()) { - foreach (ContractMethodMetadata method in descriptors) + foreach (ContractMethodMetadata method in methodDescriptors.Where(u => u.ActiveIn is null || settings.IsHardforkEnabled(u.ActiveIn.Value, index))) { method.Descriptor.Offset = sb.Length; sb.EmitPush(0); //version @@ -133,31 +189,118 @@ protected NativeContract() } script = sb.ToArray(); } - this.Nef = new NefFile + + return new NativeContractsCache.CacheEntry() { Methods = methods, Script = script }; + } + + /// + /// The of the native contract. + /// + /// The where the HardForks are configured. + /// Block index + /// The . + public ContractState GetContractState(ProtocolSettings settings, uint index) + { + // Get allowed methods and nef script + NativeContractsCache.CacheEntry allowedMethods = GetAllowedMethods(settings, index); + + // Compose nef file + NefFile nef = new() { Compiler = "neo-core-v3.0", Source = string.Empty, Tokens = Array.Empty(), - Script = script + Script = allowedMethods.Script }; - this.Nef.CheckSum = NefFile.ComputeChecksum(Nef); - this.Hash = Helper.GetContractHash(UInt160.Zero, 0, Name); - this.Manifest = new ContractManifest + nef.CheckSum = NefFile.ComputeChecksum(nef); + + // Compose manifest + ContractManifest manifest = new() { Name = Name, Groups = Array.Empty(), SupportedStandards = Array.Empty(), Abi = new ContractAbi() { - Events = Array.Empty(), - Methods = descriptors.Select(p => p.Descriptor).ToArray() + Events = eventsDescriptors + .Where(u => u.ActiveIn is null || settings.IsHardforkEnabled(u.ActiveIn.Value, index)) + .Select(p => p.Descriptor).ToArray(), + Methods = allowedMethods.Methods.Values + .Select(p => p.Descriptor).ToArray() }, Permissions = new[] { ContractPermission.DefaultPermission }, Trusts = WildcardContainer.Create(), Extra = null }; - contractsList.Add(this); - contractsDictionary.Add(Hash, this); + + OnManifestCompose(manifest); + + // Return ContractState + return new ContractState + { + Id = Id, + Nef = nef, + Hash = Hash, + Manifest = manifest + }; + } + + protected virtual void OnManifestCompose(ContractManifest manifest) { } + + /// + /// It is the initialize block + /// + /// The where the HardForks are configured. + /// Block index + /// Active hardfork + /// True if the native contract must be initialized + internal bool IsInitializeBlock(ProtocolSettings settings, uint index, out Hardfork? hardfork) + { + // If is not configured, the Genesis is the a initialized block + if (index == 0 && ActiveIn is null) + { + hardfork = null; + return true; + } + + // If is in the hardfork height, return true + foreach (Hardfork hf in usedHardforks) + { + if (!settings.Hardforks.TryGetValue(hf, out var activeIn)) + { + // If is not set in the configuration is treated as enabled from the genesis + activeIn = 0; + } + + if (activeIn == index) + { + hardfork = hf; + return true; + } + } + + // Initialized not required + hardfork = null; + return false; + } + + /// + /// Is the native contract active + /// + /// The where the HardForks are configured. + /// Block index + /// True if the native contract is active + internal bool IsActive(ProtocolSettings settings, uint index) + { + if (ActiveIn is null) return true; + + if (!settings.Hardforks.TryGetValue(ActiveIn.Value, out var activeIn)) + { + // If is not set in the configuration is treated as enabled from the genesis + activeIn = 0; + } + + return activeIn <= index; } /// @@ -193,23 +336,33 @@ internal async void Invoke(ApplicationEngine engine, byte version) { if (version != 0) throw new InvalidOperationException($"The native contract of version {version} is not active."); + // Get native contracts invocation cache + NativeContractsCache nativeContracts = engine.GetState(() => new NativeContractsCache()); + NativeContractsCache.CacheEntry currentAllowedMethods = nativeContracts.GetAllowedMethods(this, engine); + // Check if the method is allowed ExecutionContext context = engine.CurrentContext; - ContractMethodMetadata method = methods[context.InstructionPointer]; + ContractMethodMetadata method = currentAllowedMethods.Methods[context.InstructionPointer]; + if (method.ActiveIn is not null && !engine.IsHardforkEnabled(method.ActiveIn.Value)) + throw new InvalidOperationException($"Cannot call this method before hardfork {method.ActiveIn}."); ExecutionContextState state = context.GetState(); if (!state.CallFlags.HasFlag(method.RequiredCallFlags)) throw new InvalidOperationException($"Cannot call this method with the flag {state.CallFlags}."); - engine.AddGas(method.CpuFee * Policy.GetExecFeeFactor(engine.Snapshot) + method.StorageFee * Policy.GetStoragePrice(engine.Snapshot)); + engine.AddGas(method.CpuFee * engine.ExecFeeFactor + method.StorageFee * engine.StoragePrice); List parameters = new(); if (method.NeedApplicationEngine) parameters.Add(engine); if (method.NeedSnapshot) parameters.Add(engine.Snapshot); for (int i = 0; i < method.Parameters.Length; i++) - parameters.Add(engine.Convert(context.EvaluationStack.Pop(), method.Parameters[i])); + parameters.Add(engine.Convert(context.EvaluationStack.Peek(i), method.Parameters[i])); object returnValue = method.Handler.Invoke(this, parameters.ToArray()); if (returnValue is ContractTask task) { await task; returnValue = task.GetResult(); } + for (int i = 0; i < method.Parameters.Length; i++) + { + context.EvaluationStack.Pop(); + } if (method.Handler.ReturnType != typeof(void) && method.Handler.ReturnType != typeof(ContractTask)) { context.EvaluationStack.Push(engine.Convert(returnValue)); @@ -231,7 +384,7 @@ public static bool IsNative(UInt160 hash) return contractsDictionary.ContainsKey(hash); } - internal virtual ContractTask Initialize(ApplicationEngine engine) + internal virtual ContractTask Initialize(ApplicationEngine engine, Hardfork? hardFork) { return ContractTask.CompletedTask; } diff --git a/src/neo/SmartContract/Native/NeoToken.cs b/src/Neo/SmartContract/Native/NeoToken.cs similarity index 65% rename from src/neo/SmartContract/Native/NeoToken.cs rename to src/Neo/SmartContract/Native/NeoToken.cs index 31a44a117d..ed796a9756 100644 --- a/src/neo/SmartContract/Native/NeoToken.cs +++ b/src/Neo/SmartContract/Native/NeoToken.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// NeoToken.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. @@ -13,6 +14,7 @@ using Neo.Cryptography.ECC; using Neo.IO; using Neo.Persistence; +using Neo.SmartContract.Iterators; using Neo.VM; using Neo.VM.Types; using System; @@ -52,7 +54,16 @@ public sealed class NeoToken : FungibleToken private const byte CommitteeRewardRatio = 10; private const byte VoterRewardRatio = 80; - internal NeoToken() + [ContractEvent(1, name: "CandidateStateChanged", + "pubkey", ContractParameterType.PublicKey, + "registered", ContractParameterType.Boolean, + "votes", ContractParameterType.Integer)] + [ContractEvent(2, name: "Vote", + "account", ContractParameterType.Hash160, + "from", ContractParameterType.PublicKey, + "to", ContractParameterType.PublicKey, + "amount", ContractParameterType.Integer)] + internal NeoToken() : base() { this.TotalAmount = 100000000 * Factor; } @@ -62,9 +73,14 @@ public override BigInteger TotalSupply(DataCache snapshot) return TotalAmount; } - internal override async ContractTask OnBalanceChanging(ApplicationEngine engine, UInt160 account, NeoAccountState state, BigInteger amount) + internal override void OnBalanceChanging(ApplicationEngine engine, UInt160 account, NeoAccountState state, BigInteger amount) { - await DistributeGas(engine, account, state); + GasDistribution distribution = DistributeGas(engine, account, state); + if (distribution is not null) + { + var list = engine.CurrentContext.GetState>(); + list.Add(distribution); + } if (amount.IsZero) return; if (state.VoteTo is null) return; engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_VotersCount)).Add(amount); @@ -74,34 +90,51 @@ internal override async ContractTask OnBalanceChanging(ApplicationEngine engine, CheckCandidate(engine.Snapshot, state.VoteTo, candidate); } - private async ContractTask DistributeGas(ApplicationEngine engine, UInt160 account, NeoAccountState state) + private protected override async ContractTask PostTransfer(ApplicationEngine engine, UInt160 from, UInt160 to, BigInteger amount, StackItem data, bool callOnPayment) + { + await base.PostTransfer(engine, from, to, amount, data, callOnPayment); + var list = engine.CurrentContext.GetState>(); + foreach (var distribution in list) + await GAS.Mint(engine, distribution.Account, distribution.Amount, callOnPayment); + } + + private GasDistribution DistributeGas(ApplicationEngine engine, UInt160 account, NeoAccountState state) { // PersistingBlock is null when running under the debugger - if (engine.PersistingBlock is null) return; + if (engine.PersistingBlock is null) return null; - BigInteger gas = CalculateBonus(engine.Snapshot, state.VoteTo, state.Balance, state.BalanceHeight, engine.PersistingBlock.Index); + BigInteger gas = CalculateBonus(engine.Snapshot, state, engine.PersistingBlock.Index); state.BalanceHeight = engine.PersistingBlock.Index; - await GAS.Mint(engine, account, gas, true); + if (state.VoteTo is not null) + { + var keyLastest = CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(state.VoteTo); + var latestGasPerVote = engine.Snapshot.TryGet(keyLastest) ?? BigInteger.Zero; + state.LastGasPerVote = latestGasPerVote; + } + if (gas == 0) return null; + return new GasDistribution + { + Account = account, + Amount = gas + }; } - private BigInteger CalculateBonus(DataCache snapshot, ECPoint vote, BigInteger value, uint start, uint end) + private BigInteger CalculateBonus(DataCache snapshot, NeoAccountState state, uint end) { - if (value.IsZero || start >= end) return BigInteger.Zero; - if (value.Sign < 0) throw new ArgumentOutOfRangeException(nameof(value)); - - BigInteger neoHolderReward = CalculateNeoHolderReward(snapshot, value, start, end); - if (vote is null) return neoHolderReward; + if (state.Balance.IsZero) return BigInteger.Zero; + if (state.Balance.Sign < 0) throw new ArgumentOutOfRangeException(nameof(state.Balance)); - byte[] border = CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(vote).ToArray(); - byte[] keyStart = CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(vote).AddBigEndian(start).ToArray(); - (_, var item) = snapshot.FindRange(keyStart, border, SeekDirection.Backward).FirstOrDefault(); - BigInteger startRewardPerNeo = item ?? BigInteger.Zero; + var expectEnd = Ledger.CurrentIndex(snapshot) + 1; + if (expectEnd != end) throw new ArgumentOutOfRangeException(nameof(end)); + if (state.BalanceHeight >= end) return BigInteger.Zero; + BigInteger neoHolderReward = CalculateNeoHolderReward(snapshot, state.Balance, state.BalanceHeight, end); + if (state.VoteTo is null) return neoHolderReward; - byte[] keyEnd = CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(vote).AddBigEndian(end).ToArray(); - (_, item) = snapshot.FindRange(keyEnd, border, SeekDirection.Backward).FirstOrDefault(); - BigInteger endRewardPerNeo = item ?? BigInteger.Zero; + var keyLastest = CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(state.VoteTo); + var latestGasPerVote = snapshot.TryGet(keyLastest) ?? BigInteger.Zero; + var voteReward = state.Balance * (latestGasPerVote - state.LastGasPerVote) / 100000000L; - return neoHolderReward + value * (endRewardPerNeo - startRewardPerNeo) / 100000000L; + return neoHolderReward + voteReward; } private BigInteger CalculateNeoHolderReward(DataCache snapshot, BigInteger value, uint start, uint end) @@ -127,8 +160,7 @@ private void CheckCandidate(DataCache snapshot, ECPoint pubkey, CandidateState c { if (!candidate.Registered && candidate.Votes.IsZero) { - foreach (var (rewardKey, _) in snapshot.Find(CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(pubkey).ToArray()).ToArray()) - snapshot.Delete(rewardKey); + snapshot.Delete(CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(pubkey)); snapshot.Delete(CreateStorageKey(Prefix_Candidate).Add(pubkey)); } } @@ -141,14 +173,18 @@ private void CheckCandidate(DataCache snapshot, ECPoint pubkey, CandidateState c /// if the votes should be recounted; otherwise, . public static bool ShouldRefreshCommittee(uint height, int committeeMembersCount) => height % committeeMembersCount == 0; - internal override ContractTask Initialize(ApplicationEngine engine) + internal override ContractTask Initialize(ApplicationEngine engine, Hardfork? hardfork) { - var cachedCommittee = new CachedCommittee(engine.ProtocolSettings.StandbyCommittee.Select(p => (p, BigInteger.Zero))); - engine.Snapshot.Add(CreateStorageKey(Prefix_Committee), new StorageItem(cachedCommittee)); - engine.Snapshot.Add(CreateStorageKey(Prefix_VotersCount), new StorageItem(System.Array.Empty())); - engine.Snapshot.Add(CreateStorageKey(Prefix_GasPerBlock).AddBigEndian(0u), new StorageItem(5 * GAS.Factor)); - engine.Snapshot.Add(CreateStorageKey(Prefix_RegisterPrice), new StorageItem(1000 * GAS.Factor)); - return Mint(engine, Contract.GetBFTAddress(engine.ProtocolSettings.StandbyValidators), TotalAmount, false); + if (hardfork == ActiveIn) + { + var cachedCommittee = new CachedCommittee(engine.ProtocolSettings.StandbyCommittee.Select(p => (p, BigInteger.Zero))); + engine.Snapshot.Add(CreateStorageKey(Prefix_Committee), new StorageItem(cachedCommittee)); + engine.Snapshot.Add(CreateStorageKey(Prefix_VotersCount), new StorageItem(System.Array.Empty())); + engine.Snapshot.Add(CreateStorageKey(Prefix_GasPerBlock).AddBigEndian(0u), new StorageItem(5 * GAS.Factor)); + engine.Snapshot.Add(CreateStorageKey(Prefix_RegisterPrice), new StorageItem(1000 * GAS.Factor)); + return Mint(engine, Contract.GetBFTAddress(engine.ProtocolSettings.StandbyValidators), TotalAmount, false); + } + return ContractTask.CompletedTask; } internal override ContractTask OnPersist(ApplicationEngine engine) @@ -173,7 +209,7 @@ internal override async ContractTask PostPersist(ApplicationEngine engine) int index = (int)(engine.PersistingBlock.Index % (uint)m); var gasPerBlock = GetGasPerBlock(engine.Snapshot); var committee = GetCommitteeFromCache(engine.Snapshot); - var pubkey = committee.ElementAt(index).PublicKey; + var pubkey = committee[index].PublicKey; var account = Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash(); await GAS.Mint(engine, account, gasPerBlock * CommitteeRewardRatio / 100, false); @@ -184,16 +220,14 @@ internal override async ContractTask PostPersist(ApplicationEngine engine) BigInteger voterRewardOfEachCommittee = gasPerBlock * VoterRewardRatio * 100000000L * m / (m + n) / 100; // Zoom in 100000000 times, and the final calculation should be divided 100000000L for (index = 0; index < committee.Count; index++) { - var member = committee.ElementAt(index); + var (PublicKey, Votes) = committee[index]; var factor = index < n ? 2 : 1; // The `voter` rewards of validator will double than other committee's - if (member.Votes > 0) + if (Votes > 0) { - BigInteger voterSumRewardPerNEO = factor * voterRewardOfEachCommittee / member.Votes; - StorageKey voterRewardKey = CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(member.PublicKey).AddBigEndian(engine.PersistingBlock.Index + 1); - byte[] border = CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(member.PublicKey).ToArray(); - (_, var item) = engine.Snapshot.FindRange(voterRewardKey.ToArray(), border, SeekDirection.Backward).FirstOrDefault(); - voterSumRewardPerNEO += (item ?? BigInteger.Zero); - engine.Snapshot.Add(voterRewardKey, new StorageItem(voterSumRewardPerNEO)); + BigInteger voterSumRewardPerNEO = factor * voterRewardOfEachCommittee / Votes; + StorageKey voterRewardKey = CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(PublicKey); + StorageItem lastRewardPerNeo = engine.Snapshot.GetAndChange(voterRewardKey, () => new StorageItem(BigInteger.Zero)); + lastRewardPerNeo.Add(voterSumRewardPerNEO); } } } @@ -247,7 +281,7 @@ public long GetRegisterPrice(DataCache snapshot) byte[] key = CreateStorageKey(Prefix_GasPerBlock).AddBigEndian(end).ToArray(); byte[] boundary = CreateStorageKey(Prefix_GasPerBlock).ToArray(); return snapshot.FindRange(key, boundary, SeekDirection.Backward) - .Select(u => (BinaryPrimitives.ReadUInt32BigEndian(u.Key.Key.AsSpan(^sizeof(uint))), (BigInteger)u.Value)); + .Select(u => (BinaryPrimitives.ReadUInt32BigEndian(u.Key.Key.Span[^sizeof(uint)..]), (BigInteger)u.Value)); } /// @@ -263,7 +297,7 @@ public BigInteger UnclaimedGas(DataCache snapshot, UInt160 account, uint end) StorageItem storage = snapshot.TryGet(CreateStorageKey(Prefix_Account).Add(account)); if (storage is null) return BigInteger.Zero; NeoAccountState state = storage.GetInteroperable(); - return CalculateBonus(snapshot, state.VoteTo, state.Balance, state.BalanceHeight, end); + return CalculateBonus(snapshot, state, end); } [ContractMethod(RequiredCallFlags = CallFlags.States)] @@ -275,7 +309,10 @@ private bool RegisterCandidate(ApplicationEngine engine, ECPoint pubkey) StorageKey key = CreateStorageKey(Prefix_Candidate).Add(pubkey); StorageItem item = engine.Snapshot.GetAndChange(key, () => new StorageItem(new CandidateState())); CandidateState state = item.GetInteroperable(); + if (state.Registered) return true; state.Registered = true; + engine.SendNotification(Hash, "CandidateStateChanged", + new VM.Types.Array(engine.ReferenceCounter) { pubkey.ToArray(), true, state.Votes }); return true; } @@ -288,8 +325,11 @@ private bool UnregisterCandidate(ApplicationEngine engine, ECPoint pubkey) if (engine.Snapshot.TryGet(key) is null) return true; StorageItem item = engine.Snapshot.GetAndChange(key); CandidateState state = item.GetInteroperable(); + if (!state.Registered) return true; state.Registered = false; CheckCandidate(engine.Snapshot, pubkey, state); + engine.SendNotification(Hash, "CandidateStateChanged", + new VM.Types.Array(engine.ReferenceCounter) { pubkey.ToArray(), false, state.Votes }); return true; } @@ -299,6 +339,7 @@ private async ContractTask Vote(ApplicationEngine engine, UInt160 account, if (!engine.CheckWitnessInternal(account)) return false; NeoAccountState state_account = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Account).Add(account))?.GetInteroperable(); if (state_account is null) return false; + if (state_account.Balance == 0) return false; CandidateState validator_new = null; if (voteTo != null) { @@ -314,7 +355,7 @@ private async ContractTask Vote(ApplicationEngine engine, UInt160 account, else item.Add(-state_account.Balance); } - await DistributeGas(engine, account, state_account); + GasDistribution gasDistribution = DistributeGas(engine, account, state_account); if (state_account.VoteTo != null) { StorageKey key = CreateStorageKey(Prefix_Candidate).Add(state_account.VoteTo); @@ -323,28 +364,76 @@ private async ContractTask Vote(ApplicationEngine engine, UInt160 account, state_validator.Votes -= state_account.Balance; CheckCandidate(engine.Snapshot, state_account.VoteTo, state_validator); } + if (voteTo != null && voteTo != state_account.VoteTo) + { + StorageKey voterRewardKey = CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(voteTo); + var latestGasPerVote = engine.Snapshot.TryGet(voterRewardKey) ?? BigInteger.Zero; + state_account.LastGasPerVote = latestGasPerVote; + } + ECPoint from = state_account.VoteTo; state_account.VoteTo = voteTo; + if (validator_new != null) { validator_new.Votes += state_account.Balance; } + engine.SendNotification(Hash, "Vote", + new VM.Types.Array(engine.ReferenceCounter) { account.ToArray(), from?.ToArray() ?? StackItem.Null, voteTo?.ToArray() ?? StackItem.Null, state_account.Balance }); + if (gasDistribution is not null) + await GAS.Mint(engine, gasDistribution.Account, gasDistribution.Amount, true); return true; } /// - /// Gets all registered candidates. + /// Gets the first 256 registered candidates. + /// + /// The snapshot used to read data. + /// All the registered candidates. + [ContractMethod(CpuFee = 1 << 22, RequiredCallFlags = CallFlags.ReadStates)] + private (ECPoint PublicKey, BigInteger Votes)[] GetCandidates(DataCache snapshot) + { + return GetCandidatesInternal(snapshot) + .Select(p => (p.PublicKey, p.State.Votes)) + .Take(256) + .ToArray(); + } + + /// + /// Gets the registered candidates iterator. /// /// The snapshot used to read data. /// All the registered candidates. [ContractMethod(CpuFee = 1 << 22, RequiredCallFlags = CallFlags.ReadStates)] - public (ECPoint PublicKey, BigInteger Votes)[] GetCandidates(DataCache snapshot) + private IIterator GetAllCandidates(DataCache snapshot) + { + const FindOptions options = FindOptions.RemovePrefix | FindOptions.DeserializeValues | FindOptions.PickField1; + var enumerator = GetCandidatesInternal(snapshot) + .Select(p => (p.Key, p.Value)) + .GetEnumerator(); + return new StorageIterator(enumerator, 1, options); + } + + internal IEnumerable<(StorageKey Key, StorageItem Value, ECPoint PublicKey, CandidateState State)> GetCandidatesInternal(DataCache snapshot) { byte[] prefix_key = CreateStorageKey(Prefix_Candidate).ToArray(); - return snapshot.Find(prefix_key).Select(p => - ( - p.Key.Key.AsSerializable(1), - p.Value.GetInteroperable() - )).Where(p => p.Item2.Registered).Select(p => (p.Item1, p.Item2.Votes)).ToArray(); + return snapshot.Find(prefix_key) + .Select(p => (p.Key, p.Value, PublicKey: p.Key.Key[1..].AsSerializable(), State: p.Value.GetInteroperable())) + .Where(p => p.State.Registered) + .Where(p => !Policy.IsBlocked(snapshot, Contract.CreateSignatureRedeemScript(p.PublicKey).ToScriptHash())); + } + + /// + /// Gets votes from specific candidate. + /// + /// The snapshot used to read data. + /// Specific public key + /// Votes or -1 if it was not found. + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] + public BigInteger GetCandidateVote(DataCache snapshot, ECPoint pubKey) + { + StorageItem storage = snapshot.TryGet(CreateStorageKey(Prefix_Candidate).Add(pubKey)); + CandidateState state = storage?.GetInteroperable(); + return state?.Registered == true ? state.Votes : -1; } /// @@ -401,10 +490,15 @@ public ECPoint[] ComputeNextBlockValidators(DataCache snapshot, ProtocolSettings { decimal votersCount = (decimal)(BigInteger)snapshot[CreateStorageKey(Prefix_VotersCount)]; decimal voterTurnout = votersCount / (decimal)TotalAmount; - var candidates = GetCandidates(snapshot); + var candidates = GetCandidatesInternal(snapshot) + .Select(p => (p.PublicKey, p.State.Votes)) + .ToArray(); if (voterTurnout < EffectiveVoterTurnout || candidates.Length < settings.CommitteeMembersCount) return settings.StandbyCommittee.Select(p => (p, candidates.FirstOrDefault(k => k.PublicKey.Equals(p)).Votes)); - return candidates.OrderByDescending(p => p.Votes).ThenBy(p => p.PublicKey).Take(settings.CommitteeMembersCount); + return candidates + .OrderByDescending(p => p.Votes) + .ThenBy(p => p.PublicKey) + .Take(settings.CommitteeMembersCount); } [ContractMethod(CpuFee = 1 << 16, RequiredCallFlags = CallFlags.ReadStates)] @@ -443,12 +537,15 @@ public class NeoAccountState : AccountState /// public ECPoint VoteTo; + public BigInteger LastGasPerVote; + public override void FromStackItem(StackItem stackItem) { base.FromStackItem(stackItem); Struct @struct = (Struct)stackItem; BalanceHeight = (uint)@struct[1].GetInteger(); - VoteTo = @struct[2].IsNull ? null : @struct[2].GetSpan().AsSerializable(); + VoteTo = @struct[2].IsNull ? null : ECPoint.DecodePoint(@struct[2].GetSpan(), ECCurve.Secp256r1); + LastGasPerVote = @struct[3].GetInteger(); } public override StackItem ToStackItem(ReferenceCounter referenceCounter) @@ -456,13 +553,14 @@ public override StackItem ToStackItem(ReferenceCounter referenceCounter) Struct @struct = (Struct)base.ToStackItem(referenceCounter); @struct.Add(BalanceHeight); @struct.Add(VoteTo?.ToArray() ?? StackItem.Null); + @struct.Add(LastGasPerVote); return @struct; } } internal class CandidateState : IInteroperable { - public bool Registered = true; + public bool Registered; public BigInteger Votes; public void FromStackItem(StackItem stackItem) @@ -478,29 +576,27 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter) } } - internal class CachedCommittee : List<(ECPoint PublicKey, BigInteger Votes)>, IInteroperable + internal class CachedCommittee : InteroperableList<(ECPoint PublicKey, BigInteger Votes)> { - public CachedCommittee() - { - } + public CachedCommittee() { } + public CachedCommittee(IEnumerable<(ECPoint, BigInteger)> collection) => AddRange(collection); - public CachedCommittee(IEnumerable<(ECPoint PublicKey, BigInteger Votes)> collection) : base(collection) + protected override (ECPoint, BigInteger) ElementFromStackItem(StackItem item) { + Struct @struct = (Struct)item; + return (ECPoint.DecodePoint(@struct[0].GetSpan(), ECCurve.Secp256r1), @struct[1].GetInteger()); } - public void FromStackItem(StackItem stackItem) + protected override StackItem ElementToStackItem((ECPoint PublicKey, BigInteger Votes) element, ReferenceCounter referenceCounter) { - foreach (StackItem item in (VM.Types.Array)stackItem) - { - Struct @struct = (Struct)item; - Add((@struct[0].GetSpan().AsSerializable(), @struct[1].GetInteger())); - } + return new Struct(referenceCounter) { element.PublicKey.ToArray(), element.Votes }; } + } - public StackItem ToStackItem(ReferenceCounter referenceCounter) - { - return new VM.Types.Array(referenceCounter, this.Select(p => new Struct(referenceCounter, new StackItem[] { p.PublicKey.ToArray(), p.Votes }))); - } + private record GasDistribution + { + public UInt160 Account { get; init; } + public BigInteger Amount { get; init; } } } } diff --git a/src/neo/SmartContract/Native/OracleContract.cs b/src/Neo/SmartContract/Native/OracleContract.cs similarity index 75% rename from src/neo/SmartContract/Native/OracleContract.cs rename to src/Neo/SmartContract/Native/OracleContract.cs index b4b5609dd9..d8415fca21 100644 --- a/src/neo/SmartContract/Native/OracleContract.cs +++ b/src/Neo/SmartContract/Native/OracleContract.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// OracleContract.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. @@ -14,7 +15,6 @@ using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.Persistence; -using Neo.SmartContract.Manifest; using Neo.VM; using Neo.VM.Types; using System; @@ -40,58 +40,15 @@ public sealed class OracleContract : NativeContract private const byte Prefix_Request = 7; private const byte Prefix_IdList = 6; - internal OracleContract() - { - var events = new List(Manifest.Abi.Events) - { - new ContractEventDescriptor - { - Name = "OracleRequest", - Parameters = new ContractParameterDefinition[] - { - new ContractParameterDefinition() - { - Name = "Id", - Type = ContractParameterType.Integer - }, - new ContractParameterDefinition() - { - Name = "RequestContract", - Type = ContractParameterType.Hash160 - }, - new ContractParameterDefinition() - { - Name = "Url", - Type = ContractParameterType.String - }, - new ContractParameterDefinition() - { - Name = "Filter", - Type = ContractParameterType.String - } - } - }, - new ContractEventDescriptor - { - Name = "OracleResponse", - Parameters = new ContractParameterDefinition[] - { - new ContractParameterDefinition() - { - Name = "Id", - Type = ContractParameterType.Integer - }, - new ContractParameterDefinition() - { - Name = "OriginalTx", - Type = ContractParameterType.Hash256 - } - } - } - }; - - Manifest.Abi.Events = events.ToArray(); - } + [ContractEvent(0, name: "OracleRequest", + "Id", ContractParameterType.Integer, + "RequestContract", ContractParameterType.Hash160, + "Url", ContractParameterType.String, + "Filter", ContractParameterType.String)] + [ContractEvent(1, name: "OracleResponse", + "Id", ContractParameterType.Integer, + "OriginalTx", ContractParameterType.Hash256)] + internal OracleContract() : base() { } [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private void SetPrice(ApplicationEngine engine, long price) @@ -116,12 +73,14 @@ public long GetPrice(DataCache snapshot) [ContractMethod(RequiredCallFlags = CallFlags.States | CallFlags.AllowCall | CallFlags.AllowNotify)] private ContractTask Finish(ApplicationEngine engine) { + if (engine.InvocationStack.Count != 2) throw new InvalidOperationException(); + if (engine.GetInvocationCounter() != 1) throw new InvalidOperationException(); Transaction tx = (Transaction)engine.ScriptContainer; OracleResponse response = tx.GetAttribute(); if (response == null) throw new ArgumentException("Oracle response was not found"); OracleRequest request = GetRequest(engine.Snapshot, response.Id); if (request == null) throw new ArgumentException("Oracle request was not found"); - engine.SendNotification(Hash, "OracleResponse", new VM.Types.Array { response.Id, request.OriginalTxid.ToArray() }); + engine.SendNotification(Hash, "OracleResponse", new VM.Types.Array(engine.ReferenceCounter) { response.Id, request.OriginalTxid.ToArray() }); StackItem userData = BinarySerializer.Deserialize(request.UserData, engine.Limits, engine.ReferenceCounter); return engine.CallFromNativeContract(Hash, request.CallbackContract, request.CallbackMethod, request.Url, userData, (int)response.Code, response.Result); } @@ -153,7 +112,7 @@ public OracleRequest GetRequest(DataCache snapshot, ulong id) /// All the pending requests. public IEnumerable<(ulong, OracleRequest)> GetRequests(DataCache snapshot) { - return snapshot.Find(CreateStorageKey(Prefix_Request).ToArray()).Select(p => (BinaryPrimitives.ReadUInt64BigEndian(p.Key.Key.AsSpan(1)), p.Value.GetInteroperable())); + return snapshot.Find(CreateStorageKey(Prefix_Request).ToArray()).Select(p => (BinaryPrimitives.ReadUInt64BigEndian(p.Key.Key.Span[1..]), p.Value.GetInteroperable())); } /// @@ -175,10 +134,13 @@ private static byte[] GetUrlHash(string url) return Crypto.Hash160(Utility.StrictUTF8.GetBytes(url)); } - internal override ContractTask Initialize(ApplicationEngine engine) + internal override ContractTask Initialize(ApplicationEngine engine, Hardfork? hardfork) { - engine.Snapshot.Add(CreateStorageKey(Prefix_RequestId), new StorageItem(BigInteger.Zero)); - engine.Snapshot.Add(CreateStorageKey(Prefix_Price), new StorageItem(0_50000000)); + if (hardfork == ActiveIn) + { + engine.Snapshot.Add(CreateStorageKey(Prefix_RequestId), new StorageItem(BigInteger.Zero)); + engine.Snapshot.Add(CreateStorageKey(Prefix_Price), new StorageItem(0_50000000)); + } return ContractTask.CompletedTask; } @@ -253,7 +215,7 @@ private async ContractTask Request(ApplicationEngine engine, string url, string Filter = filter, CallbackContract = engine.CallingScriptHash, CallbackMethod = callback, - UserData = BinarySerializer.Serialize(userData, MaxUserDataLength) + UserData = BinarySerializer.Serialize(userData, MaxUserDataLength, engine.Limits.MaxStackSize) })); //Add the id to the IdList @@ -262,7 +224,7 @@ private async ContractTask Request(ApplicationEngine engine, string url, string throw new InvalidOperationException("There are too many pending responses for this url"); list.Add(id); - engine.SendNotification(Hash, "OracleRequest", new VM.Types.Array { id, engine.CallingScriptHash.ToArray(), url, filter ?? StackItem.Null }); + engine.SendNotification(Hash, "OracleRequest", new VM.Types.Array(engine.ReferenceCounter) { id, engine.CallingScriptHash.ToArray(), url, filter ?? StackItem.Null }); } [ContractMethod(CpuFee = 1 << 15)] @@ -272,17 +234,16 @@ private bool Verify(ApplicationEngine engine) return tx?.GetAttribute() != null; } - private class IdList : List, IInteroperable + private class IdList : InteroperableList { - public void FromStackItem(StackItem stackItem) + protected override ulong ElementFromStackItem(StackItem item) { - foreach (StackItem item in (VM.Types.Array)stackItem) - Add((ulong)item.GetInteger()); + return (ulong)item.GetInteger(); } - public StackItem ToStackItem(ReferenceCounter referenceCounter) + protected override StackItem ElementToStackItem(ulong element, ReferenceCounter referenceCounter) { - return new VM.Types.Array(referenceCounter, this.Select(p => (Integer)p)); + return element; } } } diff --git a/src/neo/SmartContract/Native/OracleRequest.cs b/src/Neo/SmartContract/Native/OracleRequest.cs similarity index 88% rename from src/neo/SmartContract/Native/OracleRequest.cs rename to src/Neo/SmartContract/Native/OracleRequest.cs index 94316d116b..d18968ef00 100644 --- a/src/neo/SmartContract/Native/OracleRequest.cs +++ b/src/Neo/SmartContract/Native/OracleRequest.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// OracleRequest.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. diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/Neo/SmartContract/Native/PolicyContract.cs similarity index 67% rename from src/neo/SmartContract/Native/PolicyContract.cs rename to src/Neo/SmartContract/Native/PolicyContract.cs index 43c593c711..5b00af92ad 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/Neo/SmartContract/Native/PolicyContract.cs @@ -1,15 +1,17 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// PolicyContract.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. #pragma warning disable IDE0051 +using Neo.Network.P2P.Payloads; using Neo.Persistence; using System; using System.Numerics; @@ -36,11 +38,21 @@ public sealed class PolicyContract : NativeContract /// public const uint DefaultFeePerByte = 1000; + /// + /// The default fee for attribute. + /// + public const uint DefaultAttributeFee = 0; + /// /// The maximum execution fee factor that the committee can set. /// public const uint MaxExecFeeFactor = 100; + /// + /// The maximum fee for attribute that the committee can set. + /// + public const uint MaxAttributeFee = 10_0000_0000; + /// /// The maximum storage price that the committee can set. /// @@ -50,16 +62,18 @@ public sealed class PolicyContract : NativeContract private const byte Prefix_FeePerByte = 10; private const byte Prefix_ExecFeeFactor = 18; private const byte Prefix_StoragePrice = 19; + private const byte Prefix_AttributeFee = 20; - internal PolicyContract() - { - } + internal PolicyContract() : base() { } - internal override ContractTask Initialize(ApplicationEngine engine) + internal override ContractTask Initialize(ApplicationEngine engine, Hardfork? hardfork) { - engine.Snapshot.Add(CreateStorageKey(Prefix_FeePerByte), new StorageItem(DefaultFeePerByte)); - engine.Snapshot.Add(CreateStorageKey(Prefix_ExecFeeFactor), new StorageItem(DefaultExecFeeFactor)); - engine.Snapshot.Add(CreateStorageKey(Prefix_StoragePrice), new StorageItem(DefaultStoragePrice)); + if (hardfork == ActiveIn) + { + engine.Snapshot.Add(CreateStorageKey(Prefix_FeePerByte), new StorageItem(DefaultFeePerByte)); + engine.Snapshot.Add(CreateStorageKey(Prefix_ExecFeeFactor), new StorageItem(DefaultExecFeeFactor)); + engine.Snapshot.Add(CreateStorageKey(Prefix_StoragePrice), new StorageItem(DefaultStoragePrice)); + } return ContractTask.CompletedTask; } @@ -96,6 +110,22 @@ public uint GetStoragePrice(DataCache snapshot) return (uint)(BigInteger)snapshot[CreateStorageKey(Prefix_StoragePrice)]; } + /// + /// Gets the fee for attribute. + /// + /// The snapshot used to read data. + /// Attribute type + /// The fee for attribute. + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] + public uint GetAttributeFee(DataCache snapshot, byte attributeType) + { + if (!Enum.IsDefined(typeof(TransactionAttributeType), attributeType)) throw new InvalidOperationException(); + StorageItem entry = snapshot.TryGet(CreateStorageKey(Prefix_AttributeFee).Add(attributeType)); + if (entry == null) return DefaultAttributeFee; + + return (uint)(BigInteger)entry; + } + /// /// Determines whether the specified account is blocked. /// @@ -108,6 +138,16 @@ public bool IsBlocked(DataCache snapshot, UInt160 account) return snapshot.Contains(CreateStorageKey(Prefix_BlockedAccount).Add(account)); } + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] + private void SetAttributeFee(ApplicationEngine engine, byte attributeType, uint value) + { + if (!Enum.IsDefined(typeof(TransactionAttributeType), attributeType)) throw new InvalidOperationException(); + if (value > MaxAttributeFee) throw new ArgumentOutOfRangeException(nameof(value)); + if (!CheckCommittee(engine)) throw new InvalidOperationException(); + + engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_AttributeFee).Add(attributeType), () => new StorageItem(DefaultAttributeFee)).Set(value); + } + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private void SetFeePerByte(ApplicationEngine engine, long value) { @@ -136,12 +176,17 @@ private void SetStoragePrice(ApplicationEngine engine, uint value) private bool BlockAccount(ApplicationEngine engine, UInt160 account) { if (!CheckCommittee(engine)) throw new InvalidOperationException(); + return BlockAccount(engine.Snapshot, account); + } + + internal bool BlockAccount(DataCache snapshot, UInt160 account) + { if (IsNative(account)) throw new InvalidOperationException("It's impossible to block a native contract."); var key = CreateStorageKey(Prefix_BlockedAccount).Add(account); - if (engine.Snapshot.Contains(key)) return false; + if (snapshot.Contains(key)) return false; - engine.Snapshot.Add(key, new StorageItem(Array.Empty())); + snapshot.Add(key, new StorageItem(Array.Empty())); return true; } diff --git a/src/neo/SmartContract/Native/Role.cs b/src/Neo/SmartContract/Native/Role.cs similarity index 69% rename from src/neo/SmartContract/Native/Role.cs rename to src/Neo/SmartContract/Native/Role.cs index 0c764d1a43..c0954d8fcd 100644 --- a/src/neo/SmartContract/Native/Role.cs +++ b/src/Neo/SmartContract/Native/Role.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Role.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. diff --git a/src/neo/SmartContract/Native/RoleManagement.cs b/src/Neo/SmartContract/Native/RoleManagement.cs similarity index 64% rename from src/neo/SmartContract/Native/RoleManagement.cs rename to src/Neo/SmartContract/Native/RoleManagement.cs index 7b43d98b8d..6e989b78cf 100644 --- a/src/neo/SmartContract/Native/RoleManagement.cs +++ b/src/Neo/SmartContract/Native/RoleManagement.cs @@ -1,22 +1,20 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// RoleManagement.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.Cryptography; using Neo.Cryptography.ECC; using Neo.IO; using Neo.Persistence; -using Neo.SmartContract.Manifest; using Neo.VM; using Neo.VM.Types; using System; -using System.Collections.Generic; using System.Linq; namespace Neo.SmartContract.Native @@ -26,31 +24,10 @@ namespace Neo.SmartContract.Native /// public sealed class RoleManagement : NativeContract { - internal RoleManagement() - { - var events = new List(Manifest.Abi.Events) - { - new ContractEventDescriptor - { - Name = "Designation", - Parameters = new ContractParameterDefinition[] - { - new ContractParameterDefinition() - { - Name = "Role", - Type = ContractParameterType.Integer - }, - new ContractParameterDefinition() - { - Name = "BlockIndex", - Type = ContractParameterType.Integer - } - } - } - }; - - Manifest.Abi.Events = events.ToArray(); - } + [ContractEvent(0, name: "Designation", + "Role", ContractParameterType.Integer, + "BlockIndex", ContractParameterType.Integer)] + internal RoleManagement() : base() { } /// /// Gets the list of nodes for the specified role. @@ -95,17 +72,16 @@ private void DesignateAsRole(ApplicationEngine engine, Role role, ECPoint[] node engine.SendNotification(Hash, "Designation", new VM.Types.Array(engine.ReferenceCounter, new StackItem[] { (int)role, engine.PersistingBlock.Index })); } - private class NodeList : List, IInteroperable + private class NodeList : InteroperableList { - public void FromStackItem(StackItem stackItem) + protected override ECPoint ElementFromStackItem(StackItem item) { - foreach (StackItem item in (VM.Types.Array)stackItem) - Add(item.GetSpan().AsSerializable()); + return ECPoint.DecodePoint(item.GetSpan(), ECCurve.Secp256r1); } - public StackItem ToStackItem(ReferenceCounter referenceCounter) + protected override StackItem ElementToStackItem(ECPoint element, ReferenceCounter referenceCounter) { - return new VM.Types.Array(referenceCounter, this.Select(p => (StackItem)p.ToArray())); + return element.ToArray(); } } } diff --git a/src/neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs similarity index 88% rename from src/neo/SmartContract/Native/StdLib.cs rename to src/Neo/SmartContract/Native/StdLib.cs index a4ee9c5fc6..f8fa9efcc2 100644 --- a/src/neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -1,17 +1,18 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// StdLib.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. #pragma warning disable IDE0051 using Neo.Cryptography; -using Neo.IO.Json; +using Neo.Json; using Neo.VM.Types; using System; using System.Globalization; @@ -26,12 +27,12 @@ public sealed class StdLib : NativeContract { private const int MaxInputLength = 1024; - internal StdLib() { } + internal StdLib() : base() { } [ContractMethod(CpuFee = 1 << 12)] private static byte[] Serialize(ApplicationEngine engine, StackItem item) { - return BinarySerializer.Serialize(item, engine.Limits.MaxItemSize); + return BinarySerializer.Serialize(item, engine.Limits); } [ContractMethod(CpuFee = 1 << 14)] @@ -49,7 +50,7 @@ private static byte[] JsonSerialize(ApplicationEngine engine, StackItem item) [ContractMethod(CpuFee = 1 << 14)] private static StackItem JsonDeserialize(ApplicationEngine engine, byte[] json) { - return JsonSerializer.Deserialize(JObject.Parse(json, 10), engine.Limits, engine.ReferenceCounter); + return JsonSerializer.Deserialize(engine, JToken.Parse(json, 10), engine.Limits, engine.ReferenceCounter); } /// @@ -102,7 +103,7 @@ public static BigInteger Atoi([MaxLength(MaxInputLength)] string value, int @bas { return @base switch { - 10 => BigInteger.Parse(value), + 10 => BigInteger.Parse(value, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture), 16 => BigInteger.Parse(value, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture), _ => throw new ArgumentOutOfRangeException(nameof(@base)) }; @@ -222,5 +223,22 @@ private static string[] StringSplit([MaxLength(MaxInputLength)] string str, stri StringSplitOptions options = removeEmptyEntries ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None; return str.Split(separator, options); } + + [ContractMethod(CpuFee = 1 << 8)] + private static int StrLen([MaxLength(MaxInputLength)] string str) + { + // return the length of the string in elements + // it should return 1 for both "🦆" and "ã" + + TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(str); + int count = 0; + + while (enumerator.MoveNext()) + { + count++; + } + + return count; + } } } diff --git a/src/Neo/SmartContract/Native/TransactionState.cs b/src/Neo/SmartContract/Native/TransactionState.cs new file mode 100644 index 0000000000..b17296b42d --- /dev/null +++ b/src/Neo/SmartContract/Native/TransactionState.cs @@ -0,0 +1,87 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TransactionState.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.IO; +using Neo.Network.P2P.Payloads; +using Neo.VM; +using Neo.VM.Types; +using System; +using System.Linq; + +namespace Neo.SmartContract.Native +{ + /// + /// Represents a transaction that has been included in a block. + /// + public class TransactionState : IInteroperable + { + /// + /// The block containing this transaction. + /// + public uint BlockIndex; + + /// + /// The transaction, if the transaction is trimmed this value will be null + /// + public Transaction Transaction; + + /// + /// The execution state + /// + public VMState State; + + private ReadOnlyMemory _rawTransaction; + + IInteroperable IInteroperable.Clone() + { + return new TransactionState + { + BlockIndex = BlockIndex, + Transaction = Transaction, + State = State, + _rawTransaction = _rawTransaction + }; + } + + void IInteroperable.FromReplica(IInteroperable replica) + { + TransactionState from = (TransactionState)replica; + BlockIndex = from.BlockIndex; + Transaction = from.Transaction; + State = from.State; + if (_rawTransaction.IsEmpty) + _rawTransaction = from._rawTransaction; + } + + void IInteroperable.FromStackItem(StackItem stackItem) + { + Struct @struct = (Struct)stackItem; + BlockIndex = (uint)@struct[0].GetInteger(); + + // Conflict record. + if (@struct.Count == 1) return; + + // Fully-qualified transaction. + _rawTransaction = ((ByteString)@struct[1]).Memory; + Transaction = _rawTransaction.AsSerializable(); + State = (VMState)(byte)@struct[2].GetInteger(); + } + + StackItem IInteroperable.ToStackItem(ReferenceCounter referenceCounter) + { + if (Transaction is null) + return new Struct(referenceCounter) { BlockIndex }; + if (_rawTransaction.IsEmpty) + _rawTransaction = Transaction.ToArray(); + return new Struct(referenceCounter) { BlockIndex, _rawTransaction, (byte)State }; + } + } +} diff --git a/src/neo/SmartContract/Native/TrimmedBlock.cs b/src/Neo/SmartContract/Native/TrimmedBlock.cs similarity index 73% rename from src/neo/SmartContract/Native/TrimmedBlock.cs rename to src/Neo/SmartContract/Native/TrimmedBlock.cs index 349a026f90..4cc4c39c0f 100644 --- a/src/neo/SmartContract/Native/TrimmedBlock.cs +++ b/src/Neo/SmartContract/Native/TrimmedBlock.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// TrimmedBlock.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. @@ -45,7 +46,7 @@ public class TrimmedBlock : IInteroperable, ISerializable public int Size => Header.Size + Hashes.GetVarSize(); - public void Deserialize(BinaryReader reader) + public void Deserialize(ref MemoryReader reader) { Header = reader.ReadSerializable
(); Hashes = reader.ReadSerializableArray(ushort.MaxValue); @@ -57,6 +58,22 @@ public void Serialize(BinaryWriter writer) writer.Write(Hashes); } + IInteroperable IInteroperable.Clone() + { + return new TrimmedBlock + { + Header = Header, + Hashes = Hashes + }; + } + + void IInteroperable.FromReplica(IInteroperable replica) + { + TrimmedBlock from = (TrimmedBlock)replica; + Header = from.Header; + Hashes = from.Hashes; + } + void IInteroperable.FromStackItem(StackItem stackItem) { throw new NotSupportedException(); diff --git a/src/neo/SmartContract/NefFile.cs b/src/Neo/SmartContract/NefFile.cs similarity index 87% rename from src/neo/SmartContract/NefFile.cs rename to src/Neo/SmartContract/NefFile.cs index 6693774e9e..1ffa7baf20 100644 --- a/src/neo/SmartContract/NefFile.cs +++ b/src/Neo/SmartContract/NefFile.cs @@ -1,16 +1,18 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// NefFile.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.Cryptography; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; +using Neo.VM; using System; using System.Buffers.Binary; using System.IO; @@ -64,18 +66,13 @@ public class NefFile : ISerializable /// /// The script of the contract. /// - public byte[] Script { get; set; } + public ReadOnlyMemory Script { get; set; } /// /// The checksum of the nef file. /// public uint CheckSum { get; set; } - /// - /// The maximum length of the script. - /// - public const int MaxScriptLength = 512 * 1024; - private const int HeaderSize = sizeof(uint) + // Magic 64; // Compiler @@ -96,7 +93,7 @@ public void Serialize(BinaryWriter writer) writer.Write((byte)0); writer.Write(Tokens); writer.Write((short)0); - writer.WriteVarBytes(Script ?? Array.Empty()); + writer.WriteVarBytes(Script.Span); writer.Write(CheckSum); } @@ -106,18 +103,20 @@ private void SerializeHeader(BinaryWriter writer) writer.WriteFixedString(Compiler, 64); } - public void Deserialize(BinaryReader reader) + public void Deserialize(ref MemoryReader reader) { + long startPosition = reader.Position; if (reader.ReadUInt32() != Magic) throw new FormatException("Wrong magic"); Compiler = reader.ReadFixedString(64); Source = reader.ReadVarString(256); if (reader.ReadByte() != 0) throw new FormatException("Reserved bytes must be 0"); Tokens = reader.ReadSerializableArray(128); if (reader.ReadUInt16() != 0) throw new FormatException("Reserved bytes must be 0"); - Script = reader.ReadVarBytes(MaxScriptLength); + Script = reader.ReadVarMemory((int)ExecutionEngineLimits.Default.MaxItemSize); if (Script.Length == 0) throw new ArgumentException($"Script can't be empty"); CheckSum = reader.ReadUInt32(); if (CheckSum != ComputeChecksum(this)) throw new FormatException("CRC verification fail"); + if (reader.Position - startPosition > ExecutionEngineLimits.Default.MaxItemSize) throw new FormatException("Max vm item size exceed"); } /// @@ -142,7 +141,7 @@ public JObject ToJson() ["compiler"] = Compiler, ["source"] = Source, ["tokens"] = new JArray(Tokens.Select(p => p.ToJson())), - ["script"] = Convert.ToBase64String(Script), + ["script"] = Convert.ToBase64String(Script.Span), ["checksum"] = CheckSum }; } diff --git a/src/neo/SmartContract/NotifyEventArgs.cs b/src/Neo/SmartContract/NotifyEventArgs.cs similarity index 86% rename from src/neo/SmartContract/NotifyEventArgs.cs rename to src/Neo/SmartContract/NotifyEventArgs.cs index 7e7ce20e0b..0a509101c1 100644 --- a/src/neo/SmartContract/NotifyEventArgs.cs +++ b/src/Neo/SmartContract/NotifyEventArgs.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// NotifyEventArgs.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. @@ -68,7 +69,7 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter) { ScriptHash.ToArray(), EventName, - State.DeepCopy() + State }; } } diff --git a/src/neo/SmartContract/StorageContext.cs b/src/Neo/SmartContract/StorageContext.cs similarity index 65% rename from src/neo/SmartContract/StorageContext.cs rename to src/Neo/SmartContract/StorageContext.cs index 0cb7516f64..5c88d1f06c 100644 --- a/src/neo/SmartContract/StorageContext.cs +++ b/src/Neo/SmartContract/StorageContext.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// StorageContext.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. diff --git a/src/neo/SmartContract/StorageItem.cs b/src/Neo/SmartContract/StorageItem.cs similarity index 72% rename from src/neo/SmartContract/StorageItem.cs rename to src/Neo/SmartContract/StorageItem.cs index 158daf7dec..41486997de 100644 --- a/src/neo/SmartContract/StorageItem.cs +++ b/src/Neo/SmartContract/StorageItem.cs @@ -1,17 +1,17 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// StorageItem.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.IO; using Neo.VM; using System; -using System.Collections.Generic; using System.IO; using System.Numerics; @@ -22,7 +22,7 @@ namespace Neo.SmartContract /// public class StorageItem : ISerializable { - private byte[] value; + private ReadOnlyMemory value; private object cache; public int Size => Value.GetVarSize(); @@ -30,16 +30,15 @@ public class StorageItem : ISerializable /// /// The byte array value of the . /// - public byte[] Value + public ReadOnlyMemory Value { get { - return value ??= cache switch + return !value.IsEmpty ? value : value = cache switch { BigInteger bi => bi.ToByteArrayStandard(), - IInteroperable interoperable => BinarySerializer.Serialize(interoperable.ToStackItem(null), 1024 * 1024), - IReadOnlyCollection list => list.ToByteArray(), - null => null, + IInteroperable interoperable => BinarySerializer.Serialize(interoperable.ToStackItem(null), ExecutionEngineLimits.Default), + null => ReadOnlyMemory.Empty, _ => throw new InvalidCastException() }; } @@ -97,15 +96,16 @@ public void Add(BigInteger integer) /// The created . public StorageItem Clone() { - return new StorageItem + return new() { - Value = Value + value = value, + cache = cache is IInteroperable interoperable ? interoperable.Clone() : cache }; } - public void Deserialize(BinaryReader reader) + public void Deserialize(ref MemoryReader reader) { - Value = reader.ReadBytes((int)(reader.BaseStream.Length)); + Value = reader.ReadToEnd(); } /// @@ -114,7 +114,18 @@ public void Deserialize(BinaryReader reader) /// The instance to be copied. public void FromReplica(StorageItem replica) { - Value = replica.Value; + value = replica.value; + if (replica.cache is IInteroperable interoperable) + { + if (cache?.GetType() == interoperable.GetType()) + ((IInteroperable)cache).FromReplica(interoperable); + else + cache = interoperable.Clone(); + } + else + { + cache = replica.cache; + } } /// @@ -134,21 +145,9 @@ public void FromReplica(StorageItem replica) return (T)cache; } - /// - /// Gets a list of from the storage. - /// - /// The type of the . - /// The list of the . - public List GetSerializableList() where T : ISerializable, new() - { - cache ??= new List(value.AsSerializableArray()); - value = null; - return (List)cache; - } - public void Serialize(BinaryWriter writer) { - writer.Write(Value); + writer.Write(Value.Span); } /// @@ -161,9 +160,19 @@ public void Set(BigInteger integer) value = null; } + /// + /// Sets the interoperable value of the storage. + /// + /// The value of the . + public void Set(IInteroperable interoperable) + { + cache = interoperable; + value = null; + } + public static implicit operator BigInteger(StorageItem item) { - item.cache ??= new BigInteger(item.value); + item.cache ??= new BigInteger(item.value.Span); return (BigInteger)item.cache; } } diff --git a/src/neo/SmartContract/StorageKey.cs b/src/Neo/SmartContract/StorageKey.cs similarity index 52% rename from src/neo/SmartContract/StorageKey.cs rename to src/Neo/SmartContract/StorageKey.cs index 3a10115e00..a0136e4456 100644 --- a/src/neo/SmartContract/StorageKey.cs +++ b/src/Neo/SmartContract/StorageKey.cs @@ -1,37 +1,45 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// StorageKey.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.Cryptography; -using Neo.IO; using System; using System.Buffers.Binary; -using System.IO; namespace Neo.SmartContract { /// /// Represents the keys in contract storage. /// - public class StorageKey : IEquatable, ISerializable + public sealed record StorageKey { /// /// The id of the contract. /// - public int Id; + public int Id { get; init; } /// /// The key of the storage entry. /// - public byte[] Key; + public ReadOnlyMemory Key { get; init; } - int ISerializable.Size => sizeof(int) + Key.Length; + private byte[] cache = null; + + public StorageKey() { } + + internal StorageKey(byte[] cache) + { + this.cache = cache; + Id = BinaryPrimitives.ReadInt32LittleEndian(cache); + Key = cache.AsMemory(sizeof(int)); + } /// /// Creates a search prefix for a contract. @@ -47,38 +55,29 @@ public static byte[] CreateSearchPrefix(int id, ReadOnlySpan prefix) return buffer; } - //If the base stream of the reader doesn't support seeking, a NotSupportedException is thrown. - //But StorageKey never works with NetworkStream, so it doesn't matter. - void ISerializable.Deserialize(BinaryReader reader) - { - Id = reader.ReadInt32(); - Key = reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position)); - } - public bool Equals(StorageKey other) { if (other is null) return false; if (ReferenceEquals(this, other)) return true; - return Id == other.Id && MemoryExtensions.SequenceEqual(Key, other.Key); - } - - public override bool Equals(object obj) - { - if (obj is not StorageKey other) return false; - return Equals(other); + return Id == other.Id && Key.Span.SequenceEqual(other.Key.Span); } public override int GetHashCode() { - return Id.GetHashCode() + (int)Key.Murmur32(0); + return Id + (int)Key.Span.Murmur32(0); } - void ISerializable.Serialize(BinaryWriter writer) + public byte[] ToArray() { - writer.Write(Id); - writer.Write(Key); + if (cache is null) + { + cache = new byte[sizeof(int) + Key.Length]; + BinaryPrimitives.WriteInt32LittleEndian(cache, Id); + Key.CopyTo(cache.AsMemory(sizeof(int))); + } + return cache; } } } diff --git a/src/neo/SmartContract/TriggerType.cs b/src/Neo/SmartContract/TriggerType.cs similarity index 82% rename from src/neo/SmartContract/TriggerType.cs rename to src/Neo/SmartContract/TriggerType.cs index 532441a9ba..1e93417968 100644 --- a/src/neo/SmartContract/TriggerType.cs +++ b/src/Neo/SmartContract/TriggerType.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// TriggerType.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. diff --git a/src/neo/SmartContract/ValidatorAttribute.cs b/src/Neo/SmartContract/ValidatorAttribute.cs similarity index 53% rename from src/neo/SmartContract/ValidatorAttribute.cs rename to src/Neo/SmartContract/ValidatorAttribute.cs index 079476abfe..23e785fc52 100644 --- a/src/neo/SmartContract/ValidatorAttribute.cs +++ b/src/Neo/SmartContract/ValidatorAttribute.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ValidatorAttribute.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. diff --git a/src/neo/TimeProvider.cs b/src/Neo/TimeProvider.cs similarity index 73% rename from src/neo/TimeProvider.cs rename to src/Neo/TimeProvider.cs index 6b3cc361bd..6a4542f9a2 100644 --- a/src/neo/TimeProvider.cs +++ b/src/Neo/TimeProvider.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// TimeProvider.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. diff --git a/src/neo/UInt160.cs b/src/Neo/UInt160.cs similarity index 94% rename from src/neo/UInt160.cs rename to src/Neo/UInt160.cs index 1dcd1150ba..8dfd6bf70c 100644 --- a/src/neo/UInt160.cs +++ b/src/Neo/UInt160.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// 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. @@ -68,7 +69,7 @@ public int CompareTo(UInt160 other) return value1.CompareTo(other.value1); } - public void Deserialize(BinaryReader reader) + public void Deserialize(ref MemoryReader reader) { value1 = reader.ReadUInt64(); value2 = reader.ReadUInt64(); diff --git a/src/neo/UInt256.cs b/src/Neo/UInt256.cs similarity index 94% rename from src/neo/UInt256.cs rename to src/Neo/UInt256.cs index 6a4647fcda..7c4d996339 100644 --- a/src/neo/UInt256.cs +++ b/src/Neo/UInt256.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// UInt256.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. @@ -71,7 +72,7 @@ public int CompareTo(UInt256 other) return value1.CompareTo(other.value1); } - public void Deserialize(BinaryReader reader) + public void Deserialize(ref MemoryReader reader) { value1 = reader.ReadUInt64(); value2 = reader.ReadUInt64(); diff --git a/src/neo/VM/Helper.cs b/src/Neo/VM/Helper.cs similarity index 82% rename from src/neo/VM/Helper.cs rename to src/Neo/VM/Helper.cs index 49a5a78f4b..4a82041ea5 100644 --- a/src/neo/VM/Helper.cs +++ b/src/Neo/VM/Helper.cs @@ -1,16 +1,17 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// 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 Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.VM.Types; using System; @@ -278,48 +279,105 @@ public static byte[] MakeScript(this UInt160 scriptHash, string method, params o /// Converts the to a JSON object. /// /// The to convert. + /// The maximum size in bytes of the result. /// The represented by a JSON object. - public static JObject ToJson(this StackItem item) + public static JObject ToJson(this StackItem item, int maxSize = int.MaxValue) { - return ToJson(item, null); + return ToJson(item, null, ref maxSize); } - private static JObject ToJson(StackItem item, HashSet context) + /// + /// Converts the to a JSON object. + /// + /// The to convert. + /// The maximum size in bytes of the result. + /// The represented by a JSON object. + public static JArray ToJson(this EvaluationStack stack, int maxSize = int.MaxValue) + { + if (maxSize <= 0) throw new ArgumentOutOfRangeException(nameof(maxSize)); + maxSize -= 2/*[]*/+ Math.Max(0, (stack.Count - 1))/*,*/; + JArray result = new(); + foreach (var item in stack) + result.Add(ToJson(item, null, ref maxSize)); + if (maxSize < 0) throw new InvalidOperationException("Max size reached."); + return result; + } + + private static JObject ToJson(StackItem item, HashSet context, ref int maxSize) { - JObject json = new(); - json["type"] = item.Type; + JObject json = new() + { + ["type"] = item.Type + }; + JToken value = null; + maxSize -= 11/*{"type":""}*/+ item.Type.ToString().Length; switch (item) { case Array array: - context ??= new HashSet(ReferenceEqualityComparer.Instance); - if (!context.Add(array)) throw new InvalidOperationException(); - json["value"] = new JArray(array.Select(p => ToJson(p, context))); - break; + { + context ??= new HashSet(ReferenceEqualityComparer.Instance); + if (!context.Add(array)) throw new InvalidOperationException(); + maxSize -= 2/*[]*/+ Math.Max(0, (array.Count - 1))/*,*/; + JArray a = new(); + foreach (StackItem stackItem in array) + a.Add(ToJson(stackItem, context, ref maxSize)); + value = a; + break; + } case Boolean boolean: - json["value"] = boolean.GetBoolean(); - break; + { + bool b = boolean.GetBoolean(); + maxSize -= b ? 4/*true*/: 5/*false*/; + value = b; + break; + } case Buffer _: case ByteString _: - json["value"] = Convert.ToBase64String(item.GetSpan()); - break; + { + string s = Convert.ToBase64String(item.GetSpan()); + maxSize -= 2/*""*/+ s.Length; + value = s; + break; + } case Integer integer: - json["value"] = integer.GetInteger().ToString(); - break; + { + string s = integer.GetInteger().ToString(); + maxSize -= 2/*""*/+ s.Length; + value = s; + break; + } case Map map: - context ??= new HashSet(ReferenceEqualityComparer.Instance); - if (!context.Add(map)) throw new InvalidOperationException(); - json["value"] = new JArray(map.Select(p => { - JObject item = new(); - item["key"] = ToJson(p.Key, context); - item["value"] = ToJson(p.Value, context); - return item; - })); - break; + context ??= new HashSet(ReferenceEqualityComparer.Instance); + if (!context.Add(map)) throw new InvalidOperationException(); + maxSize -= 2/*[]*/+ Math.Max(0, (map.Count - 1))/*,*/; + JArray a = new(); + foreach (var (k, v) in map) + { + maxSize -= 17/*{"key":,"value":}*/; + JObject i = new() + { + ["key"] = ToJson(k, context, ref maxSize), + ["value"] = ToJson(v, context, ref maxSize) + }; + a.Add(i); + } + value = a; + break; + } case Pointer pointer: - json["value"] = pointer.Position; - break; + { + maxSize -= pointer.Position.ToString().Length; + value = pointer.Position; + break; + } + } + if (value is not null) + { + maxSize -= 9/*,"value":*/; + json["value"] = value; } + if (maxSize < 0) throw new InvalidOperationException("Max size reached."); return json; } diff --git a/src/neo/Wallets/AssetDescriptor.cs b/src/Neo/Wallets/AssetDescriptor.cs similarity index 88% rename from src/neo/Wallets/AssetDescriptor.cs rename to src/Neo/Wallets/AssetDescriptor.cs index 44c08e9c93..4eb84e938d 100644 --- a/src/neo/Wallets/AssetDescriptor.cs +++ b/src/Neo/Wallets/AssetDescriptor.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// AssetDescriptor.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. diff --git a/src/Neo/Wallets/Helper.cs b/src/Neo/Wallets/Helper.cs new file mode 100644 index 0000000000..960baa27af --- /dev/null +++ b/src/Neo/Wallets/Helper.cs @@ -0,0 +1,166 @@ +// 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 Neo.Cryptography; +using Neo.IO; +using Neo.Network.P2P; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using System; +using static Neo.SmartContract.Helper; + +namespace Neo.Wallets +{ + /// + /// A helper class related to wallets. + /// + public static class Helper + { + /// + /// Signs an with the specified private key. + /// + /// The to sign. + /// The private key to be used. + /// The magic number of the NEO network. + /// The signature for the . + public static byte[] Sign(this IVerifiable verifiable, KeyPair key, uint network) + { + return Crypto.Sign(verifiable.GetSignData(network), key.PrivateKey, key.PublicKey.EncodePoint(false)[1..]); + } + + /// + /// Converts the specified script hash to an address. + /// + /// The script hash to convert. + /// The address version. + /// The converted address. + public static string ToAddress(this UInt160 scriptHash, byte version) + { + Span data = stackalloc byte[21]; + data[0] = version; + scriptHash.ToArray().CopyTo(data[1..]); + return Base58.Base58CheckEncode(data); + } + + /// + /// Converts the specified address to a script hash. + /// + /// The address to convert. + /// The address version. + /// The converted script hash. + public static UInt160 ToScriptHash(this string address, byte version) + { + byte[] data = address.Base58CheckDecode(); + if (data.Length != 21) + throw new FormatException(); + if (data[0] != version) + throw new FormatException(); + return new UInt160(data.AsSpan(1)); + } + + internal static byte[] XOR(byte[] x, byte[] y) + { + if (x.Length != y.Length) throw new ArgumentException(); + byte[] r = new byte[x.Length]; + for (int i = 0; i < r.Length; i++) + r[i] = (byte)(x[i] ^ y[i]); + return r; + } + + /// + /// Calculates the network fee for the specified transaction. + /// + /// The transaction to calculate. + /// The snapshot used to read data. + /// Thr protocol settings to use. + /// Function to retrive the script's account from a hash. + /// The maximum cost that can be spent when a contract is executed. + /// The network fee of the transaction. + public static long CalculateNetworkFee(this Transaction tx, DataCache snapshot, ProtocolSettings settings, Func accountScript, long maxExecutionCost = ApplicationEngine.TestModeGas) + { + UInt160[] hashes = tx.GetScriptHashesForVerifying(snapshot); + + // base size for transaction: includes const_header + signers + attributes + script + hashes + int size = Transaction.HeaderSize + tx.Signers.GetVarSize() + tx.Attributes.GetVarSize() + tx.Script.GetVarSize() + IO.Helper.GetVarSize(hashes.Length), index = -1; + uint exec_fee_factor = NativeContract.Policy.GetExecFeeFactor(snapshot); + long networkFee = 0; + foreach (UInt160 hash in hashes) + { + index++; + byte[] witnessScript = accountScript(hash); + byte[] invocationScript = null; + + if (tx.Witnesses != null && witnessScript is null) + { + // Try to find the script in the witnesses + Witness witness = tx.Witnesses[index]; + witnessScript = witness?.VerificationScript.ToArray(); + + if (witnessScript is null || witnessScript.Length == 0) + { + // Then it's a contract-based witness, so try to get the corresponding invocation script for it + invocationScript = witness?.InvocationScript.ToArray(); + } + } + + if (witnessScript is null || witnessScript.Length == 0) + { + var contract = NativeContract.ContractManagement.GetContract(snapshot, hash); + if (contract is null) + throw new ArgumentException($"The smart contract or address {hash} is not found"); + var md = contract.Manifest.Abi.GetMethod("verify", -1); + if (md is null) + throw new ArgumentException($"The smart contract {contract.Hash} haven't got verify method"); + if (md.ReturnType != ContractParameterType.Boolean) + throw new ArgumentException("The verify method doesn't return boolean value."); + if (md.Parameters.Length > 0 && invocationScript is null) + throw new ArgumentException("The verify method requires parameters that need to be passed via the witness' invocation script."); + + // Empty verification and non-empty invocation scripts + var invSize = invocationScript?.GetVarSize() ?? Array.Empty().GetVarSize(); + size += Array.Empty().GetVarSize() + invSize; + + // Check verify cost + using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot.CreateSnapshot(), settings: settings, gas: maxExecutionCost); + engine.LoadContract(contract, md, CallFlags.ReadOnly); + if (invocationScript != null) engine.LoadScript(invocationScript, configureState: p => p.CallFlags = CallFlags.None); + if (engine.Execute() == VMState.FAULT) throw new ArgumentException($"Smart contract {contract.Hash} verification fault."); + if (!engine.ResultStack.Pop().GetBoolean()) throw new ArgumentException($"Smart contract {contract.Hash} returns false."); + + maxExecutionCost -= engine.GasConsumed; + if (maxExecutionCost <= 0) throw new InvalidOperationException("Insufficient GAS."); + networkFee += engine.GasConsumed; + } + else if (IsSignatureContract(witnessScript)) + { + size += 67 + witnessScript.GetVarSize(); + networkFee += exec_fee_factor * SignatureContractCost(); + } + else if (IsMultiSigContract(witnessScript, out int m, out int n)) + { + int size_inv = 66 * m; + size += IO.Helper.GetVarSize(size_inv) + size_inv + witnessScript.GetVarSize(); + networkFee += exec_fee_factor * MultiSignatureContractCost(m, n); + } + // We can support more contract types in the future. + } + networkFee += size * NativeContract.Policy.GetFeePerByte(snapshot); + foreach (TransactionAttribute attr in tx.Attributes) + { + networkFee += attr.CalculateNetworkFee(snapshot, tx); + } + return networkFee; + } + } +} diff --git a/src/Neo/Wallets/IWalletFactory.cs b/src/Neo/Wallets/IWalletFactory.cs new file mode 100644 index 0000000000..5c69b56262 --- /dev/null +++ b/src/Neo/Wallets/IWalletFactory.cs @@ -0,0 +1,22 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// IWalletFactory.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.Wallets +{ + public interface IWalletFactory + { + public bool Handle(string path); + + public Wallet CreateWallet(string name, string path, string password, ProtocolSettings settings); + + public Wallet OpenWallet(string path, string password, ProtocolSettings settings); + } +} diff --git a/src/neo/Plugins/IWalletProvider.cs b/src/Neo/Wallets/IWalletProvider.cs similarity index 67% rename from src/neo/Plugins/IWalletProvider.cs rename to src/Neo/Wallets/IWalletProvider.cs index 02e0051e62..a4edc17692 100644 --- a/src/neo/Plugins/IWalletProvider.cs +++ b/src/Neo/Wallets/IWalletProvider.cs @@ -1,17 +1,17 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// IWalletProvider.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.Wallets; using System; -namespace Neo.Plugins +namespace Neo.Wallets { /// /// A provider for obtaining wallet instance. diff --git a/src/neo/Wallets/KeyPair.cs b/src/Neo/Wallets/KeyPair.cs similarity index 76% rename from src/neo/Wallets/KeyPair.cs rename to src/Neo/Wallets/KeyPair.cs index eca8f67191..d0842242d8 100644 --- a/src/neo/Wallets/KeyPair.cs +++ b/src/Neo/Wallets/KeyPair.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// KeyPair.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. @@ -95,11 +96,33 @@ public string Export() /// The P field of the to be used. /// The private key in NEP-2 format. public string Export(string passphrase, byte version, int N = 16384, int r = 8, int p = 8) + { + byte[] passphrasedata = Encoding.UTF8.GetBytes(passphrase); + try + { + return Export(passphrasedata, version, N, r, p); + } + finally + { + passphrasedata.AsSpan().Clear(); + } + } + + /// + /// Exports the private key in NEP-2 format. + /// + /// The passphrase of the private key. + /// The address version. + /// The N field of the to be used. + /// The R field of the to be used. + /// The P field of the to be used. + /// The private key in NEP-2 format. + public string Export(byte[] passphrase, byte version, int N = 16384, int r = 8, int p = 8) { UInt160 script_hash = Contract.CreateSignatureRedeemScript(PublicKey).ToScriptHash(); string address = script_hash.ToAddress(version); byte[] addresshash = Encoding.ASCII.GetBytes(address).Sha256().Sha256()[..4]; - byte[] derivedkey = SCrypt.Generate(Encoding.UTF8.GetBytes(passphrase), addresshash, N, r, p, 64); + byte[] derivedkey = SCrypt.Generate(passphrase, addresshash, N, r, p, 64); byte[] derivedhalf1 = derivedkey[..32]; byte[] derivedhalf2 = derivedkey[32..]; byte[] encryptedkey = Encrypt(XOR(PrivateKey, derivedhalf1), derivedhalf2); diff --git a/src/neo/Wallets/NEP6/NEP6Account.cs b/src/Neo/Wallets/NEP6/NEP6Account.cs similarity index 91% rename from src/neo/Wallets/NEP6/NEP6Account.cs rename to src/Neo/Wallets/NEP6/NEP6Account.cs index 754d502764..7998b9ea29 100644 --- a/src/neo/Wallets/NEP6/NEP6Account.cs +++ b/src/Neo/Wallets/NEP6/NEP6Account.cs @@ -1,14 +1,15 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// NEP6Account.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.IO.Json; +using Neo.Json; using System; using System.Threading; @@ -20,7 +21,7 @@ sealed class NEP6Account : WalletAccount private string nep2key; private string nep2KeyNew = null; private KeyPair key; - public JObject Extra; + public JToken Extra; public bool Decrypted => nep2key == null || key != null; public override bool HasKey => nep2key != null; @@ -45,7 +46,7 @@ public static NEP6Account FromJson(JObject json, NEP6Wallet wallet) Label = json["label"]?.GetString(), IsDefault = json["isDefault"].GetBoolean(), Lock = json["lock"].GetBoolean(), - Contract = NEP6Contract.FromJson(json["contract"]), + Contract = NEP6Contract.FromJson((JObject)json["contract"]), Extra = json["extra"] }; } diff --git a/src/neo/Wallets/NEP6/NEP6Contract.cs b/src/Neo/Wallets/NEP6/NEP6Contract.cs similarity index 77% rename from src/neo/Wallets/NEP6/NEP6Contract.cs rename to src/Neo/Wallets/NEP6/NEP6Contract.cs index 8ef4470eaa..cecc80be7e 100644 --- a/src/neo/Wallets/NEP6/NEP6Contract.cs +++ b/src/Neo/Wallets/NEP6/NEP6Contract.cs @@ -1,14 +1,15 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// NEP6Contract.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.IO.Json; +using Neo.Json; using Neo.SmartContract; using System; using System.Linq; @@ -26,7 +27,7 @@ public static NEP6Contract FromJson(JObject json) return new NEP6Contract { Script = Convert.FromBase64String(json["script"].AsString()), - ParameterList = ((JArray)json["parameters"]).Select(p => p["type"].TryGetEnum()).ToArray(), + ParameterList = ((JArray)json["parameters"]).Select(p => p["type"].GetEnum()).ToArray(), ParameterNames = ((JArray)json["parameters"]).Select(p => p["name"].AsString()).ToArray(), Deployed = json["deployed"].AsBoolean() }; diff --git a/src/neo/Wallets/NEP6/NEP6Wallet.cs b/src/Neo/Wallets/NEP6/NEP6Wallet.cs similarity index 78% rename from src/neo/Wallets/NEP6/NEP6Wallet.cs rename to src/Neo/Wallets/NEP6/NEP6Wallet.cs index c58826116e..b600fe6dcd 100644 --- a/src/neo/Wallets/NEP6/NEP6Wallet.cs +++ b/src/Neo/Wallets/NEP6/NEP6Wallet.cs @@ -1,14 +1,15 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// NEP6Wallet.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.IO.Json; +using Neo.Json; using Neo.SmartContract; using System; using System.Collections.Generic; @@ -17,7 +18,6 @@ using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using UserWallet = Neo.Wallets.SQLite.UserWallet; namespace Neo.Wallets.NEP6 { @@ -31,7 +31,7 @@ public class NEP6Wallet : Wallet private string name; private Version version; private readonly Dictionary accounts; - private readonly JObject extra; + private readonly JToken extra; /// /// The parameters of the SCrypt algorithm used for encrypting and decrypting the private keys in the wallet. @@ -49,13 +49,15 @@ public class NEP6Wallet : Wallet /// Loads or creates a wallet at the specified path. /// /// The path of the wallet file. + /// The password of the wallet. /// The to be used by the wallet. /// The name of the wallet. If the wallet is loaded from an existing file, this parameter is ignored. - public NEP6Wallet(string path, ProtocolSettings settings, string name = null) : base(path, settings) + public NEP6Wallet(string path, string password, ProtocolSettings settings, string name = null) : base(path, settings) { + this.password = password; if (File.Exists(path)) { - JObject wallet = JObject.Parse(File.ReadAllBytes(path)); + JObject wallet = (JObject)JToken.Parse(File.ReadAllBytes(path)); LoadFromJson(wallet, out Scrypt, out accounts, out extra); } else @@ -64,7 +66,7 @@ public NEP6Wallet(string path, ProtocolSettings settings, string name = null) : this.version = Version.Parse("1.0"); this.Scrypt = ScryptParameters.Default; this.accounts = new Dictionary(); - this.extra = JObject.Null; + this.extra = JToken.Null; } } @@ -72,20 +74,24 @@ public NEP6Wallet(string path, ProtocolSettings settings, string name = null) : /// Loads the wallet with the specified JSON string. /// /// The path of the wallet. + /// The password of the wallet. /// The to be used by the wallet. /// The JSON object representing the wallet. - public NEP6Wallet(string path, ProtocolSettings settings, JObject json) : base(path, settings) + public NEP6Wallet(string path, string password, ProtocolSettings settings, JObject json) : base(path, settings) { + this.password = password; LoadFromJson(json, out Scrypt, out accounts, out extra); } - private void LoadFromJson(JObject wallet, out ScryptParameters scrypt, out Dictionary accounts, out JObject extra) + private void LoadFromJson(JObject wallet, out ScryptParameters scrypt, out Dictionary accounts, out JToken extra) { this.version = Version.Parse(wallet["version"].AsString()); this.name = wallet["name"]?.AsString(); - scrypt = ScryptParameters.FromJson(wallet["scrypt"]); - accounts = ((JArray)wallet["accounts"]).Select(p => NEP6Account.FromJson(p, this)).ToDictionary(p => p.ScriptHash); + scrypt = ScryptParameters.FromJson((JObject)wallet["scrypt"]); + accounts = ((JArray)wallet["accounts"]).Select(p => NEP6Account.FromJson((JObject)p, this)).ToDictionary(p => p.ScriptHash); extra = wallet["extra"]; + if (!VerifyPasswordInternal(password)) + throw new InvalidOperationException("Wrong password."); } private void AddAccount(NEP6Account account) @@ -179,7 +185,7 @@ public override WalletAccount CreateAccount(UInt160 scriptHash) /// /// The NEP-2 string to decrypt. /// The decrypted private key. - public KeyPair DecryptKey(string nep2key) + internal KeyPair DecryptKey(string nep2key) { return new KeyPair(GetPrivateKeyFromNEP2(nep2key, password, ProtocolSettings.AddressVersion, Scrypt.N, Scrypt.R, Scrypt.P)); } @@ -215,20 +221,6 @@ public override IEnumerable GetAccounts() } } - public WalletAccount GetDefaultAccount() - { - NEP6Account first = null; - lock (accounts) - { - foreach (NEP6Account account in accounts.Values) - { - if (account.IsDefault) return account; - if (first == null) first = account; - } - } - return first; - } - public override WalletAccount Import(X509Certificate2 cert) { KeyPair key; @@ -289,61 +281,32 @@ public override WalletAccount Import(string nep2, string passphrase, int N = 163 return account; } - internal void Lock() - { - password = null; - } - /// - /// Migrates the accounts from to a new . + /// Exports the wallet as JSON /// - /// The path of the new wallet file. - /// The path of the db3 wallet file. - /// The password of the wallets. - /// The to be used by the wallet. - /// The created new wallet. - public static NEP6Wallet Migrate(string path, string db3path, string password, ProtocolSettings settings) + public JObject ToJson() { - UserWallet wallet_old = UserWallet.Open(db3path, password, settings); - NEP6Wallet wallet_new = new(path, settings, wallet_old.Name); - using (wallet_new.Unlock(password)) + return new() { - foreach (WalletAccount account in wallet_old.GetAccounts()) - { - wallet_new.CreateAccount(account.Contract, account.GetKey()); - } - } - return wallet_new; + ["name"] = name, + ["version"] = version.ToString(), + ["scrypt"] = Scrypt.ToJson(), + ["accounts"] = accounts.Values.Select(p => p.ToJson()).ToArray(), + ["extra"] = extra + }; } - /// - /// Saves the wallet to the file. - /// - public void Save() + public override void Save() { - JObject wallet = new(); - wallet["name"] = name; - wallet["version"] = version.ToString(); - wallet["scrypt"] = Scrypt.ToJson(); - wallet["accounts"] = new JArray(accounts.Values.Select(p => p.ToJson())); - wallet["extra"] = extra; - File.WriteAllText(Path, wallet.ToString()); + File.WriteAllText(Path, ToJson().ToString()); } - /// - /// Unlocks the wallet with the specified password. - /// - /// The password of the wallet. - /// The object that can be disposed to lock the wallet again. - public IDisposable Unlock(string password) + public override bool VerifyPassword(string password) { - if (!VerifyPassword(password)) - throw new CryptographicException(); - this.password = password; - return new WalletLocker(this); + return this.password == password; } - public override bool VerifyPassword(string password) + private bool VerifyPasswordInternal(string password) { lock (accounts) { @@ -390,8 +353,7 @@ public override bool ChangePassword(string oldPassword, string newPassword) { foreach (NEP6Account account in accounts.Values) account.ChangePasswordCommit(); - if (password != null) - password = newPassword; + password = newPassword; } else { diff --git a/src/Neo/Wallets/NEP6/NEP6WalletFactory.cs b/src/Neo/Wallets/NEP6/NEP6WalletFactory.cs new file mode 100644 index 0000000000..759ab98de6 --- /dev/null +++ b/src/Neo/Wallets/NEP6/NEP6WalletFactory.cs @@ -0,0 +1,40 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// NEP6WalletFactory.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; +using System.IO; + +namespace Neo.Wallets.NEP6 +{ + class NEP6WalletFactory : IWalletFactory + { + public static readonly NEP6WalletFactory Instance = new(); + + public bool Handle(string path) + { + return Path.GetExtension(path).ToLowerInvariant() == ".json"; + } + + public Wallet CreateWallet(string name, string path, string password, ProtocolSettings settings) + { + if (File.Exists(path)) + throw new InvalidOperationException("The wallet file already exists."); + NEP6Wallet wallet = new NEP6Wallet(path, password, settings, name); + wallet.Save(); + return wallet; + } + + public Wallet OpenWallet(string path, string password, ProtocolSettings settings) + { + return new NEP6Wallet(path, password, settings); + } + } +} diff --git a/src/neo/Wallets/NEP6/ScryptParameters.cs b/src/Neo/Wallets/NEP6/ScryptParameters.cs similarity index 87% rename from src/neo/Wallets/NEP6/ScryptParameters.cs rename to src/Neo/Wallets/NEP6/ScryptParameters.cs index 15b6f08fc2..ab720b3a08 100644 --- a/src/neo/Wallets/NEP6/ScryptParameters.cs +++ b/src/Neo/Wallets/NEP6/ScryptParameters.cs @@ -1,14 +1,15 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// ScryptParameters.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.IO.Json; +using Neo.Json; namespace Neo.Wallets.NEP6 { diff --git a/src/neo/Wallets/TransferOutput.cs b/src/Neo/Wallets/TransferOutput.cs similarity index 72% rename from src/neo/Wallets/TransferOutput.cs rename to src/Neo/Wallets/TransferOutput.cs index 09abd33ff0..7f7d7777a1 100644 --- a/src/neo/Wallets/TransferOutput.cs +++ b/src/Neo/Wallets/TransferOutput.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// TransferOutput.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. diff --git a/src/neo/Wallets/Wallet.cs b/src/Neo/Wallets/Wallet.cs similarity index 80% rename from src/neo/Wallets/Wallet.cs rename to src/Neo/Wallets/Wallet.cs index 0610e6d5d3..761b0b1999 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/Neo/Wallets/Wallet.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// Wallet.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. @@ -35,6 +36,8 @@ namespace Neo.Wallets /// public abstract class Wallet { + private static readonly List factories = new() { NEP6WalletFactory.Instance }; + /// /// The to be used by the wallet. /// @@ -124,8 +127,8 @@ public abstract class Wallet /// The to be used by the wallet. protected Wallet(string path, ProtocolSettings settings) { - this.ProtocolSettings = settings; - this.Path = path; + ProtocolSettings = settings; + Path = path; } /// @@ -134,24 +137,26 @@ protected Wallet(string path, ProtocolSettings settings) /// The created account. public WalletAccount CreateAccount() { - byte[] privateKey = new byte[32]; - generate: - try + var privateKey = new byte[32]; + using var rng = RandomNumberGenerator.Create(); + + do { - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + try { rng.GetBytes(privateKey); + return CreateAccount(privateKey); + } + catch (ArgumentException) + { + // Try again + } + finally + { + Array.Clear(privateKey, 0, privateKey.Length); } - return CreateAccount(privateKey); - } - catch (ArgumentException) - { - goto generate; - } - finally - { - Array.Clear(privateKey, 0, privateKey.Length); } + while (true); } /// @@ -169,7 +174,7 @@ public WalletAccount CreateAccount(Contract contract, byte[] privateKey) private static List<(UInt160 Account, BigInteger Value)> FindPayingAccounts(List<(UInt160 Account, BigInteger Value)> orderedAccounts, BigInteger amount) { var result = new List<(UInt160 Account, BigInteger Value)>(); - BigInteger sum_balance = orderedAccounts.Select(p => p.Value).Sum(); + var sum_balance = orderedAccounts.Select(p => p.Value).Sum(); if (sum_balance == amount) { result.AddRange(orderedAccounts); @@ -237,6 +242,21 @@ public WalletAccount GetAccount(ECPoint pubkey) return GetAccount(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash()); } + /// + /// Gets the default account of the wallet. + /// + /// The default account of the wallet. + public virtual WalletAccount GetDefaultAccount() + { + WalletAccount first = null; + foreach (WalletAccount account in GetAccounts()) + { + if (account.IsDefault) return account; + if (first == null) first = account; + } + return first; + } + /// /// Gets the available balance for the specified asset in the wallet. /// @@ -299,6 +319,29 @@ private static byte[] Decrypt(byte[] data, byte[] key) /// The P field of the to be used. /// The decoded private key. public static byte[] GetPrivateKeyFromNEP2(string nep2, string passphrase, byte version, int N = 16384, int r = 8, int p = 8) + { + byte[] passphrasedata = Encoding.UTF8.GetBytes(passphrase); + try + { + return GetPrivateKeyFromNEP2(nep2, passphrasedata, version, N, r, p); + } + finally + { + passphrasedata.AsSpan().Clear(); + } + } + + /// + /// Decodes a private key from the specified NEP-2 string. + /// + /// The NEP-2 string to be decoded. + /// The passphrase of the private key. + /// The address version of NEO system. + /// The N field of the to be used. + /// The R field of the to be used. + /// The P field of the to be used. + /// The decoded private key. + public static byte[] GetPrivateKeyFromNEP2(string nep2, byte[] passphrase, byte version, int N = 16384, int r = 8, int p = 8) { if (nep2 == null) throw new ArgumentNullException(nameof(nep2)); if (passphrase == null) throw new ArgumentNullException(nameof(passphrase)); @@ -307,9 +350,7 @@ public static byte[] GetPrivateKeyFromNEP2(string nep2, string passphrase, byte throw new FormatException(); byte[] addresshash = new byte[4]; Buffer.BlockCopy(data, 3, addresshash, 0, 4); - byte[] datapassphrase = Encoding.UTF8.GetBytes(passphrase); - byte[] derivedkey = SCrypt.Generate(datapassphrase, addresshash, N, r, p, 64); - Array.Clear(datapassphrase, 0, datapassphrase.Length); + byte[] derivedkey = SCrypt.Generate(passphrase, addresshash, N, r, p, 64); byte[] derivedhalf1 = derivedkey[..32]; byte[] derivedhalf2 = derivedkey[32..]; Array.Clear(derivedkey, 0, derivedkey.Length); @@ -417,9 +458,10 @@ public virtual WalletAccount Import(string nep2, string passphrase, int N = 1638 /// The snapshot used to read data. /// The array of that contain the asset, amount, and targets of the transfer. /// The account to transfer from. - /// The cosigners to be added to the transction. - /// The created transction. - public Transaction MakeTransaction(DataCache snapshot, TransferOutput[] outputs, UInt160 from = null, Signer[] cosigners = null) + /// The cosigners to be added to the transaction. + /// The block environment to execute the transaction. If null, will be used. + /// The created transaction. + public Transaction MakeTransaction(DataCache snapshot, TransferOutput[] outputs, UInt160 from = null, Signer[] cosigners = null, Block persistingBlock = null) { UInt160[] accounts; if (from is null) @@ -442,7 +484,7 @@ public Transaction MakeTransaction(DataCache snapshot, TransferOutput[] outputs, { using ScriptBuilder sb2 = new(); sb2.EmitDynamicCall(assetId, "balanceOf", CallFlags.ReadOnly, account); - using ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot, settings: ProtocolSettings); + using ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot, settings: ProtocolSettings, persistingBlock: persistingBlock); if (engine.State != VMState.HALT) throw new InvalidOperationException($"Execution for {assetId}.balanceOf('{account}' fault"); BigInteger value = engine.ResultStack.Pop().GetInteger(); @@ -482,7 +524,7 @@ public Transaction MakeTransaction(DataCache snapshot, TransferOutput[] outputs, if (balances_gas is null) balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); - return MakeTransaction(snapshot, script, cosignerList.Values.ToArray(), Array.Empty(), balances_gas); + return MakeTransaction(snapshot, script, cosignerList.Values.ToArray(), Array.Empty(), balances_gas, persistingBlock: persistingBlock); } /// @@ -491,11 +533,12 @@ public Transaction MakeTransaction(DataCache snapshot, TransferOutput[] outputs, /// The snapshot used to read data. /// The script to be loaded in the transaction. /// The sender of the transaction. - /// The cosigners to be added to the transction. - /// The attributes to be added to the transction. + /// The cosigners to be added to the transaction. + /// The attributes to be added to the transaction. /// The maximum gas that can be spent to execute the script. - /// The created transction. - public Transaction MakeTransaction(DataCache snapshot, byte[] script, UInt160 sender = null, Signer[] cosigners = null, TransactionAttribute[] attributes = null, long maxGas = ApplicationEngine.TestModeGas) + /// The block environment to execute the transaction. If null, will be used. + /// The created transaction. + public Transaction MakeTransaction(DataCache snapshot, ReadOnlyMemory script, UInt160 sender = null, Signer[] cosigners = null, TransactionAttribute[] attributes = null, long maxGas = ApplicationEngine.TestModeGas, Block persistingBlock = null) { UInt160[] accounts; if (sender is null) @@ -507,10 +550,10 @@ public Transaction MakeTransaction(DataCache snapshot, byte[] script, UInt160 se accounts = new[] { sender }; } var balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); - return MakeTransaction(snapshot, script, cosigners ?? Array.Empty(), attributes ?? Array.Empty(), balances_gas, maxGas); + return MakeTransaction(snapshot, script, cosigners ?? Array.Empty(), attributes ?? Array.Empty(), balances_gas, maxGas, persistingBlock: persistingBlock); } - private Transaction MakeTransaction(DataCache snapshot, byte[] script, Signer[] cosigners, TransactionAttribute[] attributes, List<(UInt160 Account, BigInteger Value)> balances_gas, long maxGas = ApplicationEngine.TestModeGas) + private Transaction MakeTransaction(DataCache snapshot, ReadOnlyMemory script, Signer[] cosigners, TransactionAttribute[] attributes, List<(UInt160 Account, BigInteger Value)> balances_gas, long maxGas = ApplicationEngine.TestModeGas, Block persistingBlock = null) { Random rand = new(); foreach (var (account, value) in balances_gas) @@ -525,105 +568,22 @@ private Transaction MakeTransaction(DataCache snapshot, byte[] script, Signer[] Attributes = attributes, }; - // will try to execute 'transfer' script to check if it works - using (ApplicationEngine engine = ApplicationEngine.Run(script, snapshot.CreateSnapshot(), tx, settings: ProtocolSettings, gas: maxGas)) + // will try to execute 'transfer' script to check if it works + using (ApplicationEngine engine = ApplicationEngine.Run(script, snapshot.CreateSnapshot(), tx, settings: ProtocolSettings, gas: maxGas, persistingBlock: persistingBlock)) { if (engine.State == VMState.FAULT) { - throw new InvalidOperationException($"Failed execution for '{Convert.ToBase64String(script)}'", engine.FaultException); + throw new InvalidOperationException($"Failed execution for '{Convert.ToBase64String(script.Span)}'", engine.FaultException); } tx.SystemFee = engine.GasConsumed; } - tx.NetworkFee = CalculateNetworkFee(snapshot, tx); + tx.NetworkFee = tx.CalculateNetworkFee(snapshot, ProtocolSettings, (a) => GetAccount(a)?.Contract?.Script, maxGas); if (value >= tx.SystemFee + tx.NetworkFee) return tx; } throw new InvalidOperationException("Insufficient GAS"); } - /// - /// Calculates the network fee for the specified transaction. - /// - /// The snapshot used to read data. - /// The transaction to calculate. - /// The network fee of the transaction. - public long CalculateNetworkFee(DataCache snapshot, Transaction tx) - { - UInt160[] hashes = tx.GetScriptHashesForVerifying(snapshot); - - // base size for transaction: includes const_header + signers + attributes + script + hashes - int size = Transaction.HeaderSize + tx.Signers.GetVarSize() + tx.Attributes.GetVarSize() + tx.Script.GetVarSize() + IO.Helper.GetVarSize(hashes.Length); - uint exec_fee_factor = NativeContract.Policy.GetExecFeeFactor(snapshot); - long networkFee = 0; - int index = -1; - foreach (UInt160 hash in hashes) - { - index++; - byte[] witness_script = GetAccount(hash)?.Contract?.Script; - byte[] invocationScript = null; - - if (tx.Witnesses != null) - { - if (witness_script is null) - { - // Try to find the script in the witnesses - Witness witness = tx.Witnesses[index]; - witness_script = witness?.VerificationScript; - - if (witness_script is null || witness_script.Length == 0) - { - // Then it's a contract-based witness, so try to get the corresponding invocation script for it - invocationScript = witness?.InvocationScript; - } - } - } - - if (witness_script is null || witness_script.Length == 0) - { - var contract = NativeContract.ContractManagement.GetContract(snapshot, hash); - if (contract is null) - throw new ArgumentException($"The smart contract or address {hash} is not found"); - var md = contract.Manifest.Abi.GetMethod("verify", -1); - if (md is null) - throw new ArgumentException($"The smart contract {contract.Hash} haven't got verify method"); - if (md.ReturnType != ContractParameterType.Boolean) - throw new ArgumentException("The verify method doesn't return boolean value."); - if (md.Parameters.Length > 0 && invocationScript is null) - throw new ArgumentException("The verify method requires parameters that need to be passed via the witness' invocation script."); - - // Empty verification and non-empty invocation scripts - var invSize = invocationScript != null ? invocationScript.GetVarSize() : Array.Empty().GetVarSize(); - size += Array.Empty().GetVarSize() + invSize; - - // Check verify cost - using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot.CreateSnapshot(), settings: ProtocolSettings); - engine.LoadContract(contract, md, CallFlags.ReadOnly); - if (invocationScript != null) engine.LoadScript(invocationScript, configureState: p => p.CallFlags = CallFlags.None); - if (engine.Execute() == VMState.FAULT) throw new ArgumentException($"Smart contract {contract.Hash} verification fault."); - if (!engine.ResultStack.Pop().GetBoolean()) throw new ArgumentException($"Smart contract {contract.Hash} returns false."); - - networkFee += engine.GasConsumed; - } - else if (witness_script.IsSignatureContract()) - { - size += 67 + witness_script.GetVarSize(); - networkFee += exec_fee_factor * SignatureContractCost(); - } - else if (witness_script.IsMultiSigContract(out int m, out int n)) - { - int size_inv = 66 * m; - size += IO.Helper.GetVarSize(size_inv) + size_inv + witness_script.GetVarSize(); - networkFee += exec_fee_factor * MultiSignatureContractCost(m, n); - } - else - { - //We can support more contract types in the future. - } - } - networkFee += size * NativeContract.Policy.GetFeePerByte(snapshot); - return networkFee; - } - /// /// Signs the in the specified with the wallet. /// @@ -644,7 +604,7 @@ public bool Sign(ContractParametersContext context) Contract multiSigContract = account.Contract; if (multiSigContract != null && - multiSigContract.Script.IsMultiSigContract(out int m, out ECPoint[] points)) + IsMultiSigContract(multiSigContract.Script, out int m, out ECPoint[] points)) { foreach (var point in points) { @@ -694,5 +654,57 @@ public bool Sign(ContractParametersContext context) /// The password to be checked. /// if the password is correct; otherwise, . public abstract bool VerifyPassword(string password); + + /// + /// Saves the wallet file to the disk. It uses the value of property. + /// + public abstract void Save(); + + public static Wallet Create(string name, string path, string password, ProtocolSettings settings) + { + return GetFactory(path)?.CreateWallet(name, path, password, settings); + } + + public static Wallet Open(string path, string password, ProtocolSettings settings) + { + return GetFactory(path)?.OpenWallet(path, password, settings); + } + + /// + /// Migrates the accounts from old wallet to a new . + /// + /// The password of the wallets. + /// The path of the new wallet file. + /// The path of the old wallet file. + /// The to be used by the wallet. + /// The created new wallet. + public static Wallet Migrate(string path, string oldPath, string password, ProtocolSettings settings) + { + IWalletFactory factoryOld = GetFactory(oldPath); + if (factoryOld is null) + throw new InvalidOperationException("The old wallet file format is not supported."); + IWalletFactory factoryNew = GetFactory(path); + if (factoryNew is null) + throw new InvalidOperationException("The new wallet file format is not supported."); + + Wallet oldWallet = factoryOld.OpenWallet(oldPath, password, settings); + Wallet newWallet = factoryNew.CreateWallet(oldWallet.Name, path, password, settings); + + foreach (WalletAccount account in oldWallet.GetAccounts()) + { + newWallet.CreateAccount(account.Contract, account.GetKey()); + } + return newWallet; + } + + private static IWalletFactory GetFactory(string path) + { + return factories.FirstOrDefault(p => p.Handle(path)); + } + + public static void RegisterFactory(IWalletFactory factory) + { + factories.Add(factory); + } } } diff --git a/src/neo/Wallets/WalletAccount.cs b/src/Neo/Wallets/WalletAccount.cs similarity index 88% rename from src/neo/Wallets/WalletAccount.cs rename to src/Neo/Wallets/WalletAccount.cs index 5f231c6d42..148f272bc1 100644 --- a/src/neo/Wallets/WalletAccount.cs +++ b/src/Neo/Wallets/WalletAccount.cs @@ -1,10 +1,11 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php +// Copyright (C) 2015-2024 The Neo Project. +// +// WalletAccount.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. diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000000..c3738b18b9 --- /dev/null +++ b/src/README.md @@ -0,0 +1,25 @@ +## Overview +This repository contain main classes of the [Neo](https://www.neo.org) blockchain. +Visit the [documentation](https://docs.neo.org/docs/en-us/index.html) to get started. + +## Related projects +Code references are provided for all platform building blocks. That includes the base library, the VM, a command line application and the compiler. + +- [neo:](https://github.com/neo-project/neo/) Neo core library, contains base classes, including ledger, p2p and IO modules. +- [neo-modules:](https://github.com/neo-project/neo-modules/) Neo modules include additional tools and plugins to be used with Neo. +- [neo-devpack-dotnet:](https://github.com/neo-project/neo-devpack-dotnet/) These are the official tools used to convert a C# smart-contract into a *neo executable file*. + +## Opening a new issue +Please feel free to create new issues to suggest features or ask questions. + +- [Feature request](https://github.com/neo-project/neo/issues/new?assignees=&labels=discussion&template=feature-or-enhancement-request.md&title=) +- [Bug report](https://github.com/neo-project/neo/issues/new?assignees=&labels=&template=bug_report.md&title=) +- [Questions](https://github.com/neo-project/neo/issues/new?assignees=&labels=question&template=questions.md&title=) + +If you found a security issue, please refer to our [security policy](https://github.com/neo-project/neo/security/policy). + +## Bounty program +You can be rewarded by finding security issues. Please refer to our [bounty program page](https://neo.org/bounty) for more information. + +## License +The NEO project is licensed under the [MIT license](http://www.opensource.org/licenses/mit-license.php). diff --git a/src/neo.png b/src/neo.png new file mode 100644 index 0000000000..1a71de07eb Binary files /dev/null and b/src/neo.png differ diff --git a/src/neo/Cryptography/Murmur128.cs b/src/neo/Cryptography/Murmur128.cs deleted file mode 100644 index eccfb9e8b5..0000000000 --- a/src/neo/Cryptography/Murmur128.cs +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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; -using System.Buffers.Binary; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Security.Cryptography; - -namespace Neo.Cryptography -{ - /// - /// Computes the 128 bits murmur hash for the input data. - /// - public sealed class Murmur128 : HashAlgorithm - { - private const ulong c1 = 0x87c37b91114253d5; - private const ulong c2 = 0x4cf5ad432745937f; - private const int r1 = 31; - private const int r2 = 33; - private const uint m = 5; - private const uint n1 = 0x52dce729; - private const uint n2 = 0x38495ab5; - - private readonly uint seed; - private int length; - - public override int HashSize => 128; - - private ulong H1 { get; set; } - private ulong H2 { get; set; } - - /// - /// Initializes a new instance of the class with the specified seed. - /// - /// The seed to be used. - public Murmur128(uint seed) - { - this.seed = seed; - Initialize(); - } - - protected override void HashCore(byte[] array, int ibStart, int cbSize) - { - length += cbSize; - int remainder = cbSize & 15; - int alignedLength = ibStart + (cbSize - remainder); - for (int i = ibStart; i < alignedLength; i += 16) - { - ulong k1 = BinaryPrimitives.ReadUInt64LittleEndian(array.AsSpan(i)); - k1 *= c1; - k1 = BitOperations.RotateLeft(k1, r1); - k1 *= c2; - H1 ^= k1; - H1 = BitOperations.RotateLeft(H1, 27); - H1 += H2; - H1 = H1 * m + n1; - - ulong k2 = BinaryPrimitives.ReadUInt64LittleEndian(array.AsSpan(i + 8)); - k2 *= c2; - k2 = BitOperations.RotateLeft(k2, r2); - k2 *= c1; - H2 ^= k2; - H2 = BitOperations.RotateLeft(H2, 31); - H2 += H1; - H2 = H2 * m + n2; - } - - if (remainder > 0) - { - ulong remainingBytesL = 0, remainingBytesH = 0; - switch (remainder) - { - case 15: remainingBytesH ^= (ulong)array[alignedLength + 14] << 48; goto case 14; - case 14: remainingBytesH ^= (ulong)array[alignedLength + 13] << 40; goto case 13; - case 13: remainingBytesH ^= (ulong)array[alignedLength + 12] << 32; goto case 12; - case 12: remainingBytesH ^= (ulong)array[alignedLength + 11] << 24; goto case 11; - case 11: remainingBytesH ^= (ulong)array[alignedLength + 10] << 16; goto case 10; - case 10: remainingBytesH ^= (ulong)array[alignedLength + 9] << 8; goto case 9; - case 9: remainingBytesH ^= (ulong)array[alignedLength + 8] << 0; goto case 8; - case 8: remainingBytesL ^= (ulong)array[alignedLength + 7] << 56; goto case 7; - case 7: remainingBytesL ^= (ulong)array[alignedLength + 6] << 48; goto case 6; - case 6: remainingBytesL ^= (ulong)array[alignedLength + 5] << 40; goto case 5; - case 5: remainingBytesL ^= (ulong)array[alignedLength + 4] << 32; goto case 4; - case 4: remainingBytesL ^= (ulong)array[alignedLength + 3] << 24; goto case 3; - case 3: remainingBytesL ^= (ulong)array[alignedLength + 2] << 16; goto case 2; - case 2: remainingBytesL ^= (ulong)array[alignedLength + 1] << 8; goto case 1; - case 1: remainingBytesL ^= (ulong)array[alignedLength] << 0; break; - } - - H2 ^= BitOperations.RotateLeft(remainingBytesH * c2, r2) * c1; - H1 ^= BitOperations.RotateLeft(remainingBytesL * c1, r1) * c2; - } - } - - protected override byte[] HashFinal() - { - ulong len = (ulong)length; - H1 ^= len; H2 ^= len; - - H1 += H2; - H2 += H1; - - H1 = FMix(H1); - H2 = FMix(H2); - - H1 += H2; - H2 += H1; - - var buffer = new byte[sizeof(ulong) * 2]; - Span bytes = buffer; - - BinaryPrimitives.WriteUInt64LittleEndian(bytes, H1); - BinaryPrimitives.WriteUInt64LittleEndian(bytes[sizeof(ulong)..], H2); - - return buffer; - } - - public override void Initialize() - { - H1 = H2 = seed; - length = 0; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong FMix(ulong h) - { - h = (h ^ (h >> 33)) * 0xff51afd7ed558ccd; - h = (h ^ (h >> 33)) * 0xc4ceb9fe1a85ec53; - - return (h ^ (h >> 33)); - } - } -} diff --git a/src/neo/IO/Actors/Idle.cs b/src/neo/IO/Actors/Idle.cs deleted file mode 100644 index cc20eaf993..0000000000 --- a/src/neo/IO/Actors/Idle.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.IO.Actors -{ - internal sealed class Idle - { - public static Idle Instance { get; } = new Idle(); - } -} diff --git a/src/neo/IO/ByteArrayComparer.cs b/src/neo/IO/ByteArrayComparer.cs deleted file mode 100644 index a2e5cf2bdb..0000000000 --- a/src/neo/IO/ByteArrayComparer.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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; -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -namespace Neo.IO -{ - internal class ByteArrayComparer : IComparer - { - public static readonly ByteArrayComparer Default = new(1); - public static readonly ByteArrayComparer Reverse = new(-1); - - private readonly int direction; - - private ByteArrayComparer(int direction) - { - this.direction = direction; - } - - public int Compare(byte[] x, byte[] y) - { - return direction > 0 - ? CompareInternal(x, y) - : -CompareInternal(x, y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int CompareInternal(byte[] x, byte[] y) - { - int length = Math.Min(x.Length, y.Length); - for (int i = 0; i < length; i++) - { - int r = x[i].CompareTo(y[i]); - if (r != 0) return r; - } - return x.Length.CompareTo(y.Length); - } - } -} diff --git a/src/neo/IO/Caching/Tree.cs b/src/neo/IO/Caching/Tree.cs deleted file mode 100644 index 256e4535d5..0000000000 --- a/src/neo/IO/Caching/Tree.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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; -using System.Collections.Generic; - -namespace Neo.IO.Caching -{ - public class Tree - { - public TreeNode Root { get; private set; } - - public TreeNode AddRoot(T item) - { - if (Root is not null) - throw new InvalidOperationException(); - Root = new TreeNode(item, null); - return Root; - } - - public IEnumerable GetItems() - { - if (Root is null) yield break; - foreach (T item in Root.GetItems()) - yield return item; - } - } -} diff --git a/src/neo/IO/Caching/TreeNode.cs b/src/neo/IO/Caching/TreeNode.cs deleted file mode 100644 index 726fd3b0cb..0000000000 --- a/src/neo/IO/Caching/TreeNode.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.Collections.Generic; - -namespace Neo.IO.Caching -{ - public class TreeNode - { - private readonly List> children = new(); - - public T Item { get; } - public TreeNode Parent { get; } - public IReadOnlyList> Children => children; - - internal TreeNode(T item, TreeNode parent) - { - Item = item; - Parent = parent; - } - - public TreeNode AddChild(T item) - { - TreeNode child = new(item, this); - children.Add(child); - return child; - } - - internal IEnumerable GetItems() - { - yield return Item; - foreach (var child in children) - foreach (T item in child.GetItems()) - yield return item; - } - } -} diff --git a/src/neo/IO/Json/JNumber.cs b/src/neo/IO/Json/JNumber.cs deleted file mode 100644 index 4eb5512385..0000000000 --- a/src/neo/IO/Json/JNumber.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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; -using System.Globalization; -using System.Text.Json; - -namespace Neo.IO.Json -{ - /// - /// Represents a JSON number. - /// - public class JNumber : JObject - { - /// - /// Represents the largest safe integer in JSON. - /// - public static readonly long MAX_SAFE_INTEGER = (long)Math.Pow(2, 53) - 1; - - /// - /// Represents the smallest safe integer in JSON. - /// - public static readonly long MIN_SAFE_INTEGER = -MAX_SAFE_INTEGER; - - /// - /// Gets the value of the JSON object. - /// - public double Value { get; } - - /// - /// Initializes a new instance of the class with the specified value. - /// - /// The value of the JSON object. - public JNumber(double value = 0) - { - if (!double.IsFinite(value)) throw new FormatException(); - this.Value = value; - } - - /// - /// Converts the current JSON object to a boolean value. - /// - /// if value is not zero; otherwise, . - public override bool AsBoolean() - { - return Value != 0; - } - - public override double AsNumber() - { - return Value; - } - - public override string AsString() - { - return Value.ToString(CultureInfo.InvariantCulture); - } - - public override double GetNumber() => Value; - - public override string ToString() - { - return AsString(); - } - - public override T TryGetEnum(T defaultValue = default, bool ignoreCase = false) - { - Type enumType = typeof(T); - object value; - try - { - value = Convert.ChangeType(Value, enumType.GetEnumUnderlyingType()); - } - catch (OverflowException) - { - return defaultValue; - } - object result = Enum.ToObject(enumType, value); - return Enum.IsDefined(enumType, result) ? (T)result : defaultValue; - } - - internal override void Write(Utf8JsonWriter writer) - { - writer.WriteNumberValue(Value); - } - - public override JObject Clone() - { - return this; - } - - public static implicit operator JNumber(double value) - { - return new JNumber(value); - } - } -} diff --git a/src/neo/IO/Json/JObject.cs b/src/neo/IO/Json/JObject.cs deleted file mode 100644 index 678c1d85db..0000000000 --- a/src/neo/IO/Json/JObject.cs +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.IO.Caching; -using System; -using System.Collections.Generic; -using System.IO; -using System.Text.Json; - -namespace Neo.IO.Json -{ - /// - /// Represents a JSON object. - /// - public class JObject - { - /// - /// Represents a object. - /// - public static readonly JObject Null = null; - - private readonly OrderedDictionary properties = new(); - - /// - /// Gets or sets the properties of the JSON object. - /// - public IDictionary Properties => properties; - - /// - /// Gets or sets the properties of the JSON object. - /// - /// The name of the property to get or set. - /// The property with the specified name. - public JObject this[string name] - { - get - { - if (Properties.TryGetValue(name, out JObject value)) - return value; - return null; - } - set - { - Properties[name] = value; - } - } - - /// - /// Gets the property at the specified index. - /// - /// The zero-based index of the property to get. - /// The property at the specified index. - public virtual JObject this[int index] - { - get => properties[index]; - set => throw new NotSupportedException(); - } - - /// - /// Converts the current JSON object to a boolean value. - /// - /// The converted value. - public virtual bool AsBoolean() - { - return true; - } - - /// - /// Converts the current JSON object to a floating point number. - /// - /// The converted value. - public virtual double AsNumber() - { - return double.NaN; - } - - /// - /// Converts the current JSON object to a . - /// - /// The converted value. - public virtual string AsString() - { - return ToString(); - } - - /// - /// Determines whether the JSON object contains a property with the specified name. - /// - /// The property name to locate in the JSON object. - /// if the JSON object contains a property with the name; otherwise, . - public bool ContainsProperty(string key) - { - return Properties.ContainsKey(key); - } - - /// - /// Converts the current JSON object to a object. - /// - /// The converted value. - /// The JSON object is not a . - public virtual JArray GetArray() => throw new InvalidCastException(); - - /// - /// Converts the current JSON object to a boolean value. - /// - /// The converted value. - /// The JSON object is not a . - public virtual bool GetBoolean() => throw new InvalidCastException(); - - /// - /// Converts the current JSON object to a 32-bit signed integer. - /// - /// The converted value. - /// The JSON object is not a . - /// The JSON object cannot be converted to an integer. - /// The JSON object cannot be converted to a 32-bit signed integer. - public int GetInt32() - { - double d = GetNumber(); - if (d % 1 != 0) throw new InvalidCastException(); - return checked((int)d); - } - - /// - /// Converts the current JSON object to a floating point number. - /// - /// The converted value. - /// The JSON object is not a . - public virtual double GetNumber() => throw new InvalidCastException(); - - /// - /// Converts the current JSON object to a . - /// - /// The converted value. - /// The JSON object is not a . - public virtual string GetString() => throw new InvalidCastException(); - - /// - /// Parses a JSON object from a byte array. - /// - /// The byte array that contains the JSON object. - /// The maximum nesting depth when parsing the JSON object. - /// The parsed JSON object. - public static JObject Parse(ReadOnlySpan value, int max_nest = 100) - { - Utf8JsonReader reader = new(value, new JsonReaderOptions - { - AllowTrailingCommas = false, - CommentHandling = JsonCommentHandling.Skip, - MaxDepth = max_nest - }); - try - { - JObject json = Read(ref reader); - if (reader.Read()) throw new FormatException(); - return json; - } - catch (JsonException ex) - { - throw new FormatException(ex.Message, ex); - } - } - - /// - /// Parses a JSON object from a . - /// - /// The that contains the JSON object. - /// The maximum nesting depth when parsing the JSON object. - /// The parsed JSON object. - public static JObject Parse(string value, int max_nest = 100) - { - return Parse(Utility.StrictUTF8.GetBytes(value), max_nest); - } - - private static JObject Read(ref Utf8JsonReader reader, bool skipReading = false) - { - if (!skipReading && !reader.Read()) throw new FormatException(); - return reader.TokenType switch - { - JsonTokenType.False => false, - JsonTokenType.Null => Null, - JsonTokenType.Number => reader.GetDouble(), - JsonTokenType.StartArray => ReadArray(ref reader), - JsonTokenType.StartObject => ReadObject(ref reader), - JsonTokenType.String => ReadString(ref reader), - JsonTokenType.True => true, - _ => throw new FormatException(), - }; - } - - private static JArray ReadArray(ref Utf8JsonReader reader) - { - JArray array = new(); - while (reader.Read()) - { - switch (reader.TokenType) - { - case JsonTokenType.EndArray: - return array; - default: - array.Add(Read(ref reader, skipReading: true)); - break; - } - } - throw new FormatException(); - } - - private static JObject ReadObject(ref Utf8JsonReader reader) - { - JObject obj = new(); - while (reader.Read()) - { - switch (reader.TokenType) - { - case JsonTokenType.EndObject: - return obj; - case JsonTokenType.PropertyName: - string name = ReadString(ref reader); - if (obj.Properties.ContainsKey(name)) throw new FormatException(); - JObject value = Read(ref reader); - obj.Properties.Add(name, value); - break; - default: - throw new FormatException(); - } - } - throw new FormatException(); - } - - private static string ReadString(ref Utf8JsonReader reader) - { - try - { - return reader.GetString(); - } - catch (InvalidOperationException ex) - { - throw new FormatException(ex.Message, ex); - } - } - - /// - /// Encode the current JSON object into a byte array. - /// - /// Indicates whether indentation is required. - /// The encoded JSON object. - public byte[] ToByteArray(bool indented) - { - using MemoryStream ms = new(); - using Utf8JsonWriter writer = new(ms, new JsonWriterOptions - { - Indented = indented, - SkipValidation = true - }); - Write(writer); - writer.Flush(); - return ms.ToArray(); - } - - /// - /// Encode the current JSON object into a . - /// - /// The encoded JSON object. - public override string ToString() - { - return ToString(false); - } - - /// - /// Encode the current JSON object into a . - /// - /// Indicates whether indentation is required. - /// The encoded JSON object. - public string ToString(bool indented) - { - return Utility.StrictUTF8.GetString(ToByteArray(indented)); - } - - /// - /// Converts the current JSON object to an . - /// - /// The type of the . - /// If the current JSON object cannot be converted to type , then the default value is returned. - /// Indicates whether case should be ignored during conversion. - /// The converted value. - public virtual T TryGetEnum(T defaultValue = default, bool ignoreCase = false) where T : Enum - { - return defaultValue; - } - - internal virtual void Write(Utf8JsonWriter writer) - { - writer.WriteStartObject(); - foreach (KeyValuePair pair in Properties) - { - writer.WritePropertyName(pair.Key); - if (pair.Value is null) - writer.WriteNullValue(); - else - pair.Value.Write(writer); - } - writer.WriteEndObject(); - } - - /// - /// Creates a copy of the current JSON object. - /// - /// A copy of the current JSON object. - public virtual JObject Clone() - { - var cloned = new JObject(); - - foreach (KeyValuePair pair in Properties) - { - cloned[pair.Key] = pair.Value != null ? pair.Value.Clone() : Null; - } - - return cloned; - } - - public JArray JsonPath(string expr) - { - JObject[] objects = { this }; - if (expr.Length == 0) return objects; - Queue tokens = new(JPathToken.Parse(expr)); - JPathToken first = tokens.Dequeue(); - if (first.Type != JPathTokenType.Root) throw new FormatException(); - JPathToken.ProcessJsonPath(ref objects, tokens); - return objects; - } - - public static implicit operator JObject(Enum value) - { - return (JString)value; - } - - public static implicit operator JObject(JObject[] value) - { - return (JArray)value; - } - - public static implicit operator JObject(bool value) - { - return (JBoolean)value; - } - - public static implicit operator JObject(double value) - { - return (JNumber)value; - } - - public static implicit operator JObject(string value) - { - return (JString)value; - } - } -} diff --git a/src/neo/IO/Json/JPathTokenType.cs b/src/neo/IO/Json/JPathTokenType.cs deleted file mode 100644 index 500b2d72cc..0000000000 --- a/src/neo/IO/Json/JPathTokenType.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.IO.Json -{ - enum JPathTokenType : byte - { - Root, - Dot, - LeftBracket, - RightBracket, - Asterisk, - Comma, - Colon, - Identifier, - String, - Number - } -} diff --git a/src/neo/IO/Json/JString.cs b/src/neo/IO/Json/JString.cs deleted file mode 100644 index 1746e5291e..0000000000 --- a/src/neo/IO/Json/JString.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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; -using System.Globalization; -using System.Text.Json; - -namespace Neo.IO.Json -{ - /// - /// Represents a JSON string. - /// - public class JString : JObject - { - /// - /// Gets the value of the JSON object. - /// - public string Value { get; } - - /// - /// Initializes a new instance of the class with the specified value. - /// - /// The value of the JSON object. - public JString(string value) - { - this.Value = value ?? throw new ArgumentNullException(nameof(value)); - } - - /// - /// Converts the current JSON object to a boolean value. - /// - /// if value is not empty; otherwise, . - public override bool AsBoolean() - { - return !string.IsNullOrEmpty(Value); - } - - public override double AsNumber() - { - if (string.IsNullOrEmpty(Value)) return 0; - return double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double result) ? result : double.NaN; - } - - public override string AsString() - { - return Value; - } - - public override string GetString() => Value; - - public override T TryGetEnum(T defaultValue = default, bool ignoreCase = false) - { - try - { - return (T)Enum.Parse(typeof(T), Value, ignoreCase); - } - catch - { - return defaultValue; - } - } - - internal override void Write(Utf8JsonWriter writer) - { - writer.WriteStringValue(Value); - } - - public override JObject Clone() - { - return this; - } - - public static implicit operator JString(Enum value) - { - return new JString(value.ToString()); - } - - public static implicit operator JString(string value) - { - return value == null ? null : new JString(value); - } - } -} diff --git a/src/neo/Network/P2P/Payloads/Conditions/CalledByEntryCondition.cs b/src/neo/Network/P2P/Payloads/Conditions/CalledByEntryCondition.cs deleted file mode 100644 index 0f0d62bc0d..0000000000 --- a/src/neo/Network/P2P/Payloads/Conditions/CalledByEntryCondition.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.SmartContract; -using System.IO; - -namespace Neo.Network.P2P.Payloads.Conditions -{ - public class CalledByEntryCondition : WitnessCondition - { - public override WitnessConditionType Type => WitnessConditionType.CalledByEntry; - - protected override void DeserializeWithoutType(BinaryReader reader, int maxNestDepth) - { - } - - public override bool Match(ApplicationEngine engine) - { - return engine.CallingScriptHash is null || engine.CallingScriptHash == engine.EntryScriptHash; - } - - protected override void SerializeWithoutType(BinaryWriter writer) - { - } - } -} diff --git a/src/neo/Network/P2P/Payloads/TransactionAttributeType.cs b/src/neo/Network/P2P/Payloads/TransactionAttributeType.cs deleted file mode 100644 index fae5a09ee2..0000000000 --- a/src/neo/Network/P2P/Payloads/TransactionAttributeType.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.IO.Caching; - -namespace Neo.Network.P2P.Payloads -{ - /// - /// Represents the type of a . - /// - public enum TransactionAttributeType : byte - { - /// - /// Indicates that the transaction is of high priority. - /// - [ReflectionCache(typeof(HighPriorityAttribute))] - HighPriority = 0x01, - - /// - /// Indicates that the transaction is an oracle response. - /// - [ReflectionCache(typeof(OracleResponse))] - OracleResponse = 0x11 - } -} diff --git a/src/neo/Persistence/Helper.cs b/src/neo/Persistence/Helper.cs deleted file mode 100644 index b5088af73a..0000000000 --- a/src/neo/Persistence/Helper.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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; - -namespace Neo.Persistence -{ - internal static class Helper - { - public static byte[] EnsureNotNull(this byte[] source) - { - return source ?? Array.Empty(); - } - } -} diff --git a/src/neo/Persistence/MemoryStore.cs b/src/neo/Persistence/MemoryStore.cs deleted file mode 100644 index f73777e992..0000000000 --- a/src/neo/Persistence/MemoryStore.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.IO; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; - -namespace Neo.Persistence -{ - /// - /// An in-memory implementation that uses ConcurrentDictionary as the underlying storage. - /// - public class MemoryStore : IStore - { - private readonly ConcurrentDictionary innerData = new(ByteArrayEqualityComparer.Default); - - public void Delete(byte[] key) - { - innerData.TryRemove(key.EnsureNotNull(), out _); - } - - public void Dispose() - { - } - - public ISnapshot GetSnapshot() - { - return new MemorySnapshot(innerData); - } - - public void Put(byte[] key, byte[] value) - { - innerData[key.EnsureNotNull()] = value; - } - - public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[] keyOrPrefix, SeekDirection direction = SeekDirection.Forward) - { - if (direction == SeekDirection.Backward && keyOrPrefix?.Length == 0) yield break; - - ByteArrayComparer comparer = direction == SeekDirection.Forward ? ByteArrayComparer.Default : ByteArrayComparer.Reverse; - IEnumerable> records = innerData; - if (keyOrPrefix?.Length > 0) - records = records.Where(p => comparer.Compare(p.Key, keyOrPrefix) >= 0); - records = records.OrderBy(p => p.Key, comparer); - foreach (var pair in records) - yield return (pair.Key, pair.Value); - } - - public byte[] TryGet(byte[] key) - { - innerData.TryGetValue(key.EnsureNotNull(), out byte[] value); - return value; - } - - public bool Contains(byte[] key) - { - return innerData.ContainsKey(key.EnsureNotNull()); - } - } -} diff --git a/src/neo/Plugins/ILogPlugin.cs b/src/neo/Plugins/ILogPlugin.cs deleted file mode 100644 index c0224206e5..0000000000 --- a/src/neo/Plugins/ILogPlugin.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.Plugins -{ - /// - /// A plug-in interface for logs. - /// - public interface ILogPlugin - { - /// - /// Writes a new log to the plugin. - /// - /// The source of the log. Used to identify the producer of the log. - /// The level of the log. - /// The message of the log. - void Log(string source, LogLevel level, object message); - } -} diff --git a/src/neo/Plugins/IMemoryPoolTxObserverPlugin.cs b/src/neo/Plugins/IMemoryPoolTxObserverPlugin.cs deleted file mode 100644 index 046a988aba..0000000000 --- a/src/neo/Plugins/IMemoryPoolTxObserverPlugin.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.Network.P2P.Payloads; -using System.Collections.Generic; - -namespace Neo.Plugins -{ - /// - /// An interface that allows plugins to observe changes in the memory pool. - /// - public interface IMemoryPoolTxObserverPlugin - { - /// - /// Called when a transaction is added to the memory pool. - /// - /// The object that contains the memory pool. - /// The transaction added. - void TransactionAdded(NeoSystem system, Transaction tx); - - /// - /// Called when transactions are removed from the memory pool. - /// - /// The object that contains the memory pool. - /// The reason the transactions were removed. - /// The removed transactions. - void TransactionsRemoved(NeoSystem system, MemoryPoolTxRemovalReason reason, IEnumerable transactions); - } -} diff --git a/src/neo/Plugins/IP2PPlugin.cs b/src/neo/Plugins/IP2PPlugin.cs deleted file mode 100644 index b5b0f3fa95..0000000000 --- a/src/neo/Plugins/IP2PPlugin.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.Network.P2P; - -namespace Neo.Plugins -{ - /// - /// An interface that allows plugins to observe the messages on the network. - /// - public interface IP2PPlugin - { - /// - /// Called when a message is received from a remote node. - /// - /// The object that contains the local node. - /// The received message. - /// if the should be dropped; otherwise, . - bool OnP2PMessage(NeoSystem system, Message message) => true; - } -} diff --git a/src/neo/Plugins/IPersistencePlugin.cs b/src/neo/Plugins/IPersistencePlugin.cs deleted file mode 100644 index 1d01e8b0a9..0000000000 --- a/src/neo/Plugins/IPersistencePlugin.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.Network.P2P.Payloads; -using Neo.Persistence; -using System; -using System.Collections.Generic; -using static Neo.Ledger.Blockchain; - -namespace Neo.Plugins -{ - /// - /// An interface that allows plugins to observe the persisted blocks. - /// - public interface IPersistencePlugin - { - /// - /// Called when a block is being persisted. - /// - /// The object that contains the blockchain. - /// The block being persisted. - /// The snapshot used for persistence. - /// The execution result of the contracts in the block. - void OnPersist(NeoSystem system, Block block, DataCache snapshot, IReadOnlyList applicationExecutedList) { } - - /// - /// Called when a block has been persisted. - /// - /// The object that contains the blockchain. - /// The block being persisted. - /// The snapshot used for persistence. - void OnCommit(NeoSystem system, Block block, DataCache snapshot) { } - - /// - /// Indicates whether to allow exceptions to be thrown from the plugin when committing. - /// - /// The exception to be thrown. - /// if the exception should be thrown; otherwise, . - bool ShouldThrowExceptionFromCommit(Exception ex) => false; - } -} diff --git a/src/neo/Plugins/MemoryPoolTxRemovalReason.cs b/src/neo/Plugins/MemoryPoolTxRemovalReason.cs deleted file mode 100644 index 0ccb2b336d..0000000000 --- a/src/neo/Plugins/MemoryPoolTxRemovalReason.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.Plugins -{ - /// - /// The reason a transaction was removed. - /// - public enum MemoryPoolTxRemovalReason : byte - { - /// - /// The transaction was ejected since it was the lowest priority transaction and the memory pool capacity was exceeded. - /// - CapacityExceeded, - - /// - /// The transaction was ejected due to failing re-validation after a block was persisted. - /// - NoLongerValid, - } -} diff --git a/src/neo/Properties/AssemblyInfo.cs b/src/neo/Properties/AssemblyInfo.cs deleted file mode 100644 index c82a7bcfc2..0000000000 --- a/src/neo/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] -[assembly: InternalsVisibleTo("neo.UnitTests")] -[assembly: InternalsVisibleTo("neodebug-3-adapter")] diff --git a/src/neo/SmartContract/Diagnostic.cs b/src/neo/SmartContract/Diagnostic.cs deleted file mode 100644 index f2f4231fff..0000000000 --- a/src/neo/SmartContract/Diagnostic.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.IO.Caching; - -namespace Neo.SmartContract -{ - public class Diagnostic - { - public Tree InvocationTree { get; } = new(); - } -} diff --git a/src/neo/SmartContract/Native/ContractMethodAttribute.cs b/src/neo/SmartContract/Native/ContractMethodAttribute.cs deleted file mode 100644 index 6bf7ffe093..0000000000 --- a/src/neo/SmartContract/Native/ContractMethodAttribute.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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; - -namespace Neo.SmartContract.Native -{ - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false)] - internal class ContractMethodAttribute : Attribute - { - public string Name { get; init; } - public CallFlags RequiredCallFlags { get; init; } - public long CpuFee { get; init; } - public long StorageFee { get; init; } - } -} diff --git a/src/neo/SmartContract/Native/TransactionState.cs b/src/neo/SmartContract/Native/TransactionState.cs deleted file mode 100644 index beb1c1c517..0000000000 --- a/src/neo/SmartContract/Native/TransactionState.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.IO; -using Neo.Network.P2P.Payloads; -using Neo.VM; -using Neo.VM.Types; - -namespace Neo.SmartContract.Native -{ - /// - /// Represents a transaction that has been included in a block. - /// - public class TransactionState : IInteroperable - { - /// - /// The block containing this transaction. - /// - public uint BlockIndex; - - /// - /// The transaction. - /// - public Transaction Transaction; - - /// - /// The execution state - /// - public VMState State; - - private StackItem _rawTransaction; - - void IInteroperable.FromStackItem(StackItem stackItem) - { - Struct @struct = (Struct)stackItem; - BlockIndex = (uint)@struct[0].GetInteger(); - _rawTransaction = @struct[1]; - Transaction = _rawTransaction.GetSpan().AsSerializable(); - State = (VMState)(byte)@struct[2].GetInteger(); - } - - StackItem IInteroperable.ToStackItem(ReferenceCounter referenceCounter) - { - _rawTransaction ??= Transaction.ToArray(); - return new Struct(referenceCounter) { BlockIndex, _rawTransaction, (byte)State }; - } - } -} diff --git a/src/neo/Wallets/Helper.cs b/src/neo/Wallets/Helper.cs deleted file mode 100644 index 106cf3012a..0000000000 --- a/src/neo/Wallets/Helper.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.Cryptography; -using Neo.IO; -using Neo.Network.P2P; -using Neo.Network.P2P.Payloads; -using System; - -namespace Neo.Wallets -{ - /// - /// A helper class related to wallets. - /// - public static class Helper - { - /// - /// Signs an with the specified private key. - /// - /// The to sign. - /// The private key to be used. - /// The magic number of the NEO network. - /// The signature for the . - public static byte[] Sign(this IVerifiable verifiable, KeyPair key, uint network) - { - return Crypto.Sign(verifiable.GetSignData(network), key.PrivateKey, key.PublicKey.EncodePoint(false)[1..]); - } - - /// - /// Converts the specified script hash to an address. - /// - /// The script hash to convert. - /// The address version. - /// The converted address. - public static string ToAddress(this UInt160 scriptHash, byte version) - { - Span data = stackalloc byte[21]; - data[0] = version; - scriptHash.ToArray().CopyTo(data[1..]); - return Base58.Base58CheckEncode(data); - } - - /// - /// Converts the specified address to a script hash. - /// - /// The address to convert. - /// The address version. - /// The converted script hash. - public static UInt160 ToScriptHash(this string address, byte version) - { - byte[] data = address.Base58CheckDecode(); - if (data.Length != 21) - throw new FormatException(); - if (data[0] != version) - throw new FormatException(); - return new UInt160(data.AsSpan(1)); - } - - internal static byte[] XOR(byte[] x, byte[] y) - { - if (x.Length != y.Length) throw new ArgumentException(); - byte[] r = new byte[x.Length]; - for (int i = 0; i < r.Length; i++) - r[i] = (byte)(x[i] ^ y[i]); - return r; - } - } -} diff --git a/src/neo/Wallets/NEP6/WalletLocker.cs b/src/neo/Wallets/NEP6/WalletLocker.cs deleted file mode 100644 index f0f849c7b0..0000000000 --- a/src/neo/Wallets/NEP6/WalletLocker.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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; - -namespace Neo.Wallets.NEP6 -{ - internal class WalletLocker : IDisposable - { - private readonly NEP6Wallet wallet; - - public WalletLocker(NEP6Wallet wallet) - { - this.wallet = wallet; - } - - public void Dispose() - { - wallet.Lock(); - } - } -} diff --git a/src/neo/Wallets/SQLite/Account.cs b/src/neo/Wallets/SQLite/Account.cs deleted file mode 100644 index a8b1da2555..0000000000 --- a/src/neo/Wallets/SQLite/Account.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.Wallets.SQLite -{ - internal class Account - { - public byte[] PublicKeyHash { get; set; } - public string Nep2key { get; set; } - } -} diff --git a/src/neo/Wallets/SQLite/Address.cs b/src/neo/Wallets/SQLite/Address.cs deleted file mode 100644 index b8db4a73f9..0000000000 --- a/src/neo/Wallets/SQLite/Address.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.Wallets.SQLite -{ - internal class Address - { - public byte[] ScriptHash { get; set; } - } -} diff --git a/src/neo/Wallets/SQLite/Contract.cs b/src/neo/Wallets/SQLite/Contract.cs deleted file mode 100644 index 909e6847a1..0000000000 --- a/src/neo/Wallets/SQLite/Contract.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.Wallets.SQLite -{ - internal class Contract - { - public byte[] RawData { get; set; } - public byte[] ScriptHash { get; set; } - public byte[] PublicKeyHash { get; set; } - public Account Account { get; set; } - public Address Address { get; set; } - } -} diff --git a/src/neo/Wallets/SQLite/Key.cs b/src/neo/Wallets/SQLite/Key.cs deleted file mode 100644 index 0dfcea35b4..0000000000 --- a/src/neo/Wallets/SQLite/Key.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.Wallets.SQLite -{ - internal class Key - { - public string Name { get; set; } - public byte[] Value { get; set; } - } -} diff --git a/src/neo/Wallets/SQLite/UserWallet.cs b/src/neo/Wallets/SQLite/UserWallet.cs deleted file mode 100644 index d757d22637..0000000000 --- a/src/neo/Wallets/SQLite/UserWallet.cs +++ /dev/null @@ -1,431 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.EntityFrameworkCore; -using Neo.Cryptography; -using Neo.IO; -using Neo.SmartContract; -using Neo.Wallets.NEP6; -using System; -using System.Buffers.Binary; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Security; -using System.Security.Cryptography; -using System.Text; -using static System.IO.Path; - -namespace Neo.Wallets.SQLite -{ - /// - /// A wallet implementation that uses SQLite as the underlying storage. - /// - public class UserWallet : Wallet - { - private readonly object db_lock = new(); - private readonly byte[] iv; - private readonly byte[] salt; - private readonly byte[] masterKey; - private readonly ScryptParameters scrypt; - private readonly Dictionary accounts; - - public override string Name => GetFileNameWithoutExtension(Path); - - public override Version Version - { - get - { - byte[] buffer = LoadStoredData("Version"); - if (buffer == null || buffer.Length < 16) return new Version(0, 0); - int major = BinaryPrimitives.ReadInt32LittleEndian(buffer); - int minor = BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(4)); - int build = BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(8)); - int revision = BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(12)); - return new Version(major, minor, build, revision); - } - } - - private UserWallet(string path, byte[] passwordKey, ProtocolSettings settings) : base(path, settings) - { - this.salt = LoadStoredData("Salt"); - byte[] passwordHash = LoadStoredData("PasswordHash"); - if (passwordHash != null && !passwordHash.SequenceEqual(passwordKey.Concat(salt).ToArray().Sha256())) - throw new CryptographicException(); - this.iv = LoadStoredData("IV"); - this.masterKey = Decrypt(LoadStoredData("MasterKey"), passwordKey, iv); - this.scrypt = new ScryptParameters - ( - BinaryPrimitives.ReadInt32LittleEndian(LoadStoredData("ScryptN")), - BinaryPrimitives.ReadInt32LittleEndian(LoadStoredData("ScryptR")), - BinaryPrimitives.ReadInt32LittleEndian(LoadStoredData("ScryptP")) - ); - this.accounts = LoadAccounts(); - } - - private UserWallet(string path, byte[] passwordKey, ProtocolSettings settings, ScryptParameters scrypt) : base(path, settings) - { - this.iv = new byte[16]; - this.salt = new byte[20]; - this.masterKey = new byte[32]; - this.scrypt = scrypt; - this.accounts = new Dictionary(); - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) - { - rng.GetBytes(iv); - rng.GetBytes(salt); - rng.GetBytes(masterKey); - } - Version version = Assembly.GetExecutingAssembly().GetName().Version; - byte[] versionBuffer = new byte[sizeof(int) * 4]; - BinaryPrimitives.WriteInt32LittleEndian(versionBuffer, version.Major); - BinaryPrimitives.WriteInt32LittleEndian(versionBuffer.AsSpan(4), version.Minor); - BinaryPrimitives.WriteInt32LittleEndian(versionBuffer.AsSpan(8), version.Build); - BinaryPrimitives.WriteInt32LittleEndian(versionBuffer.AsSpan(12), version.Revision); - BuildDatabase(); - SaveStoredData("IV", iv); - SaveStoredData("Salt", salt); - SaveStoredData("PasswordHash", passwordKey.Concat(salt).ToArray().Sha256()); - SaveStoredData("MasterKey", Encrypt(masterKey, passwordKey, iv)); - SaveStoredData("Version", versionBuffer); - SaveStoredData("ScryptN", this.scrypt.N); - SaveStoredData("ScryptR", this.scrypt.R); - SaveStoredData("ScryptP", this.scrypt.P); - } - - private void AddAccount(UserWalletAccount account) - { - lock (accounts) - { - if (accounts.TryGetValue(account.ScriptHash, out UserWalletAccount account_old)) - { - if (account.Contract == null) - { - account.Contract = account_old.Contract; - } - } - accounts[account.ScriptHash] = account; - } - lock (db_lock) - { - using WalletDataContext ctx = new(Path); - if (account.HasKey) - { - string passphrase = Encoding.UTF8.GetString(masterKey); - Account db_account = ctx.Accounts.FirstOrDefault(p => p.PublicKeyHash == account.Key.PublicKeyHash.ToArray()); - if (db_account == null) - { - db_account = ctx.Accounts.Add(new Account - { - Nep2key = account.Key.Export(passphrase, ProtocolSettings.AddressVersion, scrypt.N, scrypt.R, scrypt.P), - PublicKeyHash = account.Key.PublicKeyHash.ToArray() - }).Entity; - } - else - { - db_account.Nep2key = account.Key.Export(passphrase, ProtocolSettings.AddressVersion, scrypt.N, scrypt.R, scrypt.P); - } - } - if (account.Contract != null) - { - Contract db_contract = ctx.Contracts.FirstOrDefault(p => p.ScriptHash == account.Contract.ScriptHash.ToArray()); - if (db_contract != null) - { - db_contract.PublicKeyHash = account.Key.PublicKeyHash.ToArray(); - } - else - { - ctx.Contracts.Add(new Contract - { - RawData = ((VerificationContract)account.Contract).ToArray(), - ScriptHash = account.Contract.ScriptHash.ToArray(), - PublicKeyHash = account.Key.PublicKeyHash.ToArray() - }); - } - } - //add address - { - Address db_address = ctx.Addresses.FirstOrDefault(p => p.ScriptHash == account.ScriptHash.ToArray()); - if (db_address == null) - { - ctx.Addresses.Add(new Address - { - ScriptHash = account.ScriptHash.ToArray() - }); - } - } - ctx.SaveChanges(); - } - } - - private void BuildDatabase() - { - using WalletDataContext ctx = new(Path); - ctx.Database.EnsureDeleted(); - ctx.Database.EnsureCreated(); - } - - public override bool ChangePassword(string oldPassword, string newPassword) - { - if (!VerifyPassword(oldPassword)) return false; - byte[] passwordKey = newPassword.ToAesKey(); - try - { - SaveStoredData("PasswordHash", passwordKey.Concat(salt).ToArray().Sha256()); - SaveStoredData("MasterKey", Encrypt(masterKey, passwordKey, iv)); - return true; - } - finally - { - Array.Clear(passwordKey, 0, passwordKey.Length); - } - } - - public override bool Contains(UInt160 scriptHash) - { - lock (accounts) - { - return accounts.ContainsKey(scriptHash); - } - } - - /// - /// Creates a new wallet at the specified path. - /// - /// The path of the wallet. - /// The password of the wallet. - /// The to be used by the wallet. - /// The parameters of the SCrypt algorithm used for encrypting and decrypting the private keys in the wallet. - /// The created wallet. - public static UserWallet Create(string path, string password, ProtocolSettings settings, ScryptParameters scrypt = null) - { - return new UserWallet(path, password.ToAesKey(), settings, scrypt ?? ScryptParameters.Default); - } - - /// - /// Creates a new wallet at the specified path. - /// - /// The path of the wallet. - /// The password of the wallet. - /// The to be used by the wallet. - /// The parameters of the SCrypt algorithm used for encrypting and decrypting the private keys in the wallet. - /// The created wallet. - public static UserWallet Create(string path, SecureString password, ProtocolSettings settings, ScryptParameters scrypt = null) - { - return new UserWallet(path, password.ToAesKey(), settings, scrypt ?? ScryptParameters.Default); - } - - public override WalletAccount CreateAccount(byte[] privateKey) - { - KeyPair key = new(privateKey); - VerificationContract contract = new() - { - Script = SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), - ParameterList = new[] { ContractParameterType.Signature } - }; - UserWalletAccount account = new(contract.ScriptHash, ProtocolSettings) - { - Key = key, - Contract = contract - }; - AddAccount(account); - return account; - } - - public override WalletAccount CreateAccount(SmartContract.Contract contract, KeyPair key = null) - { - if (contract is not VerificationContract verification_contract) - { - verification_contract = new VerificationContract - { - Script = contract.Script, - ParameterList = contract.ParameterList - }; - } - UserWalletAccount account = new(verification_contract.ScriptHash, ProtocolSettings) - { - Key = key, - Contract = verification_contract - }; - AddAccount(account); - return account; - } - - public override WalletAccount CreateAccount(UInt160 scriptHash) - { - UserWalletAccount account = new(scriptHash, ProtocolSettings); - AddAccount(account); - return account; - } - - public override void Delete() - { - using WalletDataContext ctx = new(Path); - ctx.Database.EnsureDeleted(); - } - - public override bool DeleteAccount(UInt160 scriptHash) - { - UserWalletAccount account; - lock (accounts) - { - if (accounts.TryGetValue(scriptHash, out account)) - accounts.Remove(scriptHash); - } - if (account != null) - { - lock (db_lock) - { - using WalletDataContext ctx = new(Path); - if (account.HasKey) - { - Account db_account = ctx.Accounts.First(p => p.PublicKeyHash == account.Key.PublicKeyHash.ToArray()); - ctx.Accounts.Remove(db_account); - } - if (account.Contract != null) - { - Contract db_contract = ctx.Contracts.First(p => p.ScriptHash == scriptHash.ToArray()); - ctx.Contracts.Remove(db_contract); - } - //delete address - { - Address db_address = ctx.Addresses.First(p => p.ScriptHash == scriptHash.ToArray()); - ctx.Addresses.Remove(db_address); - } - ctx.SaveChanges(); - } - return true; - } - return false; - } - - public override WalletAccount GetAccount(UInt160 scriptHash) - { - lock (accounts) - { - accounts.TryGetValue(scriptHash, out UserWalletAccount account); - return account; - } - } - - public override IEnumerable GetAccounts() - { - lock (accounts) - { - foreach (UserWalletAccount account in accounts.Values) - yield return account; - } - } - - private Dictionary LoadAccounts() - { - using WalletDataContext ctx = new(Path); - string passphrase = Encoding.UTF8.GetString(masterKey); - Dictionary accounts = ctx.Addresses.Select(p => p.ScriptHash).AsEnumerable().Select(p => new UserWalletAccount(new UInt160(p), ProtocolSettings)).ToDictionary(p => p.ScriptHash); - foreach (Contract db_contract in ctx.Contracts.Include(p => p.Account)) - { - VerificationContract contract = db_contract.RawData.AsSerializable(); - UserWalletAccount account = accounts[contract.ScriptHash]; - account.Contract = contract; - account.Key = new KeyPair(GetPrivateKeyFromNEP2(db_contract.Account.Nep2key, passphrase, ProtocolSettings.AddressVersion, scrypt.N, scrypt.R, scrypt.P)); - } - return accounts; - } - - private byte[] LoadStoredData(string name) - { - using WalletDataContext ctx = new(Path); - return ctx.Keys.FirstOrDefault(p => p.Name == name)?.Value; - } - - /// - /// Opens a wallet at the specified path. - /// - /// The path of the wallet. - /// The password of the wallet. - /// The to be used by the wallet. - /// The opened wallet. - public static UserWallet Open(string path, string password, ProtocolSettings settings) - { - return new UserWallet(path, password.ToAesKey(), settings); - } - - /// - /// Opens a wallet at the specified path. - /// - /// The path of the wallet. - /// The password of the wallet. - /// The to be used by the wallet. - /// The opened wallet. - public static UserWallet Open(string path, SecureString password, ProtocolSettings settings) - { - return new UserWallet(path, password.ToAesKey(), settings); - } - - private void SaveStoredData(string name, int value) - { - byte[] data = new byte[sizeof(int)]; - BinaryPrimitives.WriteInt32LittleEndian(data, value); - SaveStoredData(name, data); - } - - private void SaveStoredData(string name, byte[] value) - { - lock (db_lock) - { - using WalletDataContext ctx = new(Path); - SaveStoredData(ctx, name, value); - ctx.SaveChanges(); - } - } - - private static void SaveStoredData(WalletDataContext ctx, string name, byte[] value) - { - Key key = ctx.Keys.FirstOrDefault(p => p.Name == name); - if (key == null) - { - ctx.Keys.Add(new Key - { - Name = name, - Value = value - }); - } - else - { - key.Value = value; - } - } - - public override bool VerifyPassword(string password) - { - return password.ToAesKey().Concat(salt).ToArray().Sha256().SequenceEqual(LoadStoredData("PasswordHash")); - } - - private static byte[] Encrypt(byte[] data, byte[] key, byte[] iv) - { - if (data == null || key == null || iv == null) throw new ArgumentNullException(); - if (data.Length % 16 != 0 || key.Length != 32 || iv.Length != 16) throw new ArgumentException(); - using Aes aes = Aes.Create(); - aes.Padding = PaddingMode.None; - using ICryptoTransform encryptor = aes.CreateEncryptor(key, iv); - return encryptor.TransformFinalBlock(data, 0, data.Length); - } - - private static byte[] Decrypt(byte[] data, byte[] key, byte[] iv) - { - if (data == null || key == null || iv == null) throw new ArgumentNullException(); - if (data.Length % 16 != 0 || key.Length != 32 || iv.Length != 16) throw new ArgumentException(); - using Aes aes = Aes.Create(); - aes.Padding = PaddingMode.None; - using ICryptoTransform decryptor = aes.CreateDecryptor(key, iv); - return decryptor.TransformFinalBlock(data, 0, data.Length); - } - } -} diff --git a/src/neo/Wallets/SQLite/UserWalletAccount.cs b/src/neo/Wallets/SQLite/UserWalletAccount.cs deleted file mode 100644 index b94f44471f..0000000000 --- a/src/neo/Wallets/SQLite/UserWalletAccount.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.Wallets.SQLite -{ - sealed class UserWalletAccount : WalletAccount - { - public KeyPair Key; - - public override bool HasKey => Key != null; - - public UserWalletAccount(UInt160 scriptHash, ProtocolSettings settings) - : base(scriptHash, settings) - { - } - - public override KeyPair GetKey() - { - return Key; - } - } -} diff --git a/src/neo/Wallets/SQLite/VerificationContract.cs b/src/neo/Wallets/SQLite/VerificationContract.cs deleted file mode 100644 index 6030ac08b6..0000000000 --- a/src/neo/Wallets/SQLite/VerificationContract.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.IO; -using Neo.SmartContract; -using System; -using System.IO; -using System.Linq; - -namespace Neo.Wallets.SQLite -{ - class VerificationContract : SmartContract.Contract, IEquatable, ISerializable - { - public int Size => ParameterList.GetVarSize() + Script.GetVarSize(); - - public void Deserialize(BinaryReader reader) - { - ParameterList = reader.ReadVarBytes().Select(p => - { - var ret = (ContractParameterType)p; - if (!Enum.IsDefined(typeof(ContractParameterType), ret)) - throw new FormatException(); - return ret; - }).ToArray(); - Script = reader.ReadVarBytes(); - } - - public bool Equals(VerificationContract other) - { - if (ReferenceEquals(this, other)) return true; - if (other is null) return false; - return ScriptHash.Equals(other.ScriptHash); - } - - public override bool Equals(object obj) - { - return Equals(obj as VerificationContract); - } - - public override int GetHashCode() - { - return ScriptHash.GetHashCode(); - } - - public void Serialize(BinaryWriter writer) - { - writer.WriteVarBytes(ParameterList.Select(p => (byte)p).ToArray()); - writer.WriteVarBytes(Script); - } - } -} diff --git a/src/neo/Wallets/SQLite/WalletDataContext.cs b/src/neo/Wallets/SQLite/WalletDataContext.cs deleted file mode 100644 index afd7805018..0000000000 --- a/src/neo/Wallets/SQLite/WalletDataContext.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (C) 2015-2021 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project 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.Data.Sqlite; -using Microsoft.EntityFrameworkCore; - -namespace Neo.Wallets.SQLite -{ - internal class WalletDataContext : DbContext - { - public DbSet Accounts { get; set; } - public DbSet
Addresses { get; set; } - public DbSet Contracts { get; set; } - public DbSet Keys { get; set; } - - private readonly string filename; - - public WalletDataContext(string filename) - { - this.filename = filename; - } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - base.OnConfiguring(optionsBuilder); - SqliteConnectionStringBuilder sb = new() - { - DataSource = filename - }; - optionsBuilder.UseSqlite(sb.ToString()); - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - modelBuilder.Entity().ToTable(nameof(Account)); - modelBuilder.Entity().HasKey(p => p.PublicKeyHash); - modelBuilder.Entity().Property(p => p.Nep2key).HasColumnType("VarChar").HasMaxLength(byte.MaxValue).IsRequired(); - modelBuilder.Entity().Property(p => p.PublicKeyHash).HasColumnType("Binary").HasMaxLength(20).IsRequired(); - modelBuilder.Entity
().ToTable(nameof(Address)); - modelBuilder.Entity
().HasKey(p => p.ScriptHash); - modelBuilder.Entity
().Property(p => p.ScriptHash).HasColumnType("Binary").HasMaxLength(20).IsRequired(); - modelBuilder.Entity().ToTable(nameof(Contract)); - modelBuilder.Entity().HasKey(p => p.ScriptHash); - modelBuilder.Entity().HasIndex(p => p.PublicKeyHash); - modelBuilder.Entity().HasOne(p => p.Account).WithMany().HasForeignKey(p => p.PublicKeyHash).OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity().HasOne(p => p.Address).WithMany().HasForeignKey(p => p.ScriptHash).OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity().Property(p => p.RawData).HasColumnType("VarBinary").IsRequired(); - modelBuilder.Entity().Property(p => p.ScriptHash).HasColumnType("Binary").HasMaxLength(20).IsRequired(); - modelBuilder.Entity().Property(p => p.PublicKeyHash).HasColumnType("Binary").HasMaxLength(20).IsRequired(); - modelBuilder.Entity().ToTable(nameof(Key)); - modelBuilder.Entity().HasKey(p => p.Name); - modelBuilder.Entity().Property(p => p.Name).HasColumnType("VarChar").HasMaxLength(20).IsRequired(); - modelBuilder.Entity().Property(p => p.Value).HasColumnType("VarBinary").IsRequired(); - } - } -} diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj deleted file mode 100644 index 129d2add66..0000000000 --- a/src/neo/neo.csproj +++ /dev/null @@ -1,35 +0,0 @@ - - - - 2015-2021 The Neo Project - Neo - 3.1.0 - The Neo Project - net6.0 - true - Neo - Neo - NEO;AntShares;Blockchain;Smart Contract - https://github.com/neo-project/neo - MIT - git - https://github.com/neo-project/neo.git - Neo - The Neo Project - Neo - true - - - - - - - - - - - - - - - diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props new file mode 100644 index 0000000000..83fe17740a --- /dev/null +++ b/tests/Directory.Build.props @@ -0,0 +1,17 @@ + + + + + net7.0 + false + + + + + + + + + + + diff --git a/tests/Neo.ConsoleService.Tests/CommandTokenTest.cs b/tests/Neo.ConsoleService.Tests/CommandTokenTest.cs new file mode 100644 index 0000000000..b5f43f9d04 --- /dev/null +++ b/tests/Neo.ConsoleService.Tests/CommandTokenTest.cs @@ -0,0 +1,103 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// CommandTokenTest.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.VisualStudio.TestTools.UnitTesting; +using System.Linq; + +namespace Neo.ConsoleService.Tests +{ + [TestClass] + public class CommandTokenTest + { + [TestMethod] + public void Test1() + { + var cmd = " "; + var args = CommandToken.Parse(cmd).ToArray(); + + AreEqual(args, new CommandSpaceToken(0, 1)); + Assert.AreEqual(cmd, CommandToken.ToString(args)); + } + + [TestMethod] + public void Test2() + { + var cmd = "show state"; + var args = CommandToken.Parse(cmd).ToArray(); + + AreEqual(args, new CommandStringToken(0, "show"), new CommandSpaceToken(4, 2), new CommandStringToken(6, "state")); + Assert.AreEqual(cmd, CommandToken.ToString(args)); + } + + [TestMethod] + public void Test3() + { + var cmd = "show \"hello world\""; + var args = CommandToken.Parse(cmd).ToArray(); + + AreEqual(args, + new CommandStringToken(0, "show"), + new CommandSpaceToken(4, 1), + new CommandQuoteToken(5, '"'), + new CommandStringToken(6, "hello world"), + new CommandQuoteToken(17, '"') + ); + Assert.AreEqual(cmd, CommandToken.ToString(args)); + } + + [TestMethod] + public void Test4() + { + var cmd = "show \"'\""; + var args = CommandToken.Parse(cmd).ToArray(); + + AreEqual(args, + new CommandStringToken(0, "show"), + new CommandSpaceToken(4, 1), + new CommandQuoteToken(5, '"'), + new CommandStringToken(6, "'"), + new CommandQuoteToken(7, '"') + ); + Assert.AreEqual(cmd, CommandToken.ToString(args)); + } + + [TestMethod] + public void Test5() + { + var cmd = "show \"123\\\"456\""; + var args = CommandToken.Parse(cmd).ToArray(); + + AreEqual(args, + new CommandStringToken(0, "show"), + new CommandSpaceToken(4, 1), + new CommandQuoteToken(5, '"'), + new CommandStringToken(6, "123\\\"456"), + new CommandQuoteToken(14, '"') + ); + Assert.AreEqual(cmd, CommandToken.ToString(args)); + } + + private void AreEqual(CommandToken[] args, params CommandToken[] compare) + { + Assert.AreEqual(compare.Length, args.Length); + + for (int x = 0; x < args.Length; x++) + { + var a = args[x]; + var b = compare[x]; + + Assert.AreEqual(a.Type, b.Type); + Assert.AreEqual(a.Value, b.Value); + Assert.AreEqual(a.Offset, b.Offset); + } + } + } +} diff --git a/tests/Neo.ConsoleService.Tests/Neo.ConsoleService.Tests.csproj b/tests/Neo.ConsoleService.Tests/Neo.ConsoleService.Tests.csproj new file mode 100644 index 0000000000..0b12c476c8 --- /dev/null +++ b/tests/Neo.ConsoleService.Tests/Neo.ConsoleService.Tests.csproj @@ -0,0 +1,13 @@ + + + + net7.0 + neo_cli.Tests + false + + + + + + + diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/Neo.Cryptography.BLS12_381.Tests.csproj b/tests/Neo.Cryptography.BLS12_381.Tests/Neo.Cryptography.BLS12_381.Tests.csproj new file mode 100644 index 0000000000..a56621448c --- /dev/null +++ b/tests/Neo.Cryptography.BLS12_381.Tests/Neo.Cryptography.BLS12_381.Tests.csproj @@ -0,0 +1,14 @@ + + + + net7.0 + enable + enable + false + + + + + + + diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp.cs b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp.cs new file mode 100644 index 0000000000..bc9cd480f2 --- /dev/null +++ b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp.cs @@ -0,0 +1,353 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Fp.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.Collections; +using System.Runtime.InteropServices; +using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; + +namespace Neo.Cryptography.BLS12_381.Tests; + +[TestClass] +public class UT_Fp +{ + [TestMethod] + public void TestSize() + { + Assert.AreEqual(Fp.Size, Marshal.SizeOf()); + } + + [TestMethod] + public void TestEquality() + { + static bool IsEqual(in Fp a, in Fp b) + { + bool eq = StructuralComparisons.StructuralEqualityComparer.Equals(a, b); + bool ct_eq = a == b; + Assert.AreEqual(eq, ct_eq); + return eq; + } + + Assert.IsTrue(IsEqual(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); + + Assert.IsFalse(IsEqual(Fp.FromRawUnchecked(new ulong[] { 7, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); + Assert.IsFalse(IsEqual(Fp.FromRawUnchecked(new ulong[] { 1, 7, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); + Assert.IsFalse(IsEqual(Fp.FromRawUnchecked(new ulong[] { 1, 2, 7, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); + Assert.IsFalse(IsEqual(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 7, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); + Assert.IsFalse(IsEqual(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 7, 6 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); + Assert.IsFalse(IsEqual(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 7 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); + } + + [TestMethod] + public void TestConditionalSelection() + { + Fp a = Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }); + Fp b = Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 }); + + Assert.AreEqual(a, ConditionalSelect(in a, in b, false)); + Assert.AreEqual(b, ConditionalSelect(in a, in b, true)); + } + + [TestMethod] + public void TestSquaring() + { + Fp a = Fp.FromRawUnchecked(new ulong[] + { + 0xd215_d276_8e83_191b, + 0x5085_d80f_8fb2_8261, + 0xce9a_032d_df39_3a56, + 0x3e9c_4fff_2ca0_c4bb, + 0x6436_b6f7_f4d9_5dfb, + 0x1060_6628_ad4a_4d90 + }); + Fp b = Fp.FromRawUnchecked(new ulong[] + { + 0x33d9_c42a_3cb3_e235, + 0xdad1_1a09_4c4c_d455, + 0xa2f1_44bd_729a_aeba, + 0xd415_0932_be9f_feac, + 0xe27b_c7c4_7d44_ee50, + 0x14b6_a78d_3ec7_a560 + }); + + Assert.AreEqual(b, a.Square()); + } + + [TestMethod] + public void TestMultiplication() + { + Fp a = Fp.FromRawUnchecked(new ulong[] + { + 0x0397_a383_2017_0cd4, + 0x734c_1b2c_9e76_1d30, + 0x5ed2_55ad_9a48_beb5, + 0x095a_3c6b_22a7_fcfc, + 0x2294_ce75_d4e2_6a27, + 0x1333_8bd8_7001_1ebb + }); + Fp b = Fp.FromRawUnchecked(new ulong[] + { + 0xb9c3_c7c5_b119_6af7, + 0x2580_e208_6ce3_35c1, + 0xf49a_ed3d_8a57_ef42, + 0x41f2_81e4_9846_e878, + 0xe076_2346_c384_52ce, + 0x0652_e893_26e5_7dc0 + }); + Fp c = Fp.FromRawUnchecked(new ulong[] + { + 0xf96e_f3d7_11ab_5355, + 0xe8d4_59ea_00f1_48dd, + 0x53f7_354a_5f00_fa78, + 0x9e34_a4f3_125c_5f83, + 0x3fbe_0c47_ca74_c19e, + 0x01b0_6a8b_bd4a_dfe4 + }); + + Assert.AreEqual(c, a * b); + } + + [TestMethod] + public void TestAddition() + { + Fp a = Fp.FromRawUnchecked(new ulong[] + { + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b + }); + Fp b = Fp.FromRawUnchecked(new ulong[] + { + 0x9fd2_8773_3d23_dda0, + 0xb16b_f2af_738b_3554, + 0x3e57_a75b_d3cc_6d1d, + 0x900b_c0bd_627f_d6d6, + 0xd319_a080_efb2_45fe, + 0x15fd_caa4_e4bb_2091 + }); + Fp c = Fp.FromRawUnchecked(new ulong[] + { + 0x3934_42cc_b58b_b327, + 0x1092_685f_3bd5_47e3, + 0x3382_252c_ab6a_c4c9, + 0xf946_94cb_7688_7f55, + 0x4b21_5e90_93a5_e071, + 0x0d56_e30f_34f5_f853 + }); + + Assert.AreEqual(c, a + b); + } + + [TestMethod] + public void TestSubtraction() + { + Fp a = Fp.FromRawUnchecked(new ulong[] + { + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b + }); + Fp b = Fp.FromRawUnchecked(new ulong[] + { + 0x9fd2_8773_3d23_dda0, + 0xb16b_f2af_738b_3554, + 0x3e57_a75b_d3cc_6d1d, + 0x900b_c0bd_627f_d6d6, + 0xd319_a080_efb2_45fe, + 0x15fd_caa4_e4bb_2091 + }); + Fp c = Fp.FromRawUnchecked(new ulong[] + { + 0x6d8d_33e6_3b43_4d3d, + 0xeb12_82fd_b766_dd39, + 0x8534_7bb6_f133_d6d5, + 0xa21d_aa5a_9892_f727, + 0x3b25_6cfb_3ad8_ae23, + 0x155d_7199_de7f_8464 + }); + + Assert.AreEqual(c, a - b); + } + + [TestMethod] + public void TestNegation() + { + Fp a = Fp.FromRawUnchecked(new ulong[] + { + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b + }); + Fp b = Fp.FromRawUnchecked(new ulong[] + { + 0x669e_44a6_8798_2a79, + 0xa0d9_8a50_37b5_ed71, + 0x0ad5_822f_2861_a854, + 0x96c5_2bf1_ebf7_5781, + 0x87f8_41f0_5c0c_658c, + 0x08a6_e795_afc5_283e + }); + + Assert.AreEqual(b, -a); + } + + [TestMethod] + public void TestToString() + { + Fp a = Fp.FromRawUnchecked(new ulong[] + { + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b + }); + + Assert.AreEqual("0x104bf052ad3bc99bcb176c24a06a6c3aad4eaf2308fc4d282e106c84a757d061052630515305e59bdddf8111bfdeb704", a.ToString()); + } + + [TestMethod] + public void TestConstructor() + { + Fp a = Fp.FromRawUnchecked(new ulong[] + { + 0xdc90_6d9b_e3f9_5dc8, + 0x8755_caf7_4596_91a1, + 0xcff1_a7f4_e958_3ab3, + 0x9b43_821f_849e_2284, + 0xf575_54f3_a297_4f3f, + 0x085d_bea8_4ed4_7f79 + }); + + for (int i = 0; i < 100; i++) + { + a = a.Square(); + byte[] tmp = a.ToArray(); + Fp b = Fp.FromBytes(tmp); + + Assert.AreEqual(b, a); + } + + Assert.AreEqual(-Fp.One, Fp.FromBytes(new byte[] + { + 26, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, + 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, + 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 + })); + + Assert.ThrowsException(() => Fp.FromBytes(new byte[] + { + 27, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, + 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, + 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 + })); + + Assert.ThrowsException(() => Fp.FromBytes(Enumerable.Repeat(0xff, 48).ToArray())); + } + + [TestMethod] + public void TestSqrt() + { + // a = 4 + Fp a = Fp.FromRawUnchecked(new ulong[] + { + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e + }); + + // b = 2 + Fp b = Fp.FromRawUnchecked(new ulong[] + { + 0x3213_0000_0006_554f, + 0xb93c_0018_d6c4_0005, + 0x5760_5e0d_b0dd_bb51, + 0x8b25_6521_ed1f_9bcb, + 0x6cf2_8d79_0162_2c03, + 0x11eb_ab9d_bb81_e28c + }); + + // sqrt(4) = -2 + Assert.AreEqual(b, -a.Sqrt()); + } + + [TestMethod] + public void TestInversion() + { + Fp a = Fp.FromRawUnchecked(new ulong[] + { + 0x43b4_3a50_78ac_2076, + 0x1ce0_7630_46f8_962b, + 0x724a_5276_486d_735c, + 0x6f05_c2a6_282d_48fd, + 0x2095_bd5b_b4ca_9331, + 0x03b3_5b38_94b0_f7da + }); + Fp b = Fp.FromRawUnchecked(new ulong[] + { + 0x69ec_d704_0952_148f, + 0x985c_cc20_2219_0f55, + 0xe19b_ba36_a9ad_2f41, + 0x19bb_16c9_5219_dbd8, + 0x14dc_acfd_fb47_8693, + 0x115f_f58a_fff9_a8e1 + }); + + Assert.AreEqual(b, a.Invert()); + Assert.ThrowsException(() => Fp.Zero.Invert()); + } + + [TestMethod] + public void TestLexicographicLargest() + { + Assert.IsFalse(Fp.Zero.LexicographicallyLargest()); + Assert.IsFalse(Fp.One.LexicographicallyLargest()); + Assert.IsFalse(Fp.FromRawUnchecked(new ulong[] + { + 0xa1fa_ffff_fffe_5557, + 0x995b_fff9_76a3_fffe, + 0x03f4_1d24_d174_ceb4, + 0xf654_7998_c199_5dbd, + 0x778a_468f_507a_6034, + 0x0205_5993_1f7f_8103 + }).LexicographicallyLargest()); + Assert.IsTrue(Fp.FromRawUnchecked(new ulong[] + { + 0x1804_0000_0001_5554, + 0x8550_0005_3ab0_0001, + 0x633c_b57c_253c_276f, + 0x6e22_d1ec_31eb_b502, + 0xd391_6126_f2d1_4ca2, + 0x17fb_b857_1a00_6596 + }).LexicographicallyLargest()); + Assert.IsTrue(Fp.FromRawUnchecked(new ulong[] + { + 0x43f5_ffff_fffc_aaae, + 0x32b7_fff2_ed47_fffd, + 0x07e8_3a49_a2e9_9d69, + 0xeca8_f331_8332_bb7a, + 0xef14_8d1e_a0f4_c069, + 0x040a_b326_3eff_0206 + }).LexicographicallyLargest()); + } +} diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp12.cs b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp12.cs new file mode 100644 index 0000000000..238a20c6c6 --- /dev/null +++ b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp12.cs @@ -0,0 +1,347 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Fp12.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.Cryptography.BLS12_381.Tests; + +[TestClass] +public class UT_Fp12 +{ + [TestMethod] + public void TestArithmetic() + { + var a = new Fp12(new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d + }), Fp.FromRawUnchecked(new ulong[] + { + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040 + }))), new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d + }), Fp.FromRawUnchecked(new ulong[] + { + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040 + })))); + + var b = new Fp12(new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d272_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d + }), Fp.FromRawUnchecked(new ulong[] + { + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e348 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040 + }))), new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd2_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d + }), Fp.FromRawUnchecked(new ulong[] + { + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a117_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040 + })))); + + var c = new Fp12(new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x47f9_cb98_71b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d + }), Fp.FromRawUnchecked(new ulong[] + { + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0x7791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_133c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_40e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_1744_c040 + }))), new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d + }), Fp.FromRawUnchecked(new ulong[] + { + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d3_c010_e60f, + 0x0acd_b8e1_58bf_e3c8 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_1040 + })))); + + // because a and b and c are similar to each other and + // I was lazy, this is just some arbitrary way to make + // them a little more different + a = a.Square().Invert().Square() + c; + b = b.Square().Invert().Square() + a; + c = c.Square().Invert().Square() + b; + + Assert.AreEqual(a * a, a.Square()); + Assert.AreEqual(b * b, b.Square()); + Assert.AreEqual(c * c, c.Square()); + + Assert.AreEqual((a + b) * c.Square(), (c * c * a) + (c * c * b)); + + Assert.AreEqual(a.Invert() * b.Invert(), (a * b).Invert()); + Assert.AreEqual(Fp12.One, a.Invert() * a); + + Assert.AreNotEqual(a, a.FrobeniusMap()); + Assert.AreEqual( + a, + a.FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + ); + } +} diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp2.cs b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp2.cs new file mode 100644 index 0000000000..8e8781b834 --- /dev/null +++ b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp2.cs @@ -0,0 +1,493 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Fp2.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.Collections; +using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; + +namespace Neo.Cryptography.BLS12_381.Tests; + +[TestClass] +public class UT_Fp2 +{ + [TestMethod] + public void TestConditionalSelection() + { + var a = new Fp2( + Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), + Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 }) + ); + var b = new Fp2( + Fp.FromRawUnchecked(new ulong[] { 13, 14, 15, 16, 17, 18 }), + Fp.FromRawUnchecked(new ulong[] { 19, 20, 21, 22, 23, 24 }) + ); + + Assert.AreEqual(a, ConditionalSelect(in a, in b, false)); + Assert.AreEqual(b, ConditionalSelect(in a, in b, true)); + } + + [TestMethod] + public void TestEquality() + { + static bool IsEqual(in Fp2 a, in Fp2 b) + { + var eq = StructuralComparisons.StructuralEqualityComparer.Equals(a, b); + var ct_eq = a == b; + Assert.AreEqual(eq, ct_eq); + return eq; + } + + Assert.IsTrue(IsEqual( + new Fp2(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 })), + new Fp2(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 })) + )); + + Assert.IsFalse(IsEqual( + new Fp2(Fp.FromRawUnchecked(new ulong[] { 2, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 })), + new Fp2(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 })) + )); + + Assert.IsFalse(IsEqual( + new Fp2(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 2, 8, 9, 10, 11, 12 })), + new Fp2(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 })) + )); + } + + [TestMethod] + public void TestSquaring() + { + var a = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + })); + var b = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + }), Fp.FromRawUnchecked(new ulong[] + { + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + })); + + Assert.AreEqual(a.Square(), b); + } + + [TestMethod] + public void TestMultiplication() + { + var a = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + })); + var b = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + }), Fp.FromRawUnchecked(new ulong[] + { + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + })); + var c = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xf597_483e_27b4_e0f7, + 0x610f_badf_811d_ae5f, + 0x8432_af91_7714_327a, + 0x6a9a_9603_cf88_f09e, + 0xf05a_7bf8_bad0_eb01, + 0x0954_9131_c003_ffae, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x963b_02d0_f93d_37cd, + 0xc95c_e1cd_b30a_73d4, + 0x3087_25fa_3126_f9b8, + 0x56da_3c16_7fab_0d50, + 0x6b50_86b5_f4b6_d6af, + 0x09c3_9f06_2f18_e9f2, + })); + + Assert.AreEqual(c, a * b); + } + + [TestMethod] + public void TestAddition() + { + var a = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + })); + var b = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + }), Fp.FromRawUnchecked(new ulong[] + { + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + })); + var c = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x6b82_a9a7_08c1_32d2, + 0x476b_1da3_39ba_5ba4, + 0x848c_0e62_4b91_cd87, + 0x11f9_5955_295a_99ec, + 0xf337_6fce_2255_9f06, + 0x0c3f_e3fa_ce8c_8f43, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x6f99_2c12_73ab_5bc5, + 0x3355_1366_17a1_df33, + 0x8b0e_f74c_0aed_aff9, + 0x062f_9246_8ad2_ca12, + 0xe146_9770_738f_d584, + 0x12c3_c3dd_84bc_a26d, + })); + + Assert.AreEqual(a + b, c); + } + + [TestMethod] + public void TestSubtraction() + { + var a = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + })); + var b = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + }), Fp.FromRawUnchecked(new ulong[] + { + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + })); + var c = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xe1c0_86bb_bf1b_5981, + 0x4faf_c3a9_aa70_5d7e, + 0x2734_b5c1_0bb7_e726, + 0xb2bd_7776_af03_7a3e, + 0x1b89_5fb3_98a8_4164, + 0x1730_4aef_6f11_3cec, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x74c3_1c79_9519_1204, + 0x3271_aa54_79fd_ad2b, + 0xc9b4_7157_4915_a30f, + 0x65e4_0313_ec44_b8be, + 0x7487_b238_5b70_67cb, + 0x0952_3b26_d0ad_19a4, + })); + + Assert.AreEqual(c, a - b); + } + + [TestMethod] + public void TestNegation() + { + var a = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + })); + var b = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xf05c_e7ce_9c11_39d7, + 0x6274_8f57_97e8_a36d, + 0xc4e8_d9df_c664_96df, + 0xb457_88e1_8118_9209, + 0x6949_13d0_8772_930d, + 0x1549_836a_3770_f3cf, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x24d0_5bb9_fb9d_491c, + 0xfb1e_a120_c12e_39d0, + 0x7067_879f_c807_c7b1, + 0x60a9_269a_31bb_dab6, + 0x45c2_56bc_fd71_649b, + 0x18f6_9b5d_2b8a_fbde, + })); + + Assert.AreEqual(b, -a); + } + + [TestMethod] + public void TestSqrt() + { + // a = 1488924004771393321054797166853618474668089414631333405711627789629391903630694737978065425271543178763948256226639*u + 784063022264861764559335808165825052288770346101304131934508881646553551234697082295473567906267937225174620141295 + var a = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x2bee_d146_27d7_f9e9, + 0xb661_4e06_660e_5dce, + 0x06c4_cc7c_2f91_d42c, + 0x996d_7847_4b7a_63cc, + 0xebae_bc4c_820d_574e, + 0x1886_5e12_d93f_d845, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x7d82_8664_baf4_f566, + 0xd17e_6639_96ec_7339, + 0x679e_ad55_cb40_78d0, + 0xfe3b_2260_e001_ec28, + 0x3059_93d0_43d9_1b68, + 0x0626_f03c_0489_b72d, + })); + + Assert.AreEqual(a, a.Sqrt().Square()); + + // b = 5, which is a generator of the p - 1 order + // multiplicative subgroup + var b = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x6631_0000_0010_5545, + 0x2114_0040_0eec_000d, + 0x3fa7_af30_c820_e316, + 0xc52a_8b8d_6387_695d, + 0x9fb4_e61d_1e83_eac5, + 0x005c_b922_afe8_4dc7, + }), Fp.Zero); + + Assert.AreEqual(b, b.Sqrt().Square()); + + // c = 25, which is a generator of the (p - 1) / 2 order + // multiplicative subgroup + var c = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x44f6_0000_0051_ffae, + 0x86b8_0141_9948_0043, + 0xd715_9952_f1f3_794a, + 0x755d_6e3d_fe1f_fc12, + 0xd36c_d6db_5547_e905, + 0x02f8_c8ec_bf18_67bb, + }), Fp.Zero); + + Assert.AreEqual(c, c.Sqrt().Square()); + + // 2155129644831861015726826462986972654175647013268275306775721078997042729172900466542651176384766902407257452753362*u + 2796889544896299244102912275102369318775038861758288697415827248356648685135290329705805931514906495247464901062529 + // is nonsquare. + Assert.ThrowsException(() => + new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xc5fa_1bc8_fd00_d7f6, + 0x3830_ca45_4606_003b, + 0x2b28_7f11_04b1_02da, + 0xa7fb_30f2_8230_f23e, + 0x339c_db9e_e953_dbf0, + 0x0d78_ec51_d989_fc57, + }), Fp.FromRawUnchecked(new ulong[]{ + 0x27ec_4898_cf87_f613, + 0x9de1_394e_1abb_05a5, + 0x0947_f85d_c170_fc14, + 0x586f_bc69_6b61_14b7, + 0x2b34_75a4_077d_7169, + 0x13e1_c895_cc4b_6c22, + })).Sqrt()); + } + + [TestMethod] + public void TestInversion() + { + var a = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + }), Fp.FromRawUnchecked(new ulong[] + { + 0xd328_e37c_c2f5_8d41, + 0x948d_f085_8a60_5869, + 0x6032_f9d5_6f93_a573, + 0x2be4_83ef_3fff_dc87, + 0x30ef_61f8_8f48_3c2a, + 0x1333_f55a_3572_5be0, + })); + + var b = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x0581_a133_3d4f_48a6, + 0x5824_2f6e_f074_8500, + 0x0292_c955_349e_6da5, + 0xba37_721d_dd95_fcd0, + 0x70d1_6790_3aa5_dfc5, + 0x1189_5e11_8b58_a9d5, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x0eda_09d2_d7a8_5d17, + 0x8808_e137_a7d1_a2cf, + 0x43ae_2625_c1ff_21db, + 0xf85a_c9fd_f7a7_4c64, + 0x8fcc_dda5_b8da_9738, + 0x08e8_4f0c_b32c_d17d, + })); + Assert.AreEqual(b, a.Invert()); + + Assert.ThrowsException(() => Fp2.Zero.Invert()); + } + + [TestMethod] + public void TestLexicographicLargest() + { + Assert.IsFalse(Fp2.Zero.LexicographicallyLargest()); + Assert.IsFalse(Fp2.One.LexicographicallyLargest()); + Assert.IsTrue(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + }), Fp.FromRawUnchecked(new ulong[] + { + 0xd328_e37c_c2f5_8d41, + 0x948d_f085_8a60_5869, + 0x6032_f9d5_6f93_a573, + 0x2be4_83ef_3fff_dc87, + 0x30ef_61f8_8f48_3c2a, + 0x1333_f55a_3572_5be0, + })).LexicographicallyLargest()); + Assert.IsFalse(new Fp2(-Fp.FromRawUnchecked(new ulong[] + { + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + }), -Fp.FromRawUnchecked(new ulong[] + { + 0xd328_e37c_c2f5_8d41, + 0x948d_f085_8a60_5869, + 0x6032_f9d5_6f93_a573, + 0x2be4_83ef_3fff_dc87, + 0x30ef_61f8_8f48_3c2a, + 0x1333_f55a_3572_5be0, + })).LexicographicallyLargest()); + Assert.IsFalse(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + }), Fp.Zero).LexicographicallyLargest()); + Assert.IsTrue(new Fp2(-Fp.FromRawUnchecked(new ulong[] + { + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + }), Fp.Zero).LexicographicallyLargest()); + } +} diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp6.cs b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp6.cs new file mode 100644 index 0000000000..6fcc63fc97 --- /dev/null +++ b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp6.cs @@ -0,0 +1,179 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Fp6.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.Cryptography.BLS12_381.Tests; + +[TestClass] +public class UT_Fp6 +{ + [TestMethod] + public void TestArithmetic() + { + var a = new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d + }), c1: Fp.FromRawUnchecked(new ulong[] + { + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1 + })), c1: new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8 + }), c1: Fp.FromRawUnchecked(new ulong[] + { + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf + })), c2: new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6 + }), c1: Fp.FromRawUnchecked(new ulong[] + { + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040 + }))); + + var b = new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xf120_cb98_b16f_d84b, + 0x5fb5_10cf_f3de_1d61, + 0x0f21_a5d0_69d8_c251, + 0xaa1f_d62f_34f2_839a, + 0x5a13_3515_7f89_913f, + 0x14a3_fe32_9643_c247 + }), c1: Fp.FromRawUnchecked(new ulong[] + { + 0x3516_cb98_b16c_82f9, + 0x926d_10c2_e126_1d5f, + 0x1709_e01a_0cc2_5fba, + 0x96c8_c960_b825_3f14, + 0x4927_c234_207e_51a9, + 0x18ae_b158_d542_c44e + })), c1: new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xbf0d_cb98_b169_82fc, + 0xa679_10b7_1d1a_1d5c, + 0xb7c1_47c2_b8fb_06ff, + 0x1efa_710d_47d2_e7ce, + 0xed20_a79c_7e27_653c, + 0x02b8_5294_dac1_dfba + }), c1: Fp.FromRawUnchecked(new ulong[] + { + 0x9d52_cb98_b180_82e5, + 0x621d_1111_5176_1d6f, + 0xe798_8260_3b48_af43, + 0x0ad3_1637_a4f4_da37, + 0xaeac_737c_5ac1_cf2e, + 0x006e_7e73_5b48_b824 + })), c2: new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xe148_cb98_b17d_2d93, + 0x94d5_1104_3ebe_1d6c, + 0xef80_bca9_de32_4cac, + 0xf77c_0969_2827_95b1, + 0x9dc1_009a_fbb6_8f97, + 0x0479_3199_9a47_ba2b + }), c1: Fp.FromRawUnchecked(new ulong[] + { + 0x253e_cb98_b179_d841, + 0xc78d_10f7_2c06_1d6a, + 0xf768_f6f3_811b_ea15, + 0xe424_fc9a_ab5a_512b, + 0x8cd5_8db9_9cab_5001, + 0x0883_e4bf_d946_bc32 + }))); + + var c = new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x6934_cb98_b176_82ef, + 0xfa45_10ea_194e_1d67, + 0xff51_313d_2405_877e, + 0xd0cd_efcc_2e8d_0ca5, + 0x7bea_1ad8_3da0_106b, + 0x0c8e_97e6_1845_be39 + }), c1: Fp.FromRawUnchecked(new ulong[] + { + 0x4779_cb98_b18d_82d8, + 0xb5e9_1144_4daa_1d7a, + 0x2f28_6bda_a653_2fc2, + 0xbca6_94f6_8bae_ff0f, + 0x3d75_e6b8_1a3a_7a5d, + 0x0a44_c3c4_98cc_96a3 + })), c1: new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x8b6f_cb98_b18a_2d86, + 0xe8a1_1137_3af2_1d77, + 0x3710_a624_493c_cd2b, + 0xa94f_8828_0ee1_ba89, + 0x2c8a_73d6_bb2f_3ac7, + 0x0e4f_76ea_d7cb_98aa + }), c1: Fp.FromRawUnchecked(new ulong[] + { + 0xcf65_cb98_b186_d834, + 0x1b59_112a_283a_1d74, + 0x3ef8_e06d_ec26_6a95, + 0x95f8_7b59_9214_7603, + 0x1b9f_00f5_5c23_fb31, + 0x125a_2a11_16ca_9ab1 + })), c2: new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x135b_cb98_b183_82e2, + 0x4e11_111d_1582_1d72, + 0x46e1_1ab7_8f10_07fe, + 0x82a1_6e8b_1547_317d, + 0x0ab3_8e13_fd18_bb9b, + 0x1664_dd37_55c9_9cb8 + }), c1: Fp.FromRawUnchecked(new ulong[] + { + 0xce65_cb98_b131_8334, + 0xc759_0fdb_7c3a_1d2e, + 0x6fcb_8164_9d1c_8eb3, + 0x0d44_004d_1727_356a, + 0x3746_b738_a7d0_d296, + 0x136c_144a_96b1_34fc + }))); + + Assert.AreEqual(a * a, a.Square()); + Assert.AreEqual(b * b, b.Square()); + Assert.AreEqual(c * c, c.Square()); + + Assert.AreEqual((a + b) * c.Square(), (c * c * a) + (c * c * b)); + + Assert.AreEqual(a.Invert() * b.Invert(), (a * b).Invert()); + Assert.AreEqual(Fp6.One, a.Invert() * a); + } +} diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/UT_G1.cs b/tests/Neo.Cryptography.BLS12_381.Tests/UT_G1.cs new file mode 100644 index 0000000000..ab1a598b3c --- /dev/null +++ b/tests/Neo.Cryptography.BLS12_381.Tests/UT_G1.cs @@ -0,0 +1,603 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_G1.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 static Neo.Cryptography.BLS12_381.Constants; +using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; +using static Neo.Cryptography.BLS12_381.G1Constants; + +namespace Neo.Cryptography.BLS12_381.Tests; + +[TestClass] +public class UT_G1 +{ + [TestMethod] + public void TestBeta() + { + Assert.AreEqual(Fp.FromBytes(new byte[] + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x19, 0x67, 0x2f, 0xdf, 0x76, + 0xce, 0x51, 0xba, 0x69, 0xc6, 0x07, 0x6a, 0x0f, 0x77, 0xea, 0xdd, 0xb3, 0xa9, 0x3b, + 0xe6, 0xf8, 0x96, 0x88, 0xde, 0x17, 0xd8, 0x13, 0x62, 0x0a, 0x00, 0x02, 0x2e, 0x01, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe + }), BETA); + Assert.AreNotEqual(Fp.One, BETA); + Assert.AreNotEqual(Fp.One, BETA * BETA); + Assert.AreEqual(Fp.One, BETA * BETA * BETA); + } + + [TestMethod] + public void TestIsOnCurve() + { + Assert.IsTrue(G1Affine.Identity.IsOnCurve); + Assert.IsTrue(G1Affine.Generator.IsOnCurve); + Assert.IsTrue(G1Projective.Identity.IsOnCurve); + Assert.IsTrue(G1Projective.Generator.IsOnCurve); + + Fp z = Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }); + + var gen = G1Affine.Generator; + G1Projective test = new(gen.X * z, gen.Y * z, in z); + + Assert.IsTrue(test.IsOnCurve); + + test = new(in z, in test.Y, in test.Z); + Assert.IsFalse(test.IsOnCurve); + } + + [TestMethod] + public void TestAffinePointEquality() + { + var a = G1Affine.Generator; + var b = G1Affine.Identity; + + Assert.AreEqual(a, a); + Assert.AreEqual(b, b); + Assert.AreNotEqual(a, b); + } + + [TestMethod] + public void TestProjectivePointEquality() + { + var a = G1Projective.Generator; + var b = G1Projective.Identity; + + Assert.AreEqual(a, a); + Assert.AreEqual(b, b); + Assert.AreNotEqual(a, b); + + Fp z = Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }); + + G1Projective c = new(a.X * z, a.Y * z, in z); + Assert.IsTrue(c.IsOnCurve); + + Assert.AreEqual(a, c); + Assert.AreNotEqual(b, c); + + c = new(in c.X, -c.Y, in c.Z); + Assert.IsTrue(c.IsOnCurve); + + Assert.AreNotEqual(a, c); + Assert.AreNotEqual(b, c); + + c = new(in z, -c.Y, in c.Z); + Assert.IsFalse(c.IsOnCurve); + Assert.AreNotEqual(a, b); + Assert.AreNotEqual(a, c); + Assert.AreNotEqual(b, c); + } + + [TestMethod] + public void TestConditionallySelectAffine() + { + var a = G1Affine.Generator; + var b = G1Affine.Identity; + + Assert.AreEqual(a, ConditionalSelect(in a, in b, false)); + Assert.AreEqual(b, ConditionalSelect(in a, in b, true)); + } + + [TestMethod] + public void TestConditionallySelectProjective() + { + var a = G1Projective.Generator; + var b = G1Projective.Identity; + + Assert.AreEqual(a, ConditionalSelect(in a, in b, false)); + Assert.AreEqual(b, ConditionalSelect(in a, in b, true)); + } + + [TestMethod] + public void TestProjectiveToAffine() + { + var a = G1Projective.Generator; + var b = G1Projective.Identity; + + Assert.IsTrue(new G1Affine(a).IsOnCurve); + Assert.IsFalse(new G1Affine(a).IsIdentity); + Assert.IsTrue(new G1Affine(b).IsOnCurve); + Assert.IsTrue(new G1Affine(b).IsIdentity); + + Fp z = Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }); + + G1Projective c = new(a.X * z, a.Y * z, in z); + + Assert.AreEqual(G1Affine.Generator, new G1Affine(c)); + } + + [TestMethod] + public void TestAffineToProjective() + { + var a = G1Affine.Generator; + var b = G1Affine.Identity; + + Assert.IsTrue(new G1Projective(a).IsOnCurve); + Assert.IsFalse(new G1Projective(a).IsIdentity); + Assert.IsTrue(new G1Projective(b).IsOnCurve); + Assert.IsTrue(new G1Projective(b).IsIdentity); + } + + [TestMethod] + public void TestDoubling() + { + { + var tmp = G1Projective.Identity.Double(); + Assert.IsTrue(tmp.IsIdentity); + Assert.IsTrue(tmp.IsOnCurve); + } + { + var tmp = G1Projective.Generator.Double(); + Assert.IsFalse(tmp.IsIdentity); + Assert.IsTrue(tmp.IsOnCurve); + + Assert.AreEqual(new G1Affine(Fp.FromRawUnchecked(new ulong[] + { + 0x53e9_78ce_58a9_ba3c, + 0x3ea0_583c_4f3d_65f9, + 0x4d20_bb47_f001_2960, + 0xa54c_664a_e5b2_b5d9, + 0x26b5_52a3_9d7e_b21f, + 0x0008_895d_26e6_8785 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x7011_0b32_9829_3940, + 0xda33_c539_3f1f_6afc, + 0xb86e_dfd1_6a5a_a785, + 0xaec6_d1c9_e7b1_c895, + 0x25cf_c2b5_22d1_1720, + 0x0636_1c83_f8d0_9b15 + })), new G1Affine(tmp)); + } + } + + [TestMethod] + public void TestProjectiveAddition() + { + { + var a = G1Projective.Identity; + var b = G1Projective.Identity; + var c = a + b; + Assert.IsTrue(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + } + { + var a = G1Projective.Identity; + var b = G1Projective.Generator; + + Fp z = Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }); + + b = new(b.X * z, b.Y * z, in z); + var c = a + b; + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.AreEqual(G1Projective.Generator, c); + } + { + var a = G1Projective.Identity; + var b = G1Projective.Generator; + + Fp z = Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }); + + b = new(b.X * z, b.Y * z, in z); + var c = b + a; + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.AreEqual(G1Projective.Generator, c); + } + { + var a = G1Projective.Generator.Double().Double(); // 4P + var b = G1Projective.Generator.Double(); // 2P + var c = a + b; + + var d = G1Projective.Generator; + for (int i = 0; i < 5; i++) + { + d += G1Projective.Generator; + } + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.IsFalse(d.IsIdentity); + Assert.IsTrue(d.IsOnCurve); + Assert.AreEqual(c, d); + } + { + Fp beta = Fp.FromRawUnchecked(new ulong[] + { + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741 + }); + beta = beta.Square(); + var a = G1Projective.Generator.Double().Double(); + var b = new G1Projective(a.X * beta, -a.Y, in a.Z); + Assert.IsTrue(a.IsOnCurve); + Assert.IsTrue(b.IsOnCurve); + + var c = a + b; + Assert.AreEqual(new G1Affine(new G1Projective(Fp.FromRawUnchecked(new ulong[] + { + 0x29e1_e987_ef68_f2d0, + 0xc5f3_ec53_1db0_3233, + 0xacd6_c4b6_ca19_730f, + 0x18ad_9e82_7bc2_bab7, + 0x46e3_b2c5_785c_c7a9, + 0x07e5_71d4_2d22_ddd6 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x94d1_17a7_e5a5_39e7, + 0x8e17_ef67_3d4b_5d22, + 0x9d74_6aaf_508a_33ea, + 0x8c6d_883d_2516_c9a2, + 0x0bc3_b8d5_fb04_47f7, + 0x07bf_a4c7_210f_4f44, + }), in Fp.One)), new G1Affine(c)); + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + } + } + + [TestMethod] + public void TestMixedAddition() + { + { + var a = G1Affine.Identity; + var b = G1Projective.Identity; + var c = a + b; + Assert.IsTrue(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + } + { + var a = G1Affine.Identity; + var b = G1Projective.Generator; + + Fp z = Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }); + + b = new(b.X * z, b.Y * z, in z); + var c = a + b; + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.AreEqual(G1Projective.Generator, c); + } + { + var a = G1Affine.Identity; + var b = G1Projective.Generator; + + Fp z = Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }); + + b = new(b.X * z, b.Y * z, in z); + var c = b + a; + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.AreEqual(G1Projective.Generator, c); + } + { + var a = G1Projective.Generator.Double().Double(); // 4P + var b = G1Projective.Generator.Double(); // 2P + var c = a + b; + + var d = G1Projective.Generator; + for (int i = 0; i < 5; i++) + { + d += G1Affine.Generator; + } + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.IsFalse(d.IsIdentity); + Assert.IsTrue(d.IsOnCurve); + Assert.AreEqual(c, d); + } + { + Fp beta = Fp.FromRawUnchecked(new ulong[] + { + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741 + }); + beta = beta.Square(); + var a = G1Projective.Generator.Double().Double(); + var b = new G1Projective(a.X * beta, -a.Y, in a.Z); + var a2 = new G1Affine(a); + Assert.IsTrue(a2.IsOnCurve); + Assert.IsTrue(b.IsOnCurve); + + var c = a2 + b; + Assert.AreEqual(new G1Affine(new G1Projective(Fp.FromRawUnchecked(new ulong[] + { + 0x29e1_e987_ef68_f2d0, + 0xc5f3_ec53_1db0_3233, + 0xacd6_c4b6_ca19_730f, + 0x18ad_9e82_7bc2_bab7, + 0x46e3_b2c5_785c_c7a9, + 0x07e5_71d4_2d22_ddd6 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x94d1_17a7_e5a5_39e7, + 0x8e17_ef67_3d4b_5d22, + 0x9d74_6aaf_508a_33ea, + 0x8c6d_883d_2516_c9a2, + 0x0bc3_b8d5_fb04_47f7, + 0x07bf_a4c7_210f_4f44 + }), Fp.One)), new G1Affine(c)); + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + } + } + + [TestMethod] + public void TestProjectiveNegationAndSubtraction() + { + var a = G1Projective.Generator.Double(); + Assert.AreEqual(a + (-a), G1Projective.Identity); + Assert.AreEqual(a + (-a), a - a); + } + + [TestMethod] + public void TestAffineNegationAndSubtraction() + { + var a = G1Affine.Generator; + Assert.AreEqual(G1Projective.Identity, new G1Projective(a) + (-a)); + Assert.AreEqual(new G1Projective(a) + (-a), new G1Projective(a) - a); + } + + [TestMethod] + public void TestProjectiveScalarMultiplication() + { + var g = G1Projective.Generator; + var a = Scalar.FromRaw(new ulong[] + { + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2 + }); + var b = Scalar.FromRaw(new ulong[] + { + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20 + }); + var c = a * b; + + Assert.AreEqual(g * a * b, g * c); + } + + [TestMethod] + public void TestAffineScalarMultiplication() + { + var g = G1Affine.Generator; + var a = Scalar.FromRaw(new ulong[] + { + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2 + }); + var b = Scalar.FromRaw(new ulong[] + { + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20 + }); + var c = a * b; + + Assert.AreEqual(new G1Affine(g * a) * b, g * c); + } + + [TestMethod] + public void TestIsTorsionFree() + { + var a = new G1Affine(Fp.FromRawUnchecked(new ulong[] + { + 0x0aba_f895_b97e_43c8, + 0xba4c_6432_eb9b_61b0, + 0x1250_6f52_adfe_307f, + 0x7502_8c34_3933_6b72, + 0x8474_4f05_b8e9_bd71, + 0x113d_554f_b095_54f7 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x73e9_0e88_f5cf_01c0, + 0x3700_7b65_dd31_97e2, + 0x5cf9_a199_2f0d_7c78, + 0x4f83_c10b_9eb3_330d, + 0xf6a6_3f6f_07f6_0961, + 0x0c53_b5b9_7e63_4df3 + })); + Assert.IsFalse(a.IsTorsionFree); + + Assert.IsTrue(G1Affine.Identity.IsTorsionFree); + Assert.IsTrue(G1Affine.Generator.IsTorsionFree); + } + + [TestMethod] + public void TestMulByX() + { + // multiplying by `x` a point in G1 is the same as multiplying by + // the equivalent scalar. + var generator = G1Projective.Generator; + var x = BLS_X_IS_NEGATIVE ? -new Scalar(BLS_X) : new Scalar(BLS_X); + Assert.AreEqual(generator.MulByX(), generator * x); + + var point = G1Projective.Generator * new Scalar(42); + Assert.AreEqual(point.MulByX(), point * x); + } + + [TestMethod] + public void TestClearCofactor() + { + // the generator (and the identity) are always on the curve, + // even after clearing the cofactor + var generator = G1Projective.Generator; + Assert.IsTrue(generator.ClearCofactor().IsOnCurve); + var id = G1Projective.Identity; + Assert.IsTrue(id.ClearCofactor().IsOnCurve); + + var z = Fp.FromRawUnchecked(new ulong[] + { + 0x3d2d1c670671394e, + 0x0ee3a800a2f7c1ca, + 0x270f4f21da2e5050, + 0xe02840a53f1be768, + 0x55debeb597512690, + 0x08bd25353dc8f791 + }); + + var point = new G1Projective(Fp.FromRawUnchecked(new ulong[] + { + 0x48af5ff540c817f0, + 0xd73893acaf379d5a, + 0xe6c43584e18e023c, + 0x1eda39c30f188b3e, + 0xf618c6d3ccc0f8d8, + 0x0073542cd671e16c + }) * z, Fp.FromRawUnchecked(new ulong[] + { + 0x57bf8be79461d0ba, + 0xfc61459cee3547c3, + 0x0d23567df1ef147b, + 0x0ee187bcce1d9b64, + 0xb0c8cfbe9dc8fdc1, + 0x1328661767ef368b + }), z.Square() * z); + + Assert.IsTrue(point.IsOnCurve); + Assert.IsFalse(new G1Affine(point).IsTorsionFree); + var cleared_point = point.ClearCofactor(); + Assert.IsTrue(cleared_point.IsOnCurve); + Assert.IsTrue(new G1Affine(cleared_point).IsTorsionFree); + + // in BLS12-381 the cofactor in G1 can be + // cleared multiplying by (1-x) + var h_eff = new Scalar(1) + new Scalar(BLS_X); + Assert.AreEqual(point.ClearCofactor(), point * h_eff); + } + + [TestMethod] + public void TestBatchNormalize() + { + var a = G1Projective.Generator.Double(); + var b = a.Double(); + var c = b.Double(); + + foreach (bool a_identity in new[] { false, true }) + { + foreach (bool b_identity in new[] { false, true }) + { + foreach (bool c_identity in new[] { false, true }) + { + var v = new[] { a, b, c }; + if (a_identity) + { + v[0] = G1Projective.Identity; + } + if (b_identity) + { + v[1] = G1Projective.Identity; + } + if (c_identity) + { + v[2] = G1Projective.Identity; + } + + var t = new G1Affine[3]; + var expected = new[] { new G1Affine(v[0]), new G1Affine(v[1]), new G1Affine(v[2]) }; + + G1Projective.BatchNormalize(v, t); + + CollectionAssert.AreEqual(expected, t); + } + } + } + } +} diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/UT_G2.cs b/tests/Neo.Cryptography.BLS12_381.Tests/UT_G2.cs new file mode 100644 index 0000000000..7b8cadb90e --- /dev/null +++ b/tests/Neo.Cryptography.BLS12_381.Tests/UT_G2.cs @@ -0,0 +1,823 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_G2.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 static Neo.Cryptography.BLS12_381.Constants; +using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; + +namespace Neo.Cryptography.BLS12_381.Tests; + +[TestClass] +public class UT_G2 +{ + [TestMethod] + public void TestIsOnCurve() + { + Assert.IsTrue(G2Affine.Identity.IsOnCurve); + Assert.IsTrue(G2Affine.Generator.IsOnCurve); + Assert.IsTrue(G2Projective.Identity.IsOnCurve); + Assert.IsTrue(G2Projective.Generator.IsOnCurve); + + var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }), Fp.FromRawUnchecked(new ulong[] + { + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844 + })); + + var gen = G2Affine.Generator; + var test = new G2Projective(gen.X * z, gen.Y * z, z); + + Assert.IsTrue(test.IsOnCurve); + + test = new(in z, in test.Y, in test.Z); + Assert.IsFalse(test.IsOnCurve); + } + + [TestMethod] + public void TestAffinePointEquality() + { + var a = G2Affine.Generator; + var b = G2Affine.Identity; + + Assert.AreEqual(a, a); + Assert.AreEqual(b, b); + Assert.AreNotEqual(a, b); + } + + [TestMethod] + public void TestProjectivePointEquality() + { + var a = G2Projective.Generator; + var b = G2Projective.Identity; + + Assert.AreEqual(a, a); + Assert.AreEqual(b, b); + Assert.AreNotEqual(a, b); + + var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }), Fp.FromRawUnchecked(new ulong[] + { + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844 + })); + + var c = new G2Projective(a.X * z, a.Y * z, in z); + Assert.IsTrue(c.IsOnCurve); + + Assert.AreEqual(a, c); + Assert.AreNotEqual(b, c); + + c = new(in c.X, -c.Y, in c.Z); + Assert.IsTrue(c.IsOnCurve); + + Assert.AreNotEqual(a, c); + Assert.AreNotEqual(b, c); + + c = new(in z, -c.Y, in c.Z); + Assert.IsFalse(c.IsOnCurve); + Assert.AreNotEqual(a, b); + Assert.AreNotEqual(a, c); + Assert.AreNotEqual(b, c); + } + + [TestMethod] + public void TestConditionallySelectAffine() + { + var a = G2Affine.Generator; + var b = G2Affine.Identity; + + Assert.AreEqual(a, ConditionalSelect(in a, in b, false)); + Assert.AreEqual(b, ConditionalSelect(in a, in b, true)); + } + + [TestMethod] + public void TestConditionallySelectProjective() + { + var a = G2Projective.Generator; + var b = G2Projective.Identity; + + Assert.AreEqual(a, ConditionalSelect(in a, in b, false)); + Assert.AreEqual(b, ConditionalSelect(in a, in b, true)); + } + + [TestMethod] + public void TestProjectiveToAffine() + { + var a = G2Projective.Generator; + var b = G2Projective.Identity; + + Assert.IsTrue(new G2Affine(a).IsOnCurve); + Assert.IsFalse(new G2Affine(a).IsIdentity); + Assert.IsTrue(new G2Affine(b).IsOnCurve); + Assert.IsTrue(new G2Affine(b).IsIdentity); + + var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }), Fp.FromRawUnchecked(new ulong[] + { + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844 + })); + + var c = new G2Projective(a.X * z, a.Y * z, in z); + + Assert.AreEqual(G2Affine.Generator, new G2Affine(c)); + } + + [TestMethod] + public void TestAffineToProjective() + { + var a = G2Affine.Generator; + var b = G2Affine.Identity; + + Assert.IsTrue(new G2Projective(a).IsOnCurve); + Assert.IsFalse(new G2Projective(a).IsIdentity); + Assert.IsTrue(new G2Projective(b).IsOnCurve); + Assert.IsTrue(new G2Projective(b).IsIdentity); + } + + [TestMethod] + public void TestDoubling() + { + { + var tmp = G2Projective.Identity.Double(); + Assert.IsTrue(tmp.IsIdentity); + Assert.IsTrue(tmp.IsOnCurve); + } + { + var tmp = G2Projective.Generator.Double(); + Assert.IsFalse(tmp.IsIdentity); + Assert.IsTrue(tmp.IsOnCurve); + + Assert.AreEqual(new G2Affine(tmp), new G2Affine(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xe9d9_e2da_9620_f98b, + 0x54f1_1993_46b9_7f36, + 0x3db3_b820_376b_ed27, + 0xcfdb_31c9_b0b6_4f4c, + 0x41d7_c127_8635_4493, + 0x0571_0794_c255_c064 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xd6c1_d3ca_6ea0_d06e, + 0xda0c_bd90_5595_489f, + 0x4f53_52d4_3479_221d, + 0x8ade_5d73_6f8c_97e0, + 0x48cc_8433_925e_f70e, + 0x08d7_ea71_ea91_ef81 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x15ba_26eb_4b0d_186f, + 0x0d08_6d64_b7e9_e01e, + 0xc8b8_48dd_652f_4c78, + 0xeecf_46a6_123b_ae4f, + 0x255e_8dd8_b6dc_812a, + 0x1641_42af_21dc_f93f + }), Fp.FromRawUnchecked(new ulong[] + { + 0xf9b4_a1a8_9598_4db4, + 0xd417_b114_cccf_f748, + 0x6856_301f_c89f_086e, + 0x41c7_7787_8931_e3da, + 0x3556_b155_066a_2105, + 0x00ac_f7d3_25cb_89cf + })))); + } + } + + [TestMethod] + public void TestProjectiveAddition() + { + { + var a = G2Projective.Identity; + var b = G2Projective.Identity; + var c = a + b; + Assert.IsTrue(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + } + { + var a = G2Projective.Identity; + var b = G2Projective.Generator; + { + var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }), Fp.FromRawUnchecked(new ulong[] + { + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844 + })); + + b = new G2Projective(b.X * z, b.Y * z, in z); + } + var c = a + b; + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.AreEqual(G2Projective.Generator, c); + } + { + var a = G2Projective.Identity; + var b = G2Projective.Generator; + { + var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }), Fp.FromRawUnchecked(new ulong[] + { + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844 + })); + + b = new G2Projective(b.X * z, b.Y * z, in z); + } + var c = b + a; + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.AreEqual(G2Projective.Generator, c); + } + { + var a = G2Projective.Generator.Double().Double(); // 4P + var b = G2Projective.Generator.Double(); // 2P + var c = a + b; + + var d = G2Projective.Generator; + for (int i = 0; i < 5; i++) + { + d += G2Projective.Generator; + } + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.IsFalse(d.IsIdentity); + Assert.IsTrue(d.IsOnCurve); + Assert.AreEqual(c, d); + } + + // Degenerate case + { + var beta = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741 + }), Fp.Zero); + beta = beta.Square(); + var a = G2Projective.Generator.Double().Double(); + var b = new G2Projective(a.X * beta, -a.Y, in a.Z); + Assert.IsTrue(a.IsOnCurve); + Assert.IsTrue(b.IsOnCurve); + + var c = a + b; + Assert.AreEqual( + new G2Affine(c), + new G2Affine(new G2Projective(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x705a_bc79_9ca7_73d3, + 0xfe13_2292_c1d4_bf08, + 0xf37e_ce3e_07b2_b466, + 0x887e_1c43_f447_e301, + 0x1e09_70d0_33bc_77e8, + 0x1985_c81e_20a6_93f2 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x1d79_b25d_b36a_b924, + 0x2394_8e4d_5296_39d3, + 0x471b_a7fb_0d00_6297, + 0x2c36_d4b4_465d_c4c0, + 0x82bb_c3cf_ec67_f538, + 0x051d_2728_b67b_f952 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x41b1_bbf6_576c_0abf, + 0xb6cc_9371_3f7a_0f9a, + 0x6b65_b43e_48f3_f01f, + 0xfb7a_4cfc_af81_be4f, + 0x3e32_dadc_6ec2_2cb6, + 0x0bb0_fc49_d798_07e3 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x7d13_9778_8f5f_2ddf, + 0xab29_0714_4ff0_d8e8, + 0x5b75_73e0_cdb9_1f92, + 0x4cb8_932d_d31d_af28, + 0x62bb_fac6_db05_2a54, + 0x11f9_5c16_d14c_3bbe + })), Fp2.One))); + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + } + } + + [TestMethod] + public void TestMixedAddition() + { + { + var a = G2Affine.Identity; + var b = G2Projective.Identity; + var c = a + b; + Assert.IsTrue(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + } + { + var a = G2Affine.Identity; + var b = G2Projective.Generator; + { + var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }), Fp.FromRawUnchecked(new ulong[] + { + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844 + })); + + b = new G2Projective(b.X * z, b.Y * z, in z); + } + var c = a + b; + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.AreEqual(G2Projective.Generator, c); + } + { + var a = G2Affine.Identity; + var b = G2Projective.Generator; + { + var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }), Fp.FromRawUnchecked(new ulong[] + { + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844 + })); + + b = new G2Projective(b.X * z, b.Y * z, in z); + } + var c = b + a; + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.AreEqual(G2Projective.Generator, c); + } + { + var a = G2Projective.Generator.Double().Double(); // 4P + var b = G2Projective.Generator.Double(); // 2P + var c = a + b; + + var d = G2Projective.Generator; + for (int i = 0; i < 5; i++) + { + d += G2Affine.Generator; + } + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.IsFalse(d.IsIdentity); + Assert.IsTrue(d.IsOnCurve); + Assert.AreEqual(c, d); + } + + // Degenerate case + { + var beta = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741 + }), Fp.Zero); + beta = beta.Square(); + var _a = G2Projective.Generator.Double().Double(); + var b = new G2Projective(_a.X * beta, -_a.Y, in _a.Z); + var a = new G2Affine(_a); + Assert.IsTrue((a.IsOnCurve)); + Assert.IsTrue((b.IsOnCurve)); + + var c = a + b; + Assert.AreEqual(new G2Affine(new G2Projective(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x705a_bc79_9ca7_73d3, + 0xfe13_2292_c1d4_bf08, + 0xf37e_ce3e_07b2_b466, + 0x887e_1c43_f447_e301, + 0x1e09_70d0_33bc_77e8, + 0x1985_c81e_20a6_93f2 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x1d79_b25d_b36a_b924, + 0x2394_8e4d_5296_39d3, + 0x471b_a7fb_0d00_6297, + 0x2c36_d4b4_465d_c4c0, + 0x82bb_c3cf_ec67_f538, + 0x051d_2728_b67b_f952 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x41b1_bbf6_576c_0abf, + 0xb6cc_9371_3f7a_0f9a, + 0x6b65_b43e_48f3_f01f, + 0xfb7a_4cfc_af81_be4f, + 0x3e32_dadc_6ec2_2cb6, + 0x0bb0_fc49_d798_07e3 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x7d13_9778_8f5f_2ddf, + 0xab29_0714_4ff0_d8e8, + 0x5b75_73e0_cdb9_1f92, + 0x4cb8_932d_d31d_af28, + 0x62bb_fac6_db05_2a54, + 0x11f9_5c16_d14c_3bbe + })), Fp2.One)), new G2Affine(c)); + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + } + } + + [TestMethod] + public void TestProjectiveNegationAndSubtraction() + { + var a = G2Projective.Generator.Double(); + Assert.AreEqual(G2Projective.Identity, a + (-a)); + Assert.AreEqual(a - a, a + (-a)); + } + + [TestMethod] + public void TestAffineNegationAndSubtraction() + { + var a = G2Affine.Generator; + Assert.AreEqual(G2Projective.Identity, new G2Projective(a) + (-a)); + Assert.AreEqual(new G2Projective(a) - a, new G2Projective(a) + (-a)); + } + + [TestMethod] + public void TestProjectiveScalarMultiplication() + { + var g = G2Projective.Generator; + var a = Scalar.FromRaw(new ulong[] + { + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2 + }); + var b = Scalar.FromRaw(new ulong[] + { + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20 + }); + var c = a * b; + + Assert.AreEqual(g * c, g * a * b); + } + + [TestMethod] + public void TestAffineScalarMultiplication() + { + var g = G2Affine.Generator; + var a = Scalar.FromRaw(new ulong[] + { + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2 + }); + var b = Scalar.FromRaw(new ulong[] + { + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20 + }); + var c = a * b; + + Assert.AreEqual(g * c, new G2Affine(g * a) * b); + } + + [TestMethod] + public void TestIsTorsionFree() + { + var a = new G2Affine(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x89f5_50c8_13db_6431, + 0xa50b_e8c4_56cd_8a1a, + 0xa45b_3741_14ca_e851, + 0xbb61_90f5_bf7f_ff63, + 0x970c_a02c_3ba8_0bc7, + 0x02b8_5d24_e840_fbac + }), + Fp.FromRawUnchecked(new ulong[] + { + 0x6888_bc53_d707_16dc, + 0x3dea_6b41_1768_2d70, + 0xd8f5_f930_500c_a354, + 0x6b5e_cb65_56f5_c155, + 0xc96b_ef04_3477_8ab0, + 0x0508_1505_5150_06ad + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x3cf1_ea0d_434b_0f40, + 0x1a0d_c610_e603_e333, + 0x7f89_9561_60c7_2fa0, + 0x25ee_03de_cf64_31c5, + 0xeee8_e206_ec0f_e137, + 0x0975_92b2_26df_ef28 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x71e8_bb5f_2924_7367, + 0xa5fe_049e_2118_31ce, + 0x0ce6_b354_502a_3896, + 0x93b0_1200_0997_314e, + 0x6759_f3b6_aa5b_42ac, + 0x1569_44c4_dfe9_2bbb + }))); + Assert.IsFalse(a.IsTorsionFree); + + Assert.IsTrue(G2Affine.Identity.IsTorsionFree); + Assert.IsTrue(G2Affine.Generator.IsTorsionFree); + } + + [TestMethod] + public void TestMulByX() + { + // multiplying by `x` a point in G2 is the same as multiplying by + // the equivalent scalar. + var generator = G2Projective.Generator; + var x = BLS_X_IS_NEGATIVE ? -new Scalar(BLS_X) : new Scalar(BLS_X); + Assert.AreEqual(generator * x, generator.MulByX()); + + var point = G2Projective.Generator * new Scalar(42); + Assert.AreEqual(point * x, point.MulByX()); + } + + [TestMethod] + public void TestPsi() + { + var generator = G2Projective.Generator; + + var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x0ef2ddffab187c0a, + 0x2424522b7d5ecbfc, + 0xc6f341a3398054f4, + 0x5523ddf409502df0, + 0xd55c0b5a88e0dd97, + 0x066428d704923e52 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x538bbe0c95b4878d, + 0xad04a50379522881, + 0x6d5c05bf5c12fb64, + 0x4ce4a069a2d34787, + 0x59ea6c8d0dffaeaf, + 0x0d42a083a75bd6f3 + })); + + // `point` is a random point in the curve + var point = new G2Projective(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xee4c8cb7c047eaf2, + 0x44ca22eee036b604, + 0x33b3affb2aefe101, + 0x15d3e45bbafaeb02, + 0x7bfc2154cd7419a4, + 0x0a2d0c2b756e5edc + }), Fp.FromRawUnchecked(new ulong[] + { + 0xfc224361029a8777, + 0x4cbf2baab8740924, + 0xc5008c6ec6592c89, + 0xecc2c57b472a9c2d, + 0x8613eafd9d81ffb1, + 0x10fe54daa2d3d495 + })) * z, new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x7de7edc43953b75c, + 0x58be1d2de35e87dc, + 0x5731d30b0e337b40, + 0xbe93b60cfeaae4c9, + 0x8b22c203764bedca, + 0x01616c8d1033b771 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xea126fe476b5733b, + 0x85cee68b5dae1652, + 0x98247779f7272b04, + 0xa649c8b468c6e808, + 0xb5b9a62dff0c4e45, + 0x1555b67fc7bbe73d + })), z.Square() * z); + Assert.IsTrue(point.IsOnCurve); + + // psi2(P) = psi(psi(P)) + Assert.AreEqual(generator.Psi2(), generator.Psi().Psi()); + Assert.AreEqual(point.Psi2(), point.Psi().Psi()); + // psi(P) is a morphism + Assert.AreEqual(generator.Double().Psi(), generator.Psi().Double()); + Assert.AreEqual(point.Psi() + generator.Psi(), (point + generator).Psi()); + // psi(P) behaves in the same way on the same projective point + var normalized_points = new G2Affine[1]; + G2Projective.BatchNormalize(new[] { point }, normalized_points); + var normalized_point = new G2Projective(normalized_points[0]); + Assert.AreEqual(point.Psi(), normalized_point.Psi()); + Assert.AreEqual(point.Psi2(), normalized_point.Psi2()); + } + + [TestMethod] + public void TestClearCofactor() + { + var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x0ef2ddffab187c0a, + 0x2424522b7d5ecbfc, + 0xc6f341a3398054f4, + 0x5523ddf409502df0, + 0xd55c0b5a88e0dd97, + 0x066428d704923e52 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x538bbe0c95b4878d, + 0xad04a50379522881, + 0x6d5c05bf5c12fb64, + 0x4ce4a069a2d34787, + 0x59ea6c8d0dffaeaf, + 0x0d42a083a75bd6f3 + })); + + // `point` is a random point in the curve + var point = new G2Projective(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xee4c8cb7c047eaf2, + 0x44ca22eee036b604, + 0x33b3affb2aefe101, + 0x15d3e45bbafaeb02, + 0x7bfc2154cd7419a4, + 0x0a2d0c2b756e5edc + }), Fp.FromRawUnchecked(new ulong[] + { + 0xfc224361029a8777, + 0x4cbf2baab8740924, + 0xc5008c6ec6592c89, + 0xecc2c57b472a9c2d, + 0x8613eafd9d81ffb1, + 0x10fe54daa2d3d495 + })) * z, new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x7de7edc43953b75c, + 0x58be1d2de35e87dc, + 0x5731d30b0e337b40, + 0xbe93b60cfeaae4c9, + 0x8b22c203764bedca, + 0x01616c8d1033b771 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xea126fe476b5733b, + 0x85cee68b5dae1652, + 0x98247779f7272b04, + 0xa649c8b468c6e808, + 0xb5b9a62dff0c4e45, + 0x1555b67fc7bbe73d + })), z.Square() * z); + + Assert.IsTrue(point.IsOnCurve); + Assert.IsFalse(new G2Affine(point).IsTorsionFree); + var cleared_point = point.ClearCofactor(); + + Assert.IsTrue(cleared_point.IsOnCurve); + Assert.IsTrue(new G2Affine(cleared_point).IsTorsionFree); + + // the generator (and the identity) are always on the curve, + // even after clearing the cofactor + var generator = G2Projective.Generator; + Assert.IsTrue(generator.ClearCofactor().IsOnCurve); + var id = G2Projective.Identity; + Assert.IsTrue(id.ClearCofactor().IsOnCurve); + + // test the effect on q-torsion points multiplying by h_eff modulo |Scalar| + // h_eff % q = 0x2b116900400069009a40200040001ffff + byte[] h_eff_modq = + { + 0xff, 0xff, 0x01, 0x00, 0x04, 0x00, 0x02, 0xa4, 0x09, 0x90, 0x06, 0x00, 0x04, 0x90, 0x16, + 0xb1, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + }; + Assert.AreEqual(generator * h_eff_modq, generator.ClearCofactor()); + Assert.AreEqual(cleared_point * h_eff_modq, cleared_point.ClearCofactor()); + } + + [TestMethod] + public void TestBatchNormalize() + { + var a = G2Projective.Generator.Double(); + var b = a.Double(); + var c = b.Double(); + + foreach (bool a_identity in new[] { false, true }) + { + foreach (bool b_identity in new[] { false, true }) + { + foreach (bool c_identity in new[] { false, true }) + { + var v = new[] { a, b, c }; + if (a_identity) + { + v[0] = G2Projective.Identity; + } + if (b_identity) + { + v[1] = G2Projective.Identity; + } + if (c_identity) + { + v[2] = G2Projective.Identity; + } + + var t = new G2Affine[3]; + var expected = new[] { new G2Affine(v[0]), new G2Affine(v[1]), new G2Affine(v[2]) }; + + G2Projective.BatchNormalize(v, t); + + CollectionAssert.AreEqual(t, expected); + } + } + } + } +} diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/UT_Pairings.cs b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Pairings.cs new file mode 100644 index 0000000000..9019477a8f --- /dev/null +++ b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Pairings.cs @@ -0,0 +1,69 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Pairings.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.Cryptography.BLS12_381.Tests; + +[TestClass] +public class UT_Pairings +{ + [TestMethod] + public void TestGtGenerator() + { + Assert.AreEqual( + Gt.Generator, + Bls12.Pairing(in G1Affine.Generator, in G2Affine.Generator) + ); + } + + [TestMethod] + public void TestBilinearity() + { + var a = Scalar.FromRaw(new ulong[] { 1, 2, 3, 4 }).Invert().Square(); + var b = Scalar.FromRaw(new ulong[] { 5, 6, 7, 8 }).Invert().Square(); + var c = a * b; + + var g = new G1Affine(G1Affine.Generator * a); + var h = new G2Affine(G2Affine.Generator * b); + var p = Bls12.Pairing(in g, in h); + + Assert.AreNotEqual(Gt.Identity, p); + + var expected = new G1Affine(G1Affine.Generator * c); + + Assert.AreEqual(p, Bls12.Pairing(in expected, in G2Affine.Generator)); + Assert.AreEqual( + p, + Bls12.Pairing(in G1Affine.Generator, in G2Affine.Generator) * c + ); + } + + [TestMethod] + public void TestUnitary() + { + var g = G1Affine.Generator; + var h = G2Affine.Generator; + var p = -Bls12.Pairing(in g, in h); + var q = Bls12.Pairing(in g, -h); + var r = Bls12.Pairing(-g, in h); + + Assert.AreEqual(p, q); + Assert.AreEqual(q, r); + } + + [TestMethod] + public void TestMillerLoopResultDefault() + { + Assert.AreEqual( + Gt.Identity, + new MillerLoopResult(Fp12.One).FinalExponentiation() + ); + } +} diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/UT_Scalar.cs b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Scalar.cs new file mode 100644 index 0000000000..03c1526a7b --- /dev/null +++ b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Scalar.cs @@ -0,0 +1,411 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Scalar.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 static Neo.Cryptography.BLS12_381.ScalarConstants; + +namespace Neo.Cryptography.BLS12_381.Tests; + +[TestClass] +public class UT_Scalar +{ + private static readonly Scalar LARGEST = new(new ulong[] + { + 0xffff_ffff_0000_0000, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48 + }); + + [TestMethod] + public void TestInv() + { + // Compute -(q^{-1} mod 2^64) mod 2^64 by exponentiating + // by totient(2**64) - 1 + + var inv = 1ul; + for (int i = 0; i < 63; i++) + { + inv = unchecked(inv * inv); + inv = unchecked(inv * MODULUS_LIMBS_64[0]); + } + inv = unchecked(~inv + 1); + + Assert.AreEqual(INV, inv); + } + + [TestMethod] + public void TestToString() + { + Assert.AreEqual("0x0000000000000000000000000000000000000000000000000000000000000000", Scalar.Zero.ToString()); + Assert.AreEqual("0x0000000000000000000000000000000000000000000000000000000000000001", Scalar.One.ToString()); + Assert.AreEqual("0x1824b159acc5056f998c4fefecbc4ff55884b7fa0003480200000001fffffffe", R2.ToString()); + } + + [TestMethod] + public void TestEquality() + { + Assert.AreEqual(Scalar.Zero, Scalar.Zero); + Assert.AreEqual(Scalar.One, Scalar.One); + Assert.AreEqual(R2, R2); + + Assert.AreNotEqual(Scalar.Zero, Scalar.One); + Assert.AreNotEqual(Scalar.One, R2); + } + + [TestMethod] + public void TestToBytes() + { + CollectionAssert.AreEqual(new byte[] + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + }, Scalar.Zero.ToArray()); + + CollectionAssert.AreEqual(new byte[] + { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + }, Scalar.One.ToArray()); + + CollectionAssert.AreEqual(new byte[] + { + 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, + 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 + }, R2.ToArray()); + + CollectionAssert.AreEqual(new byte[] + { + 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + }, (-Scalar.One).ToArray()); + } + + [TestMethod] + public void TestFromBytes() + { + Assert.AreEqual(Scalar.Zero, Scalar.FromBytes(new byte[] + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + })); + + Assert.AreEqual(Scalar.One, Scalar.FromBytes(new byte[] + { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + })); + + Assert.AreEqual(R2, Scalar.FromBytes(new byte[] + { + 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, + 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 + })); + + // -1 should work + Scalar.FromBytes(new byte[] + { + 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + }); + + // modulus is invalid + Assert.ThrowsException(() => Scalar.FromBytes(new byte[] + { + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + })); + + // Anything larger than the modulus is invalid + Assert.ThrowsException(() => Scalar.FromBytes(new byte[] + { + 2, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + })); + Assert.ThrowsException(() => Scalar.FromBytes(new byte[] + { + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 58, 51, 72, 125, 157, 41, 83, 167, 237, 115 + })); + Assert.ThrowsException(() => Scalar.FromBytes(new byte[] + { + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 116 + })); + } + + [TestMethod] + public void TestFromBytesWideR2() + { + Assert.AreEqual(R2, Scalar.FromBytesWide(new byte[] + { + 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, + 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + })); + } + + [TestMethod] + public void TestFromBytesWideNegativeOne() + { + Assert.AreEqual(-Scalar.One, Scalar.FromBytesWide(new byte[] + { + 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + })); + } + + [TestMethod] + public void TestFromBytesWideMaximum() + { + Assert.AreEqual(new Scalar(new ulong[] + { + 0xc62c_1805_439b_73b1, + 0xc2b9_551e_8ced_218e, + 0xda44_ec81_daf9_a422, + 0x5605_aa60_1c16_2e79 + }), Scalar.FromBytesWide(Enumerable.Repeat(0xff, 64).ToArray())); + } + + [TestMethod] + public void TestZero() + { + Assert.AreEqual(Scalar.Zero, -Scalar.Zero); + Assert.AreEqual(Scalar.Zero, Scalar.Zero + Scalar.Zero); + Assert.AreEqual(Scalar.Zero, Scalar.Zero - Scalar.Zero); + Assert.AreEqual(Scalar.Zero, Scalar.Zero * Scalar.Zero); + } + + [TestMethod] + public void TestAddition() + { + var tmp = LARGEST; + tmp += LARGEST; + + Assert.AreEqual(new Scalar(new ulong[] + { + 0xffff_fffe_ffff_ffff, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48 + }), tmp); + + tmp = LARGEST; + tmp += new Scalar(new ulong[] { 1, 0, 0, 0 }); + + Assert.AreEqual(Scalar.Zero, tmp); + } + + [TestMethod] + public void TestNegation() + { + var tmp = -LARGEST; + + Assert.AreEqual(new Scalar(new ulong[] { 1, 0, 0, 0 }), tmp); + + tmp = -Scalar.Zero; + Assert.AreEqual(Scalar.Zero, tmp); + tmp = -new Scalar(new ulong[] { 1, 0, 0, 0 }); + Assert.AreEqual(LARGEST, tmp); + } + + [TestMethod] + public void TestSubtraction() + { + var tmp = LARGEST; + tmp -= LARGEST; + + Assert.AreEqual(Scalar.Zero, tmp); + + tmp = Scalar.Zero; + tmp -= LARGEST; + + var tmp2 = MODULUS; + tmp2 -= LARGEST; + + Assert.AreEqual(tmp, tmp2); + } + + [TestMethod] + public void TestMultiplication() + { + var cur = LARGEST; + + for (int i = 0; i < 100; i++) + { + var tmp = cur; + tmp *= cur; + + var tmp2 = Scalar.Zero; + foreach (bool b in cur + .ToArray() + .SelectMany(p => Enumerable.Range(0, 8).Select(q => ((p >> q) & 1) == 1)) + .Reverse()) + { + var tmp3 = tmp2; + tmp2 += tmp3; + + if (b) + { + tmp2 += cur; + } + } + + Assert.AreEqual(tmp, tmp2); + + cur += LARGEST; + } + } + + [TestMethod] + public void TestSquaring() + { + var cur = LARGEST; + + for (int i = 0; i < 100; i++) + { + var tmp = cur; + tmp = tmp.Square(); + + var tmp2 = Scalar.Zero; + foreach (bool b in cur + .ToArray() + .SelectMany(p => Enumerable.Range(0, 8).Select(q => ((p >> q) & 1) == 1)) + .Reverse()) + { + var tmp3 = tmp2; + tmp2 += tmp3; + + if (b) + { + tmp2 += cur; + } + } + + Assert.AreEqual(tmp, tmp2); + + cur += LARGEST; + } + } + + [TestMethod] + public void TestInversion() + { + Assert.ThrowsException(() => Scalar.Zero.Invert()); + Assert.AreEqual(Scalar.One, Scalar.One.Invert()); + Assert.AreEqual(-Scalar.One, (-Scalar.One).Invert()); + + var tmp = R2; + + for (int i = 0; i < 100; i++) + { + var tmp2 = tmp.Invert(); + tmp2 *= tmp; + + Assert.AreEqual(Scalar.One, tmp2); + + tmp += R2; + } + } + + [TestMethod] + public void TestInvertIsPow() + { + ulong[] q_minus_2 = + { + 0xffff_fffe_ffff_ffff, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48 + }; + + var r1 = R; + var r2 = R; + var r3 = R; + + for (int i = 0; i < 100; i++) + { + r1 = r1.Invert(); + r2 = r2.PowVartime(q_minus_2); + r3 = r3.Pow(q_minus_2); + + Assert.AreEqual(r1, r2); + Assert.AreEqual(r2, r3); + // Add R so we check something different next time around + r1 += R; + r2 = r1; + r3 = r1; + } + } + + [TestMethod] + public void TestSqrt() + { + Assert.AreEqual(Scalar.Zero.Sqrt(), Scalar.Zero); + + var square = new Scalar(new ulong[] + { + 0x46cd_85a5_f273_077e, + 0x1d30_c47d_d68f_c735, + 0x77f6_56f6_0bec_a0eb, + 0x494a_a01b_df32_468d + }); + + var none_count = 0; + + for (int i = 0; i < 100; i++) + { + Scalar square_root; + try + { + square_root = square.Sqrt(); + Assert.AreEqual(square, square_root * square_root); + } + catch (ArithmeticException) + { + none_count++; + } + square -= Scalar.One; + } + + Assert.AreEqual(49, none_count); + } + + [TestMethod] + public void TestFromRaw() + { + Assert.AreEqual(Scalar.FromRaw(new ulong[] + { + 0x0001_ffff_fffd, + 0x5884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff5, + 0x1824_b159_acc5_056f + }), Scalar.FromRaw(Enumerable.Repeat(0xffff_ffff_ffff_ffff, 4).ToArray())); + + Assert.AreEqual(Scalar.Zero, Scalar.FromRaw(MODULUS_LIMBS_64)); + + Assert.AreEqual(R, Scalar.FromRaw(new ulong[] { 1, 0, 0, 0 })); + } + + [TestMethod] + public void TestDouble() + { + var a = Scalar.FromRaw(new ulong[] + { + 0x1fff_3231_233f_fffd, + 0x4884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff3, + 0x1824_b159_acc5_0562 + }); + + Assert.AreEqual(a + a, a.Double()); + } +} diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/Usings.cs b/tests/Neo.Cryptography.BLS12_381.Tests/Usings.cs new file mode 100644 index 0000000000..d35553e8ae --- /dev/null +++ b/tests/Neo.Cryptography.BLS12_381.Tests/Usings.cs @@ -0,0 +1,12 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Usings.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. + +global using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/tests/Neo.Json.UnitTests/Neo.Json.UnitTests.csproj b/tests/Neo.Json.UnitTests/Neo.Json.UnitTests.csproj new file mode 100644 index 0000000000..f8dec3e7d5 --- /dev/null +++ b/tests/Neo.Json.UnitTests/Neo.Json.UnitTests.csproj @@ -0,0 +1,13 @@ + + + + net7.0 + enable + false + + + + + + + diff --git a/tests/neo.UnitTests/IO/Json/UT_JArray.cs b/tests/Neo.Json.UnitTests/UT_JArray.cs similarity index 94% rename from tests/neo.UnitTests/IO/Json/UT_JArray.cs rename to tests/Neo.Json.UnitTests/UT_JArray.cs index 913fb9b10b..40388d11fc 100644 --- a/tests/neo.UnitTests/IO/Json/UT_JArray.cs +++ b/tests/Neo.Json.UnitTests/UT_JArray.cs @@ -1,11 +1,17 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using System; +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_JArray.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.Collections; -using System.Linq; -namespace Neo.UnitTests.IO.Json +namespace Neo.Json.UnitTests { enum Foo { diff --git a/tests/Neo.Json.UnitTests/UT_JBoolean.cs b/tests/Neo.Json.UnitTests/UT_JBoolean.cs new file mode 100644 index 0000000000..3ab19bd1b3 --- /dev/null +++ b/tests/Neo.Json.UnitTests/UT_JBoolean.cs @@ -0,0 +1,43 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_JBoolean.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.Json.UnitTests +{ + [TestClass] + public class UT_JBoolean + { + private JBoolean jFalse; + private JBoolean jTrue; + + [TestInitialize] + public void SetUp() + { + jFalse = new JBoolean(); + jTrue = new JBoolean(true); + } + + [TestMethod] + public void TestAsNumber() + { + jFalse.AsNumber().Should().Be(0); + jTrue.AsNumber().Should().Be(1); + } + + [TestMethod] + public void TestEqual() + { + Assert.IsTrue(jTrue.Equals(new JBoolean(true))); + Assert.IsTrue(jTrue == new JBoolean(true)); + Assert.IsTrue(jFalse.Equals(new JBoolean())); + Assert.IsTrue(jFalse == new JBoolean()); + } + } +} diff --git a/tests/Neo.Json.UnitTests/UT_JNumber.cs b/tests/Neo.Json.UnitTests/UT_JNumber.cs new file mode 100644 index 0000000000..6eb0598fd3 --- /dev/null +++ b/tests/Neo.Json.UnitTests/UT_JNumber.cs @@ -0,0 +1,77 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_JNumber.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.Json.UnitTests +{ + enum Woo + { + Tom, + Jerry, + James + } + + [TestClass] + public class UT_JNumber + { + private JNumber maxInt; + private JNumber minInt; + private JNumber zero; + + [TestInitialize] + public void SetUp() + { + maxInt = new JNumber(JNumber.MAX_SAFE_INTEGER); + minInt = new JNumber(JNumber.MIN_SAFE_INTEGER); + zero = new JNumber(); + } + + [TestMethod] + public void TestAsBoolean() + { + maxInt.AsBoolean().Should().BeTrue(); + zero.AsBoolean().Should().BeFalse(); + } + + [TestMethod] + public void TestAsString() + { + Action action1 = () => new JNumber(double.PositiveInfinity).AsString(); + action1.Should().Throw(); + + Action action2 = () => new JNumber(double.NegativeInfinity).AsString(); + action2.Should().Throw(); + + Action action3 = () => new JNumber(double.NaN).AsString(); + action3.Should().Throw(); + } + + [TestMethod] + public void TestGetEnum() + { + zero.GetEnum().Should().Be(Woo.Tom); + new JNumber(1).GetEnum().Should().Be(Woo.Jerry); + new JNumber(2).GetEnum().Should().Be(Woo.James); + new JNumber(3).AsEnum().Should().Be(Woo.Tom); + Action action = () => new JNumber(3).GetEnum(); + action.Should().Throw(); + } + + [TestMethod] + public void TestEqual() + { + Assert.IsTrue(maxInt.Equals(JNumber.MAX_SAFE_INTEGER)); + Assert.IsTrue(maxInt == JNumber.MAX_SAFE_INTEGER); + Assert.IsTrue(minInt.Equals(JNumber.MIN_SAFE_INTEGER)); + Assert.IsTrue(minInt == JNumber.MIN_SAFE_INTEGER); + Assert.IsTrue(zero == new JNumber()); + } + } +} diff --git a/tests/neo.UnitTests/IO/Json/UT_JObject.cs b/tests/Neo.Json.UnitTests/UT_JObject.cs similarity index 74% rename from tests/neo.UnitTests/IO/Json/UT_JObject.cs rename to tests/Neo.Json.UnitTests/UT_JObject.cs index e2c394f5bb..e3660a7d3e 100644 --- a/tests/neo.UnitTests/IO/Json/UT_JObject.cs +++ b/tests/Neo.Json.UnitTests/UT_JObject.cs @@ -1,9 +1,15 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using System; +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_JObject.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.UnitTests.IO.Json +namespace Neo.Json.UnitTests { [TestClass] public class UT_JObject @@ -77,24 +83,25 @@ public void TestParse() } [TestMethod] - public void TestTryGetEnum() + public void TestGetEnum() { - alice.TryGetEnum().Should().Be(Woo.Tom); + alice.AsEnum().Should().Be(Woo.Tom); + + Action action = () => alice.GetEnum(); + action.Should().Throw(); } [TestMethod] public void TestOpImplicitEnum() { - var obj = new JObject(); - obj = Woo.Tom; + JToken obj = Woo.Tom; obj.AsString().Should().Be("Tom"); } [TestMethod] public void TestOpImplicitString() { - var obj = new JObject(); - obj = null; + JToken obj = null; obj.Should().BeNull(); obj = "{\"aaa\":\"111\"}"; @@ -104,17 +111,28 @@ public void TestOpImplicitString() [TestMethod] public void TestGetNull() { - JObject.Null.Should().BeNull(); + JToken.Null.Should().BeNull(); } [TestMethod] public void TestClone() { - var bobClone = bob.Clone(); + var bobClone = (JObject)bob.Clone(); bobClone.Should().NotBeSameAs(bob); foreach (var key in bobClone.Properties.Keys) { - bobClone[key].Should().BeEquivalentTo(bob[key]); + switch (bob[key]) + { + case JToken.Null: + bobClone[key].Should().BeNull(); + break; + case JObject obj: + ((JObject)bobClone[key]).Properties.Should().BeEquivalentTo(obj.Properties); + break; + default: + bobClone[key].Should().BeEquivalentTo(bob[key]); + break; + } } } } diff --git a/tests/neo.UnitTests/IO/Json/UT_JPath.cs b/tests/Neo.Json.UnitTests/UT_JPath.cs similarity index 89% rename from tests/neo.UnitTests/IO/Json/UT_JPath.cs rename to tests/Neo.Json.UnitTests/UT_JPath.cs index 64b5cfdceb..9d958b1cde 100644 --- a/tests/neo.UnitTests/IO/Json/UT_JPath.cs +++ b/tests/Neo.Json.UnitTests/UT_JPath.cs @@ -1,8 +1,15 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using System; +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_JPath.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.UnitTests.IO.Json +namespace Neo.Json.UnitTests { [TestClass] public class UT_JPath @@ -54,6 +61,13 @@ public class UT_JPath ["data"] = null, }; + [TestMethod] + public void TestOOM() + { + var filter = "$" + string.Concat(Enumerable.Repeat("[0" + string.Concat(Enumerable.Repeat(",0", 64)) + "]", 6)); + Assert.ThrowsException(() => JObject.Parse("[[[[[[{}]]]]]]")!.JsonPath(filter)); + } + [TestMethod] public void TestJsonPath() { diff --git a/tests/neo.UnitTests/IO/Json/UT_JString.cs b/tests/Neo.Json.UnitTests/UT_JString.cs similarity index 55% rename from tests/neo.UnitTests/IO/Json/UT_JString.cs rename to tests/Neo.Json.UnitTests/UT_JString.cs index 0b6df4c087..7e2bec9834 100644 --- a/tests/neo.UnitTests/IO/Json/UT_JString.cs +++ b/tests/Neo.Json.UnitTests/UT_JString.cs @@ -1,9 +1,15 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using Neo.SmartContract; -using System; +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_JString.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.UnitTests.IO +namespace Neo.Json.UnitTests { [TestClass] public class UT_JString @@ -43,15 +49,24 @@ public void TestAsNumber() } [TestMethod] - public void TestTryGetEnum() + public void TestGetEnum() { - JString s = new JString("Signature"); - ContractParameterType cp = s.TryGetEnum(ContractParameterType.Void, false); - Assert.AreEqual(ContractParameterType.Signature, cp); + JString s = "James"; + Woo woo = s.GetEnum(); + Assert.AreEqual(Woo.James, woo); - s = new JString(""); - cp = s.TryGetEnum(ContractParameterType.Void, false); - Assert.AreEqual(ContractParameterType.Void, cp); + s = ""; + woo = s.AsEnum(Woo.Jerry, false); + Assert.AreEqual(Woo.Jerry, woo); + } + [TestMethod] + public void TestEqual() + { + var str = "hello world"; + var jString = new JString(str); + Assert.IsTrue(jString.Equals(str)); + Assert.IsTrue(jString == str); + Assert.IsTrue(jString != "hello world2"); } } } diff --git a/tests/neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs b/tests/Neo.Json.UnitTests/UT_OrderedDictionary.cs similarity index 85% rename from tests/neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs rename to tests/Neo.Json.UnitTests/UT_OrderedDictionary.cs index 66b1dd0e7c..83a5c2c2f7 100644 --- a/tests/neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs +++ b/tests/Neo.Json.UnitTests/UT_OrderedDictionary.cs @@ -1,10 +1,17 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Caching; +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_OrderedDictionary.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.Collections; -using System.Collections.Generic; -namespace Neo.UnitTests.IO.Caching +namespace Neo.Json.UnitTests { [TestClass] public class UT_OrderedDictionary @@ -57,7 +64,7 @@ public void TestSetAndGetItem() public void TestGetKeys() { var keys = od.Keys; - keys.Contains("a").Should().BeTrue(); + keys.Should().Contain("a"); keys.Count.Should().Be(3); } @@ -65,7 +72,7 @@ public void TestGetKeys() public void TestGetValues() { var values = od.Values; - values.Contains(1).Should().BeTrue(); + values.Should().Contain(1); values.Count.Should().Be(3); } diff --git a/tests/Neo.Json.UnitTests/Usings.cs b/tests/Neo.Json.UnitTests/Usings.cs new file mode 100644 index 0000000000..e79662735c --- /dev/null +++ b/tests/Neo.Json.UnitTests/Usings.cs @@ -0,0 +1,13 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Usings.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. + +global using FluentAssertions; +global using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/tests/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs b/tests/Neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs similarity index 91% rename from tests/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs rename to tests/Neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs index 94fe8a77ec..3d4e4c56f2 100644 --- a/tests/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs +++ b/tests/Neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ECFieldElement.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; diff --git a/tests/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs b/tests/Neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs similarity index 92% rename from tests/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs rename to tests/Neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs index 6bf381d9a5..ee56b3c57e 100644 --- a/tests/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs +++ b/tests/Neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ECPoint.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; @@ -84,22 +95,40 @@ public void TestDecodePoint() public void TestDeserializeFrom() { byte[] input1 = { 0 }; - Action action = () => ECPoint.DeserializeFrom(new BinaryReader(new MemoryStream(input1)), ECCurve.Secp256k1); - action.Should().Throw(); + MemoryReader reader1 = new(input1); + try + { + ECPoint.DeserializeFrom(ref reader1, ECCurve.Secp256k1); + Assert.Fail(); + } + catch (FormatException) { } byte[] input2 = { 4, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152, 72, 58, 218, 119, 38, 163, 196, 101, 93, 164, 251, 252, 14, 17, 8, 168, 253, 23, 180, 72, 166, 133, 84, 25, 156, 71, 208, 143, 251, 16, 212, 184 }; - ECPoint.DeserializeFrom(new BinaryReader(new MemoryStream(input2)), ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); - action = () => ECPoint.DeserializeFrom(new BinaryReader(new MemoryStream(input2.Take(32).ToArray())), ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); - action.Should().Throw(); + MemoryReader reader2 = new(input2); + ECPoint.DeserializeFrom(ref reader2, ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); + reader2 = new(input2.Take(32).ToArray()); + try + { + ECPoint.DeserializeFrom(ref reader2, ECCurve.Secp256k1); + Assert.Fail(); + } + catch (FormatException) { } byte[] input3 = { 2, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152 }; - ECPoint.DeserializeFrom(new BinaryReader(new MemoryStream(input3)), ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); + MemoryReader reader3 = new(input3); + ECPoint.DeserializeFrom(ref reader3, ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); byte[] input4 = { 3, 107, 23, 209, 242, 225, 44, 66, 71, 248, 188, 230, 229, 99, 164, 64, 242, 119, 3, 125, 129, 45, 235, 51, 160, 244, 161, 57, 69, 216, 152, 194, 150 }; - ECPoint.DeserializeFrom(new BinaryReader(new MemoryStream(input4)), ECCurve.Secp256r1).Should().Be(ECCurve.Secp256r1.G); + MemoryReader reader4 = new(input4); + ECPoint.DeserializeFrom(ref reader4, ECCurve.Secp256r1).Should().Be(ECCurve.Secp256r1.G); - action = () => ECPoint.DeserializeFrom(new BinaryReader(new MemoryStream(input3.Take(input3.Length - 1).ToArray())), ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); - action.Should().Throw(); + reader3 = new(input3.Take(input3.Length - 1).ToArray()); + try + { + ECPoint.DeserializeFrom(ref reader3, ECCurve.Secp256k1); + Assert.Fail(); + } + catch (FormatException) { } } [TestMethod] @@ -270,7 +299,8 @@ public void TestDeserialize() ISerializable serializable = point; byte[] input = { 4, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152, 72, 58, 218, 119, 38, 163, 196, 101, 93, 164, 251, 252, 14, 17, 8, 168, 253, 23, 180, 72, 166, 133, 84, 25, 156, 71, 208, 143, 251, 16, 212, 184 }; - serializable.Deserialize(new BinaryReader(new MemoryStream(input))); + MemoryReader reader = new(input); + serializable.Deserialize(ref reader); point.X.Should().Be(ECCurve.Secp256k1.G.X); point.Y.Should().Be(ECCurve.Secp256k1.G.Y); } diff --git a/tests/neo.UnitTests/Cryptography/UT_Base58.cs b/tests/Neo.UnitTests/Cryptography/UT_Base58.cs similarity index 86% rename from tests/neo.UnitTests/Cryptography/UT_Base58.cs rename to tests/Neo.UnitTests/Cryptography/UT_Base58.cs index fbf29ba888..e7cb467dfa 100644 --- a/tests/neo.UnitTests/Cryptography/UT_Base58.cs +++ b/tests/Neo.UnitTests/Cryptography/UT_Base58.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Base58.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; diff --git a/tests/neo.UnitTests/Cryptography/UT_BloomFilter.cs b/tests/Neo.UnitTests/Cryptography/UT_BloomFilter.cs similarity index 84% rename from tests/neo.UnitTests/Cryptography/UT_BloomFilter.cs rename to tests/Neo.UnitTests/Cryptography/UT_BloomFilter.cs index 7ed470e3e1..907a5df2b9 100644 --- a/tests/neo.UnitTests/Cryptography/UT_BloomFilter.cs +++ b/tests/Neo.UnitTests/Cryptography/UT_BloomFilter.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_BloomFilter.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; diff --git a/tests/neo.UnitTests/Cryptography/UT_Crypto.cs b/tests/Neo.UnitTests/Cryptography/UT_Crypto.cs similarity index 89% rename from tests/neo.UnitTests/Cryptography/UT_Crypto.cs rename to tests/Neo.UnitTests/Cryptography/UT_Crypto.cs index cf54ed16ad..31c4a7ce49 100644 --- a/tests/neo.UnitTests/Cryptography/UT_Crypto.cs +++ b/tests/Neo.UnitTests/Cryptography/UT_Crypto.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Crypto.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; diff --git a/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs b/tests/Neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs similarity index 66% rename from tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs rename to tests/Neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs index eebe1c9802..01022baab9 100644 --- a/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs +++ b/tests/Neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Cryptography_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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; @@ -8,7 +19,6 @@ using Neo.Wallets.NEP6; using System; using System.Linq; -using System.Security; using System.Text; namespace Neo.UnitTests.Cryptography @@ -54,8 +64,7 @@ public void TestRIPEMD160() [TestMethod] public void TestAESEncryptAndDecrypt() { - NEP6Wallet wallet = new NEP6Wallet("", ProtocolSettings.Default); - wallet.Unlock("1"); + NEP6Wallet wallet = new NEP6Wallet("", "1", TestProtocolSettings.Default); wallet.CreateAccount(); WalletAccount account = wallet.GetAccounts().ToArray()[0]; KeyPair key = account.GetKey(); @@ -71,8 +80,7 @@ public void TestAESEncryptAndDecrypt() [TestMethod] public void TestEcdhEncryptAndDecrypt() { - NEP6Wallet wallet = new NEP6Wallet("", ProtocolSettings.Default); - wallet.Unlock("1"); + NEP6Wallet wallet = new NEP6Wallet("", "1", ProtocolSettings.Default); wallet.CreateAccount(); wallet.CreateAccount(); WalletAccount account1 = wallet.GetAccounts().ToArray()[0]; @@ -121,62 +129,5 @@ public void TestTest() filter.Add(tx.Hash.ToArray()); filter.Test(tx).Should().BeTrue(); } - - [TestMethod] - public void TestStringToAesKey() - { - string password = "hello world"; - string string1 = "bc62d4b80d9e36da29c16c5d4d9f11731f36052c72401a76c23c0fb5a9b74423"; - byte[] byteArray = string1.HexToBytes(); - password.ToAesKey().Should().Equal(byteArray); - } - - [TestMethod] - public void TestSecureStringToAesKey() - { - var password = new SecureString(); - password.AppendChar('h'); - password.AppendChar('e'); - password.AppendChar('l'); - password.AppendChar('l'); - password.AppendChar('o'); - password.AppendChar(' '); - password.AppendChar('w'); - password.AppendChar('o'); - password.AppendChar('r'); - password.AppendChar('l'); - password.AppendChar('d'); - string string1 = "bc62d4b80d9e36da29c16c5d4d9f11731f36052c72401a76c23c0fb5a9b74423"; - byte[] byteArray = string1.HexToBytes(); - password.ToAesKey().Should().Equal(byteArray); - } - - [TestMethod] - public void TestToArray() - { - var password = new SecureString(); - password.AppendChar('h'); - password.AppendChar('e'); - password.AppendChar('l'); - password.AppendChar('l'); - password.AppendChar('o'); - password.AppendChar(' '); - password.AppendChar('w'); - password.AppendChar('o'); - password.AppendChar('r'); - password.AppendChar('l'); - password.AppendChar('d'); - byte[] byteArray = { 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64 }; - password.ToArray().Should().Equal(byteArray); - - SecureString nullString = null; - Action action = () => nullString.ToArray(); - action.Should().Throw(); - - var zeroString = new SecureString(); - var result = zeroString.ToArray(); - byteArray = Array.Empty(); - result.Should().Equal(byteArray); - } } } diff --git a/tests/neo.UnitTests/Cryptography/UT_MerkleTree.cs b/tests/Neo.UnitTests/Cryptography/UT_MerkleTree.cs similarity index 86% rename from tests/neo.UnitTests/Cryptography/UT_MerkleTree.cs rename to tests/Neo.UnitTests/Cryptography/UT_MerkleTree.cs index 612a013923..f8a6b19b34 100644 --- a/tests/neo.UnitTests/Cryptography/UT_MerkleTree.cs +++ b/tests/Neo.UnitTests/Cryptography/UT_MerkleTree.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_MerkleTree.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; diff --git a/tests/neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs b/tests/Neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs similarity index 71% rename from tests/neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs rename to tests/Neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs index 7ae6f7658b..8defb8d6c7 100644 --- a/tests/neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs +++ b/tests/Neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_MerkleTreeNode.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; @@ -8,7 +19,7 @@ namespace Neo.UnitTests.Cryptography [TestClass] public class UT_MerkleTreeNode { - private MerkleTreeNode node = new MerkleTreeNode(); + private readonly MerkleTreeNode node = new MerkleTreeNode(); [TestInitialize] public void TestSetup() diff --git a/tests/neo.UnitTests/Cryptography/UT_Murmur128.cs b/tests/Neo.UnitTests/Cryptography/UT_Murmur128.cs similarity index 73% rename from tests/neo.UnitTests/Cryptography/UT_Murmur128.cs rename to tests/Neo.UnitTests/Cryptography/UT_Murmur128.cs index 49f6657696..f33dd7110a 100644 --- a/tests/neo.UnitTests/Cryptography/UT_Murmur128.cs +++ b/tests/Neo.UnitTests/Cryptography/UT_Murmur128.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Murmur128.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; diff --git a/tests/neo.UnitTests/Cryptography/UT_Murmur32.cs b/tests/Neo.UnitTests/Cryptography/UT_Murmur32.cs similarity index 57% rename from tests/neo.UnitTests/Cryptography/UT_Murmur32.cs rename to tests/Neo.UnitTests/Cryptography/UT_Murmur32.cs index 1b5b2fc218..1c885dbd08 100644 --- a/tests/neo.UnitTests/Cryptography/UT_Murmur32.cs +++ b/tests/Neo.UnitTests/Cryptography/UT_Murmur32.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Murmur32.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; diff --git a/tests/neo.UnitTests/Cryptography/UT_SCrypt.cs b/tests/Neo.UnitTests/Cryptography/UT_SCrypt.cs similarity index 59% rename from tests/neo.UnitTests/Cryptography/UT_SCrypt.cs rename to tests/Neo.UnitTests/Cryptography/UT_SCrypt.cs index f0934b8138..c3a3127ff0 100644 --- a/tests/neo.UnitTests/Cryptography/UT_SCrypt.cs +++ b/tests/Neo.UnitTests/Cryptography/UT_SCrypt.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_SCrypt.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.VisualStudio.TestTools.UnitTesting; using Org.BouncyCastle.Crypto.Generators; diff --git a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs b/tests/Neo.UnitTests/Extensions/NativeContractExtensions.cs similarity index 90% rename from tests/neo.UnitTests/Extensions/NativeContractExtensions.cs rename to tests/Neo.UnitTests/Extensions/NativeContractExtensions.cs index 58ab1bf5b1..9e72fc7ec3 100644 --- a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs +++ b/tests/Neo.UnitTests/Extensions/NativeContractExtensions.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// NativeContractExtensions.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.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract; @@ -42,7 +53,7 @@ public static void UpdateContract(this DataCache snapshot, UInt160 callingScript // Fake calling script hash if (callingScriptHash != null) { - engine.CurrentContext.GetState().CallingScriptHash = callingScriptHash; + engine.CurrentContext.GetState().NativeCallingScriptHash = callingScriptHash; engine.CurrentContext.GetState().ScriptHash = callingScriptHash; } @@ -65,7 +76,7 @@ public static void DestroyContract(this DataCache snapshot, UInt160 callingScrip // Fake calling script hash if (callingScriptHash != null) { - engine.CurrentContext.GetState().CallingScriptHash = callingScriptHash; + engine.CurrentContext.GetState().NativeCallingScriptHash = callingScriptHash; engine.CurrentContext.GetState().ScriptHash = callingScriptHash; } diff --git a/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs b/tests/Neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs similarity index 88% rename from tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs rename to tests/Neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs index 01720306ed..a3f33f26a7 100644 --- a/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs +++ b/tests/Neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs @@ -1,4 +1,16 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Nep17NativeContractExtensions.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 FluentAssertions; +using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract; @@ -24,9 +36,9 @@ public ManualWitness(params UInt160[] hashForVerify) _hashForVerify = hashForVerify ?? System.Array.Empty(); } - public void Deserialize(BinaryReader reader) { } + public void Deserialize(ref MemoryReader reader) { } - public void DeserializeUnsigned(BinaryReader reader) { } + public void DeserializeUnsigned(ref MemoryReader reader) { } public UInt160[] GetScriptHashesForVerifying(DataCache snapshot) => _hashForVerify; diff --git a/tests/neo.UnitTests/IO/Caching/UT_Cache.cs b/tests/Neo.UnitTests/IO/Caching/UT_Cache.cs similarity index 94% rename from tests/neo.UnitTests/IO/Caching/UT_Cache.cs rename to tests/Neo.UnitTests/IO/Caching/UT_Cache.cs index 529ac66a9e..a622cc3767 100644 --- a/tests/neo.UnitTests/IO/Caching/UT_Cache.cs +++ b/tests/Neo.UnitTests/IO/Caching/UT_Cache.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Cache.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Caching; diff --git a/tests/Neo.UnitTests/IO/Caching/UT_CloneCache.cs b/tests/Neo.UnitTests/IO/Caching/UT_CloneCache.cs new file mode 100644 index 0000000000..4d57da62b9 --- /dev/null +++ b/tests/Neo.UnitTests/IO/Caching/UT_CloneCache.cs @@ -0,0 +1,184 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_CloneCache.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 FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.Persistence; +using Neo.SmartContract; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Neo.UnitTests.IO.Caching +{ + [TestClass] + public class UT_CloneCache + { + private readonly MemoryStore store = new(); + private SnapshotCache myDataCache; + private ClonedCache clonedCache; + + private static readonly StorageKey key1 = new() { Id = 0, Key = Encoding.UTF8.GetBytes("key1") }; + private static readonly StorageKey key2 = new() { Id = 0, Key = Encoding.UTF8.GetBytes("key2") }; + private static readonly StorageKey key3 = new() { Id = 0, Key = Encoding.UTF8.GetBytes("key3") }; + private static readonly StorageKey key4 = new() { Id = 0, Key = Encoding.UTF8.GetBytes("key4") }; + + private static readonly StorageItem value1 = new(Encoding.UTF8.GetBytes("value1")); + private static readonly StorageItem value2 = new(Encoding.UTF8.GetBytes("value2")); + private static readonly StorageItem value3 = new(Encoding.UTF8.GetBytes("value3")); + private static readonly StorageItem value4 = new(Encoding.UTF8.GetBytes("value4")); + + [TestInitialize] + public void Init() + { + myDataCache = new(store); + clonedCache = new ClonedCache(myDataCache); + } + + [TestMethod] + public void TestCloneCache() + { + clonedCache.Should().NotBeNull(); + } + + [TestMethod] + public void TestAddInternal() + { + clonedCache.Add(key1, value1); + clonedCache[key1].Should().Be(value1); + + clonedCache.Commit(); + Assert.IsTrue(myDataCache[key1].Value.Span.SequenceEqual(value1.Value.Span)); + } + + [TestMethod] + public void TestDeleteInternal() + { + myDataCache.Add(key1, value1); + clonedCache.Delete(key1); // trackable.State = TrackState.Deleted + clonedCache.Commit(); + + clonedCache.TryGet(key1).Should().BeNull(); + myDataCache.TryGet(key1).Should().BeNull(); + } + + [TestMethod] + public void TestFindInternal() + { + clonedCache.Add(key1, value1); + myDataCache.Add(key2, value2); + store.Put(key3.ToArray(), value3.ToArray()); + + var items = clonedCache.Find(key1.ToArray()); + items.ElementAt(0).Key.Should().Be(key1); + items.ElementAt(0).Value.Should().Be(value1); + items.Count().Should().Be(1); + + items = clonedCache.Find(key2.ToArray()); + items.ElementAt(0).Key.Should().Be(key2); + value2.EqualsTo(items.ElementAt(0).Value).Should().BeTrue(); + items.Count().Should().Be(1); + + items = clonedCache.Find(key3.ToArray()); + items.ElementAt(0).Key.Should().Be(key3); + value3.EqualsTo(items.ElementAt(0).Value).Should().BeTrue(); + items.Count().Should().Be(1); + + items = clonedCache.Find(key4.ToArray()); + items.Count().Should().Be(0); + } + + [TestMethod] + public void TestGetInternal() + { + clonedCache.Add(key1, value1); + myDataCache.Add(key2, value2); + store.Put(key3.ToArray(), value3.ToArray()); + + value1.EqualsTo(clonedCache[key1]).Should().BeTrue(); + value2.EqualsTo(clonedCache[key2]).Should().BeTrue(); + value3.EqualsTo(clonedCache[key3]).Should().BeTrue(); + + Action action = () => + { + var item = clonedCache[key4]; + }; + action.Should().Throw(); + } + + [TestMethod] + public void TestTryGetInternal() + { + clonedCache.Add(key1, value1); + myDataCache.Add(key2, value2); + store.Put(key3.ToArray(), value3.ToArray()); + + value1.EqualsTo(clonedCache.TryGet(key1)).Should().BeTrue(); + value2.EqualsTo(clonedCache.TryGet(key2)).Should().BeTrue(); + value3.EqualsTo(clonedCache.TryGet(key3)).Should().BeTrue(); + clonedCache.TryGet(key4).Should().BeNull(); + } + + [TestMethod] + public void TestUpdateInternal() + { + clonedCache.Add(key1, value1); + myDataCache.Add(key2, value2); + store.Put(key3.ToArray(), value3.ToArray()); + + clonedCache.GetAndChange(key1).Value = Encoding.Default.GetBytes("value_new_1"); + clonedCache.GetAndChange(key2).Value = Encoding.Default.GetBytes("value_new_2"); + clonedCache.GetAndChange(key3).Value = Encoding.Default.GetBytes("value_new_3"); + + clonedCache.Commit(); + + StorageItem value_new_1 = new(Encoding.UTF8.GetBytes("value_new_1")); + StorageItem value_new_2 = new(Encoding.UTF8.GetBytes("value_new_2")); + StorageItem value_new_3 = new(Encoding.UTF8.GetBytes("value_new_3")); + + value_new_1.EqualsTo(clonedCache[key1]).Should().BeTrue(); + value_new_2.EqualsTo(clonedCache[key2]).Should().BeTrue(); + value_new_3.EqualsTo(clonedCache[key3]).Should().BeTrue(); + value_new_2.EqualsTo(clonedCache[key2]).Should().BeTrue(); + } + + [TestMethod] + public void TestCacheOverrideIssue2572() + { + var snapshot = TestBlockchain.GetTestSnapshot(); + var storages = snapshot.CreateSnapshot(); + + storages.Add + ( + new StorageKey() { Key = new byte[] { 0x00, 0x01 }, Id = 0 }, + new StorageItem() { Value = Array.Empty() } + ); + storages.Add + ( + new StorageKey() { Key = new byte[] { 0x01, 0x01 }, Id = 0 }, + new StorageItem() { Value = new byte[] { 0x05 } } + ); + + storages.Commit(); + + var item = storages.GetAndChange(new StorageKey() { Key = new byte[] { 0x01, 0x01 }, Id = 0 }); + item.Value = new byte[] { 0x06 }; + + var res = snapshot.TryGet(new StorageKey() { Key = new byte[] { 0x01, 0x01 }, Id = 0 }); + Assert.AreEqual("05", res.Value.Span.ToHexString()); + storages.Commit(); + res = snapshot.TryGet(new StorageKey() { Key = new byte[] { 0x01, 0x01 }, Id = 0 }); + Assert.AreEqual("06", res.Value.Span.ToHexString()); + } + } +} diff --git a/tests/Neo.UnitTests/IO/Caching/UT_DataCache.cs b/tests/Neo.UnitTests/IO/Caching/UT_DataCache.cs new file mode 100644 index 0000000000..5235b99c70 --- /dev/null +++ b/tests/Neo.UnitTests/IO/Caching/UT_DataCache.cs @@ -0,0 +1,401 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_DataCache.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 FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.Persistence; +using Neo.SmartContract; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Neo.UnitTests.IO.Caching +{ + [TestClass] + public class UT_DataCache + { + private readonly MemoryStore store = new(); + private SnapshotCache myDataCache; + + private static readonly StorageKey key1 = new() { Id = 0, Key = Encoding.UTF8.GetBytes("key1") }; + private static readonly StorageKey key2 = new() { Id = 0, Key = Encoding.UTF8.GetBytes("key2") }; + private static readonly StorageKey key3 = new() { Id = 0, Key = Encoding.UTF8.GetBytes("key3") }; + private static readonly StorageKey key4 = new() { Id = 0, Key = Encoding.UTF8.GetBytes("key4") }; + private static readonly StorageKey key5 = new() { Id = 0, Key = Encoding.UTF8.GetBytes("key5") }; + + private static readonly StorageItem value1 = new(Encoding.UTF8.GetBytes("value1")); + private static readonly StorageItem value2 = new(Encoding.UTF8.GetBytes("value2")); + private static readonly StorageItem value3 = new(Encoding.UTF8.GetBytes("value3")); + private static readonly StorageItem value4 = new(Encoding.UTF8.GetBytes("value4")); + private static readonly StorageItem value5 = new(Encoding.UTF8.GetBytes("value5")); + + [TestInitialize] + public void Initialize() + { + myDataCache = new(store); + } + + [TestMethod] + public void TestAccessByKey() + { + myDataCache.Add(key1, value1); + myDataCache.Add(key2, value2); + + myDataCache[key1].EqualsTo(value1).Should().BeTrue(); + + // case 2 read from inner + store.Put(key3.ToArray(), value3.ToArray()); + myDataCache[key3].EqualsTo(value3).Should().BeTrue(); + } + + [TestMethod] + public void TestAccessByNotFoundKey() + { + Action action = () => + { + var item = myDataCache[key1]; + }; + action.Should().Throw(); + } + + [TestMethod] + public void TestAccessByDeletedKey() + { + store.Put(key1.ToArray(), value1.ToArray()); + myDataCache.Delete(key1); + + Action action = () => + { + var item = myDataCache[key1]; + }; + action.Should().Throw(); + } + + [TestMethod] + public void TestAdd() + { + myDataCache.Add(key1, value1); + myDataCache[key1].Should().Be(value1); + + Action action = () => myDataCache.Add(key1, value1); + action.Should().Throw(); + + store.Put(key2.ToArray(), value2.ToArray()); + myDataCache.Delete(key2); + Assert.AreEqual(TrackState.Deleted, myDataCache.GetChangeSet().Where(u => u.Key.Equals(key2)).Select(u => u.State).FirstOrDefault()); + myDataCache.Add(key2, value2); + Assert.AreEqual(TrackState.Changed, myDataCache.GetChangeSet().Where(u => u.Key.Equals(key2)).Select(u => u.State).FirstOrDefault()); + + action = () => myDataCache.Add(key2, value2); + action.Should().Throw(); + } + + [TestMethod] + public void TestCommit() + { + using var store = new MemoryStore(); + store.Put(key2.ToArray(), value2.ToArray()); + store.Put(key3.ToArray(), value3.ToArray()); + + using var snapshot = store.GetSnapshot(); + using var myDataCache = new SnapshotCache(snapshot); + + myDataCache.Add(key1, value1); + Assert.AreEqual(TrackState.Added, myDataCache.GetChangeSet().Where(u => u.Key.Equals(key1)).Select(u => u.State).FirstOrDefault()); + + myDataCache.Delete(key2); + Assert.AreEqual(TrackState.Deleted, myDataCache.GetChangeSet().Where(u => u.Key.Equals(key2)).Select(u => u.State).FirstOrDefault()); + + Assert.AreEqual(TrackState.None, myDataCache.GetChangeSet().Where(u => u.Key.Equals(key3)).Select(u => u.State).FirstOrDefault()); + myDataCache.Delete(key3); + Assert.AreEqual(TrackState.Deleted, myDataCache.GetChangeSet().Where(u => u.Key.Equals(key3)).Select(u => u.State).FirstOrDefault()); + myDataCache.Add(key3, value4); + Assert.AreEqual(TrackState.Changed, myDataCache.GetChangeSet().Where(u => u.Key.Equals(key3)).Select(u => u.State).FirstOrDefault()); + + myDataCache.Commit(); + Assert.AreEqual(0, myDataCache.GetChangeSet().Count()); + + store.TryGet(key1.ToArray()).SequenceEqual(value1.ToArray()).Should().BeTrue(); + store.TryGet(key2.ToArray()).Should().BeNull(); + store.TryGet(key3.ToArray()).SequenceEqual(value4.ToArray()).Should().BeTrue(); + } + + [TestMethod] + public void TestCreateSnapshot() + { + myDataCache.CreateSnapshot().Should().NotBeNull(); + } + + [TestMethod] + public void TestDelete() + { + using var store = new MemoryStore(); + store.Put(key2.ToArray(), value2.ToArray()); + + using var snapshot = store.GetSnapshot(); + using var myDataCache = new SnapshotCache(snapshot); + + myDataCache.Add(key1, value1); + myDataCache.Delete(key1); + store.TryGet(key1.ToArray()).Should().BeNull(); + + myDataCache.Delete(key2); + myDataCache.Commit(); + store.TryGet(key2.ToArray()).Should().BeNull(); + } + + [TestMethod] + public void TestFind() + { + myDataCache.Add(key1, value1); + myDataCache.Add(key2, value2); + + store.Put(key3.ToArray(), value3.ToArray()); + store.Put(key4.ToArray(), value4.ToArray()); + + var k1 = key1.ToArray(); + var items = myDataCache.Find(k1); + key1.Should().Be(items.ElementAt(0).Key); + value1.Should().Be(items.ElementAt(0).Value); + items.Count().Should().Be(1); + + // null and empty with the forward direction -> finds everything. + items = myDataCache.Find(null); + items.Count().Should().Be(4); + items = myDataCache.Find(new byte[] { }); + items.Count().Should().Be(4); + + // null and empty with the backwards direction -> miserably fails. + Action action = () => myDataCache.Find(null, SeekDirection.Backward); + action.Should().Throw(); + action = () => myDataCache.Find(new byte[] { }, SeekDirection.Backward); + action.Should().Throw(); + + items = myDataCache.Find(k1, SeekDirection.Backward); + key1.Should().Be(items.ElementAt(0).Key); + value1.Should().Be(items.ElementAt(0).Value); + items.Count().Should().Be(1); + + var prefix = k1.Take(k1.Count() - 1).ToArray(); // Just the "key" part to match everything. + items = myDataCache.Find(prefix); + items.Count().Should().Be(4); + key1.Should().Be(items.ElementAt(0).Key); + value1.Should().Be(items.ElementAt(0).Value); + key2.Should().Be(items.ElementAt(1).Key); + value2.Should().Be(items.ElementAt(1).Value); + key3.Should().Be(items.ElementAt(2).Key); + value3.EqualsTo(items.ElementAt(2).Value).Should().BeTrue(); + key4.Should().Be(items.ElementAt(3).Key); + value4.EqualsTo(items.ElementAt(3).Value).Should().BeTrue(); + + items = myDataCache.Find(prefix, SeekDirection.Backward); + items.Count().Should().Be(4); + key4.Should().Be(items.ElementAt(0).Key); + value4.EqualsTo(items.ElementAt(0).Value).Should().BeTrue(); + key3.Should().Be(items.ElementAt(1).Key); + value3.EqualsTo(items.ElementAt(1).Value).Should().BeTrue(); + key2.Should().Be(items.ElementAt(2).Key); + value2.Should().Be(items.ElementAt(2).Value); + key1.Should().Be(items.ElementAt(3).Key); + value1.Should().Be(items.ElementAt(3).Value); + + items = myDataCache.Find(key5.ToArray()); + items.Count().Should().Be(0); + } + + [TestMethod] + public void TestSeek() + { + myDataCache.Add(key1, value1); + myDataCache.Add(key2, value2); + + store.Put(key3.ToArray(), value3.ToArray()); + store.Put(key4.ToArray(), value4.ToArray()); + + var items = myDataCache.Seek(key3.ToArray(), SeekDirection.Backward).ToArray(); + key3.Should().Be(items[0].Key); + value3.EqualsTo(items[0].Value).Should().BeTrue(); + key2.Should().Be(items[1].Key); + value2.EqualsTo(items[1].Value).Should().BeTrue(); + items.Length.Should().Be(3); + + items = myDataCache.Seek(key5.ToArray(), SeekDirection.Forward).ToArray(); + items.Length.Should().Be(0); + } + + [TestMethod] + public void TestFindRange() + { + var store = new MemoryStore(); + store.Put(key3.ToArray(), value3.ToArray()); + store.Put(key4.ToArray(), value4.ToArray()); + + var myDataCache = new SnapshotCache(store); + myDataCache.Add(key1, value1); + myDataCache.Add(key2, value2); + + var items = myDataCache.FindRange(key3.ToArray(), key5.ToArray()).ToArray(); + key3.Should().Be(items[0].Key); + value3.EqualsTo(items[0].Value).Should().BeTrue(); + key4.Should().Be(items[1].Key); + value4.EqualsTo(items[1].Value).Should().BeTrue(); + items.Length.Should().Be(2); + + // case 2 Need to sort the cache of myDataCache + + store = new(); + store.Put(key4.ToArray(), value4.ToArray()); + store.Put(key3.ToArray(), value3.ToArray()); + + myDataCache = new(store); + myDataCache.Add(key1, value1); + myDataCache.Add(key2, value2); + + items = myDataCache.FindRange(key3.ToArray(), key5.ToArray()).ToArray(); + key3.Should().Be(items[0].Key); + value3.EqualsTo(items[0].Value).Should().BeTrue(); + key4.Should().Be(items[1].Key); + value4.EqualsTo(items[1].Value).Should().BeTrue(); + items.Length.Should().Be(2); + + // case 3 FindRange by Backward + + store = new(); + store.Put(key4.ToArray(), value4.ToArray()); + store.Put(key3.ToArray(), value3.ToArray()); + store.Put(key5.ToArray(), value5.ToArray()); + + myDataCache = new(store); + myDataCache.Add(key1, value1); + myDataCache.Add(key2, value2); + + items = myDataCache.FindRange(key5.ToArray(), key3.ToArray(), SeekDirection.Backward).ToArray(); + key5.Should().Be(items[0].Key); + value5.EqualsTo(items[0].Value).Should().BeTrue(); + key4.Should().Be(items[1].Key); + value4.EqualsTo(items[1].Value).Should().BeTrue(); + items.Length.Should().Be(2); + } + + [TestMethod] + public void TestGetChangeSet() + { + myDataCache.Add(key1, value1); + Assert.AreEqual(TrackState.Added, myDataCache.GetChangeSet().Where(u => u.Key.Equals(key1)).Select(u => u.State).FirstOrDefault()); + myDataCache.Add(key2, value2); + Assert.AreEqual(TrackState.Added, myDataCache.GetChangeSet().Where(u => u.Key.Equals(key2)).Select(u => u.State).FirstOrDefault()); + + store.Put(key3.ToArray(), value3.ToArray()); + store.Put(key4.ToArray(), value4.ToArray()); + myDataCache.Delete(key3); + Assert.AreEqual(TrackState.Deleted, myDataCache.GetChangeSet().Where(u => u.Key.Equals(key3)).Select(u => u.State).FirstOrDefault()); + myDataCache.Delete(key4); + Assert.AreEqual(TrackState.Deleted, myDataCache.GetChangeSet().Where(u => u.Key.Equals(key4)).Select(u => u.State).FirstOrDefault()); + + var items = myDataCache.GetChangeSet(); + int i = 0; + foreach (var item in items) + { + i++; + StorageKey key = new() { Id = 0, Key = Encoding.UTF8.GetBytes("key" + i) }; + StorageItem value = new(Encoding.UTF8.GetBytes("value" + i)); + key.Should().Be(item.Key); + value.EqualsTo(item.Item).Should().BeTrue(); + } + i.Should().Be(4); + } + + [TestMethod] + public void TestGetAndChange() + { + myDataCache.Add(key1, value1); + Assert.AreEqual(TrackState.Added, myDataCache.GetChangeSet().Where(u => u.Key.Equals(key1)).Select(u => u.State).FirstOrDefault()); + store.Put(key2.ToArray(), value2.ToArray()); + store.Put(key3.ToArray(), value3.ToArray()); + myDataCache.Delete(key3); + Assert.AreEqual(TrackState.Deleted, myDataCache.GetChangeSet().Where(u => u.Key.Equals(key3)).Select(u => u.State).FirstOrDefault()); + + StorageItem value_bk_1 = new(Encoding.UTF8.GetBytes("value_bk_1")); + StorageItem value_bk_2 = new(Encoding.UTF8.GetBytes("value_bk_2")); + StorageItem value_bk_3 = new(Encoding.UTF8.GetBytes("value_bk_3")); + StorageItem value_bk_4 = new(Encoding.UTF8.GetBytes("value_bk_4")); + + myDataCache.GetAndChange(key1, () => value_bk_1).EqualsTo(value1).Should().BeTrue(); + myDataCache.GetAndChange(key2, () => value_bk_2).EqualsTo(value2).Should().BeTrue(); + myDataCache.GetAndChange(key3, () => value_bk_3).EqualsTo(value_bk_3).Should().BeTrue(); + myDataCache.GetAndChange(key4, () => value_bk_4).EqualsTo(value_bk_4).Should().BeTrue(); + } + + [TestMethod] + public void TestGetOrAdd() + { + myDataCache.Add(key1, value1); + Assert.AreEqual(TrackState.Added, myDataCache.GetChangeSet().Where(u => u.Key.Equals(key1)).Select(u => u.State).FirstOrDefault()); + store.Put(key2.ToArray(), value2.ToArray()); + store.Put(key3.ToArray(), value3.ToArray()); + myDataCache.Delete(key3); + Assert.AreEqual(TrackState.Deleted, myDataCache.GetChangeSet().Where(u => u.Key.Equals(key3)).Select(u => u.State).FirstOrDefault()); + + StorageItem value_bk_1 = new(Encoding.UTF8.GetBytes("value_bk_1")); + StorageItem value_bk_2 = new(Encoding.UTF8.GetBytes("value_bk_2")); + StorageItem value_bk_3 = new(Encoding.UTF8.GetBytes("value_bk_3")); + StorageItem value_bk_4 = new(Encoding.UTF8.GetBytes("value_bk_4")); + + myDataCache.GetOrAdd(key1, () => value_bk_1).EqualsTo(value1).Should().BeTrue(); + myDataCache.GetOrAdd(key2, () => value_bk_2).EqualsTo(value2).Should().BeTrue(); + myDataCache.GetOrAdd(key3, () => value_bk_3).EqualsTo(value_bk_3).Should().BeTrue(); + myDataCache.GetOrAdd(key4, () => value_bk_4).EqualsTo(value_bk_4).Should().BeTrue(); + } + + [TestMethod] + public void TestTryGet() + { + myDataCache.Add(key1, value1); + Assert.AreEqual(TrackState.Added, myDataCache.GetChangeSet().Where(u => u.Key.Equals(key1)).Select(u => u.State).FirstOrDefault()); + store.Put(key2.ToArray(), value2.ToArray()); + store.Put(key3.ToArray(), value3.ToArray()); + myDataCache.Delete(key3); + Assert.AreEqual(TrackState.Deleted, myDataCache.GetChangeSet().Where(u => u.Key.Equals(key3)).Select(u => u.State).FirstOrDefault()); + + myDataCache.TryGet(key1).EqualsTo(value1).Should().BeTrue(); + myDataCache.TryGet(key2).EqualsTo(value2).Should().BeTrue(); + myDataCache.TryGet(key3).Should().BeNull(); + } + + [TestMethod] + public void TestFindInvalid() + { + using var store = new MemoryStore(); + using var myDataCache = new SnapshotCache(store); + myDataCache.Add(key1, value1); + + store.Put(key2.ToArray(), value2.ToArray()); + store.Put(key3.ToArray(), value3.ToArray()); + store.Put(key4.ToArray(), value3.ToArray()); + + var items = myDataCache.Find().GetEnumerator(); + items.MoveNext().Should().Be(true); + items.Current.Key.Should().Be(key1); + + myDataCache.TryGet(key3); // GETLINE + + items.MoveNext().Should().Be(true); + items.Current.Key.Should().Be(key2); + items.MoveNext().Should().Be(true); + items.Current.Key.Should().Be(key3); + items.MoveNext().Should().Be(true); + items.Current.Key.Should().Be(key4); + items.MoveNext().Should().Be(false); + } + } +} diff --git a/tests/neo.UnitTests/IO/Caching/UT_ECPointCache.cs b/tests/Neo.UnitTests/IO/Caching/UT_ECPointCache.cs similarity index 63% rename from tests/neo.UnitTests/IO/Caching/UT_ECPointCache.cs rename to tests/Neo.UnitTests/IO/Caching/UT_ECPointCache.cs index dc36bdb96f..87be485ab6 100644 --- a/tests/neo.UnitTests/IO/Caching/UT_ECPointCache.cs +++ b/tests/Neo.UnitTests/IO/Caching/UT_ECPointCache.cs @@ -1,9 +1,18 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ECPointCache.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; using Neo.IO.Caching; -using Neo.Network.P2P.Payloads; -using System; namespace Neo.UnitTests.IO.Caching { diff --git a/tests/neo.UnitTests/IO/Caching/UT_HashSetCache.cs b/tests/Neo.UnitTests/IO/Caching/UT_HashSetCache.cs similarity index 92% rename from tests/neo.UnitTests/IO/Caching/UT_HashSetCache.cs rename to tests/Neo.UnitTests/IO/Caching/UT_HashSetCache.cs index ceb51130d2..9357aeb418 100644 --- a/tests/neo.UnitTests/IO/Caching/UT_HashSetCache.cs +++ b/tests/Neo.UnitTests/IO/Caching/UT_HashSetCache.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_HashSetCache.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Caching; diff --git a/tests/neo.UnitTests/IO/Caching/UT_IndexedQueue.cs b/tests/Neo.UnitTests/IO/Caching/UT_IndexedQueue.cs similarity index 88% rename from tests/neo.UnitTests/IO/Caching/UT_IndexedQueue.cs rename to tests/Neo.UnitTests/IO/Caching/UT_IndexedQueue.cs index e09726924c..4c7cc0697e 100644 --- a/tests/neo.UnitTests/IO/Caching/UT_IndexedQueue.cs +++ b/tests/Neo.UnitTests/IO/Caching/UT_IndexedQueue.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_IndexedQueue.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Caching; diff --git a/tests/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs b/tests/Neo.UnitTests/IO/Caching/UT_ReflectionCache.cs similarity index 82% rename from tests/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs rename to tests/Neo.UnitTests/IO/Caching/UT_ReflectionCache.cs index 62c1bd12ed..57e466d837 100644 --- a/tests/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs +++ b/tests/Neo.UnitTests/IO/Caching/UT_ReflectionCache.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ReflectionCache.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; @@ -9,7 +20,7 @@ namespace Neo.UnitTests.IO.Caching public class TestItem : ISerializable { public int Size => 0; - public void Deserialize(BinaryReader reader) { } + public void Deserialize(ref MemoryReader reader) { } public void Serialize(BinaryWriter writer) { } } diff --git a/tests/neo.UnitTests/IO/Caching/UT_RelayCache.cs b/tests/Neo.UnitTests/IO/Caching/UT_RelayCache.cs similarity index 73% rename from tests/neo.UnitTests/IO/Caching/UT_RelayCache.cs rename to tests/Neo.UnitTests/IO/Caching/UT_RelayCache.cs index 659b7f5797..a6c5ea3939 100644 --- a/tests/neo.UnitTests/IO/Caching/UT_RelayCache.cs +++ b/tests/Neo.UnitTests/IO/Caching/UT_RelayCache.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_RelayCache.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Caching; diff --git a/tests/neo.UnitTests/IO/UT_ByteArrayComparer.cs b/tests/Neo.UnitTests/IO/UT_ByteArrayComparer.cs similarity index 53% rename from tests/neo.UnitTests/IO/UT_ByteArrayComparer.cs rename to tests/Neo.UnitTests/IO/UT_ByteArrayComparer.cs index 89c0c621d7..19c3fafcd6 100644 --- a/tests/neo.UnitTests/IO/UT_ByteArrayComparer.cs +++ b/tests/Neo.UnitTests/IO/UT_ByteArrayComparer.cs @@ -1,6 +1,18 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ByteArrayComparer.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; +using System; namespace Neo.UnitTests.IO { @@ -11,10 +23,23 @@ public class UT_ByteArrayComparer public void TestCompare() { ByteArrayComparer comparer = ByteArrayComparer.Default; - byte[] x = new byte[0], y = new byte[0]; + byte[] x = null, y = null; + comparer.Compare(x, y).Should().Be(0); + + x = new byte[] { 1, 2, 3, 4, 5 }; + y = x; comparer.Compare(x, y).Should().Be(0); + comparer.Compare(x, x).Should().Be(0); + + y = null; + comparer.Compare(x, y).Should().Be(5); + + y = x; + x = null; + comparer.Compare(x, y).Should().Be(-5); x = new byte[] { 1 }; + y = Array.Empty(); comparer.Compare(x, y).Should().Be(1); y = x; comparer.Compare(x, y).Should().Be(0); diff --git a/tests/neo.UnitTests/IO/UT_ByteArrayEqualityComparer.cs b/tests/Neo.UnitTests/IO/UT_ByteArrayEqualityComparer.cs similarity index 76% rename from tests/neo.UnitTests/IO/UT_ByteArrayEqualityComparer.cs rename to tests/Neo.UnitTests/IO/UT_ByteArrayEqualityComparer.cs index 6b7152c239..ea5ae86be4 100644 --- a/tests/neo.UnitTests/IO/UT_ByteArrayEqualityComparer.cs +++ b/tests/Neo.UnitTests/IO/UT_ByteArrayEqualityComparer.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ByteArrayEqualityComparer.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.VisualStudio.TestTools.UnitTesting; using Neo.IO; using System; diff --git a/tests/neo.UnitTests/IO/UT_IOHelper.cs b/tests/Neo.UnitTests/IO/UT_IOHelper.cs similarity index 76% rename from tests/neo.UnitTests/IO/UT_IOHelper.cs rename to tests/Neo.UnitTests/IO/UT_IOHelper.cs index b126b7f485..4b74a495b5 100644 --- a/tests/neo.UnitTests/IO/UT_IOHelper.cs +++ b/tests/Neo.UnitTests/IO/UT_IOHelper.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_IOHelper.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; @@ -30,7 +41,7 @@ public void TestReadFixedBytes() // Less data - using (var reader = new BinaryReader(new MemoryStream(data), Encoding.UTF8, false)) + using (BinaryReader reader = new(new MemoryStream(data), Encoding.UTF8, false)) { byte[] result = Neo.IO.Helper.ReadFixedBytes(reader, 3); @@ -40,7 +51,7 @@ public void TestReadFixedBytes() // Same data - using (var reader = new BinaryReader(new MemoryStream(data), Encoding.UTF8, false)) + using (BinaryReader reader = new(new MemoryStream(data), Encoding.UTF8, false)) { byte[] result = Neo.IO.Helper.ReadFixedBytes(reader, 4); @@ -50,7 +61,7 @@ public void TestReadFixedBytes() // More data - using (var reader = new BinaryReader(new MemoryStream(data), Encoding.UTF8, false)) + using (BinaryReader reader = new(new MemoryStream(data), Encoding.UTF8, false)) { Assert.ThrowsException(() => Neo.IO.Helper.ReadFixedBytes(reader, 5)); Assert.AreEqual(4, reader.BaseStream.Position); @@ -72,8 +83,8 @@ public void TestNullableArray() }; byte[] data; - using (var stream = new MemoryStream()) - using (var writter = new BinaryWriter(stream)) + using (MemoryStream stream = new()) + using (BinaryWriter writter = new(stream)) { Neo.IO.Helper.WriteNullableArray(writter, caseArray); data = stream.ToArray(); @@ -81,20 +92,18 @@ public void TestNullableArray() // Read Error - using (var stream = new MemoryStream(data)) - using (var reader = new BinaryReader(stream)) + Assert.ThrowsException(() => { - Assert.ThrowsException(() => Neo.IO.Helper.ReadNullableArray(reader, 2)); - } + var reader = new MemoryReader(data); + reader.ReadNullableArray(2); + Assert.Fail(); + }); // Read 100% - using (var stream = new MemoryStream(data)) - using (var reader = new BinaryReader(stream)) - { - var read = Neo.IO.Helper.ReadNullableArray(reader); - CollectionAssert.AreEqual(caseArray, read); - } + MemoryReader reader = new(data); + var read = Neo.IO.Helper.ReadNullableArray(ref reader); + CollectionAssert.AreEqual(caseArray, read); } [TestMethod] @@ -113,7 +122,7 @@ public void TestAsSerializable() } else { - Action action = () => Neo.IO.Helper.AsSerializable(new byte[0], typeof(Double)); + Action action = () => Neo.IO.Helper.AsSerializable(Array.Empty(), typeof(double)); action.Should().Throw(); } } @@ -124,7 +133,7 @@ public void TestCompression() { var data = new byte[] { 1, 2, 3, 4 }; var byteArray = Neo.IO.Helper.CompressLz4(data); - var result = Neo.IO.Helper.DecompressLz4(byteArray, byte.MaxValue); + var result = Neo.IO.Helper.DecompressLz4(byteArray.Span, byte.MaxValue); CollectionAssert.AreEqual(result, data); @@ -134,20 +143,21 @@ public void TestCompression() for (int x = 0; x < data.Length; x++) data[x] = 1; byteArray = Neo.IO.Helper.CompressLz4(data); - result = Neo.IO.Helper.DecompressLz4(byteArray, byte.MaxValue); + result = Neo.IO.Helper.DecompressLz4(byteArray.Span, byte.MaxValue); Assert.IsTrue(byteArray.Length < result.Length); CollectionAssert.AreEqual(result, data); // Error max length - Assert.ThrowsException(() => Neo.IO.Helper.DecompressLz4(byteArray, byte.MaxValue - 1)); - Assert.ThrowsException(() => Neo.IO.Helper.DecompressLz4(byteArray, -1)); + Assert.ThrowsException(() => Neo.IO.Helper.DecompressLz4(byteArray.Span, byte.MaxValue - 1)); + Assert.ThrowsException(() => Neo.IO.Helper.DecompressLz4(byteArray.Span, -1)); // Error length - byteArray[0]++; - Assert.ThrowsException(() => Neo.IO.Helper.DecompressLz4(byteArray, byte.MaxValue)); + byte[] data_wrong = byteArray.ToArray(); + data_wrong[0]++; + Assert.ThrowsException(() => Neo.IO.Helper.DecompressLz4(data_wrong, byte.MaxValue)); } [TestMethod] @@ -228,7 +238,7 @@ public void TestGetVarSizeGeneric() } else if (i == 1)//sbyte { - List initList = new List + List initList = new() { TestEnum0.case1 }; @@ -238,7 +248,7 @@ public void TestGetVarSizeGeneric() } else if (i == 2)//byte { - List initList = new List + List initList = new() { TestEnum1.case1 }; @@ -248,7 +258,7 @@ public void TestGetVarSizeGeneric() } else if (i == 3)//short { - List initList = new List + List initList = new() { TestEnum2.case1 }; @@ -258,7 +268,7 @@ public void TestGetVarSizeGeneric() } else if (i == 4)//ushort { - List initList = new List + List initList = new() { TestEnum3.case1 }; @@ -268,7 +278,7 @@ public void TestGetVarSizeGeneric() } else if (i == 5)//int { - List initList = new List + List initList = new() { TestEnum4.case1 }; @@ -278,7 +288,7 @@ public void TestGetVarSizeGeneric() } else if (i == 6)//uint { - List initList = new List + List initList = new() { TestEnum5.case1 }; @@ -288,7 +298,7 @@ public void TestGetVarSizeGeneric() } else if (i == 7)//long { - List initList = new List + List initList = new() { TestEnum6.case1 }; @@ -298,7 +308,7 @@ public void TestGetVarSizeGeneric() } else if (i == 8) { - List initList = new List + List initList = new() { 1 }; @@ -316,39 +326,25 @@ public void TestGetVarSizeString() Assert.AreEqual(3, result); } - [TestMethod] - public void TestReadFixedString() - { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); - Neo.IO.Helper.WriteFixedString(writer, "AA", Encoding.UTF8.GetBytes("AA").Length + 1); - stream.Seek(0, SeekOrigin.Begin); - BinaryReader reader = new BinaryReader(stream); - string result = Neo.IO.Helper.ReadFixedString(reader, Encoding.UTF8.GetBytes("AA").Length + 1); - Assert.AreEqual("AA", result); - } - [TestMethod] public void TestReadSerializable() { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Neo.IO.Helper.Write(writer, UInt160.Zero); - stream.Seek(0, SeekOrigin.Begin); - BinaryReader reader = new BinaryReader(stream); - UInt160 result = Neo.IO.Helper.ReadSerializable(reader); + MemoryReader reader = new(stream.ToArray()); + UInt160 result = Neo.IO.Helper.ReadSerializable(ref reader); Assert.AreEqual(UInt160.Zero, result); } [TestMethod] public void TestReadSerializableArray() { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Neo.IO.Helper.Write(writer, new UInt160[] { UInt160.Zero }); - stream.Seek(0, SeekOrigin.Begin); - BinaryReader reader = new BinaryReader(stream); - UInt160[] resultArray = Neo.IO.Helper.ReadSerializableArray(reader); + MemoryReader reader = new(stream.ToArray()); + UInt160[] resultArray = Neo.IO.Helper.ReadSerializableArray(ref reader); Assert.AreEqual(1, resultArray.Length); Assert.AreEqual(UInt160.Zero, resultArray[0]); } @@ -356,11 +352,11 @@ public void TestReadSerializableArray() [TestMethod] public void TestReadVarBytes() { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Neo.IO.Helper.WriteVarBytes(writer, new byte[] { 0xAA, 0xAA }); stream.Seek(0, SeekOrigin.Begin); - BinaryReader reader = new BinaryReader(stream); + BinaryReader reader = new(stream); byte[] byteArray = Neo.IO.Helper.ReadVarBytes(reader, 10); Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0xAA, 0xAA }), Encoding.Default.GetString(byteArray)); } @@ -372,50 +368,37 @@ public void TestReadVarInt() { if (i == 0) { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Neo.IO.Helper.WriteVarInt(writer, 0xFFFF); stream.Seek(0, SeekOrigin.Begin); - BinaryReader reader = new BinaryReader(stream); + BinaryReader reader = new(stream); ulong result = Neo.IO.Helper.ReadVarInt(reader, 0xFFFF); Assert.AreEqual((ulong)0xFFFF, result); } else if (i == 1) { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Neo.IO.Helper.WriteVarInt(writer, 0xFFFFFFFF); stream.Seek(0, SeekOrigin.Begin); - BinaryReader reader = new BinaryReader(stream); + BinaryReader reader = new(stream); ulong result = Neo.IO.Helper.ReadVarInt(reader, 0xFFFFFFFF); Assert.AreEqual(0xFFFFFFFF, result); } else { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Neo.IO.Helper.WriteVarInt(writer, 0xFFFFFFFFFF); stream.Seek(0, SeekOrigin.Begin); - BinaryReader reader = new BinaryReader(stream); + BinaryReader reader = new(stream); Action action = () => Neo.IO.Helper.ReadVarInt(reader, 0xFFFFFFFF); action.Should().Throw(); } } } - [TestMethod] - public void TestReadVarString() - { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); - Neo.IO.Helper.WriteVarString(writer, "AAAAAAA"); - stream.Seek(0, SeekOrigin.Begin); - BinaryReader reader = new BinaryReader(stream); - string result = Neo.IO.Helper.ReadVarString(reader, 10); - stream.Seek(0, SeekOrigin.Begin); - Assert.AreEqual("AAAAAAA", result); - } - [TestMethod] public void TestToArray() { @@ -439,8 +422,8 @@ public void TestToByteArrayGeneric() [TestMethod] public void TestWrite() { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Neo.IO.Helper.Write(writer, UInt160.Zero); stream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[stream.Length]; @@ -454,8 +437,8 @@ public void TestWrite() [TestMethod] public void TestWriteGeneric() { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Neo.IO.Helper.Write(writer, new UInt160[] { UInt160.Zero }); stream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[stream.Length]; @@ -473,29 +456,29 @@ public void TestWriteFixedString() { if (i == 0) { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Action action = () => Neo.IO.Helper.WriteFixedString(writer, null, 0); action.Should().Throw(); } else if (i == 1) { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Action action = () => Neo.IO.Helper.WriteFixedString(writer, "AA", Encoding.UTF8.GetBytes("AA").Length - 1); action.Should().Throw(); } else if (i == 2) { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Action action = () => Neo.IO.Helper.WriteFixedString(writer, "拉拉", Encoding.UTF8.GetBytes("拉拉").Length - 1); action.Should().Throw(); } else if (i == 3) { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Neo.IO.Helper.WriteFixedString(writer, "AA", Encoding.UTF8.GetBytes("AA").Length + 1); stream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[stream.Length]; @@ -510,8 +493,8 @@ public void TestWriteFixedString() [TestMethod] public void TestWriteVarBytes() { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Neo.IO.Helper.WriteVarBytes(writer, new byte[] { 0xAA }); stream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[stream.Length]; @@ -526,15 +509,15 @@ public void TestWriteVarInt() { if (i == 0) { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Action action = () => Neo.IO.Helper.WriteVarInt(writer, -1); action.Should().Throw(); } else if (i == 1) { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Neo.IO.Helper.WriteVarInt(writer, 0xFC); stream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[stream.Length]; @@ -543,8 +526,8 @@ public void TestWriteVarInt() } else if (i == 2) { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Neo.IO.Helper.WriteVarInt(writer, 0xFFFF); stream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[stream.Length]; @@ -554,8 +537,8 @@ public void TestWriteVarInt() } else if (i == 3) { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Neo.IO.Helper.WriteVarInt(writer, 0xFFFFFFFF); stream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[stream.Length]; @@ -565,8 +548,8 @@ public void TestWriteVarInt() } else { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Neo.IO.Helper.WriteVarInt(writer, 0xAEFFFFFFFF); stream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[stream.Length]; @@ -580,8 +563,8 @@ public void TestWriteVarInt() [TestMethod] public void TestWriteVarString() { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream stream = new(); + BinaryWriter writer = new(stream); Neo.IO.Helper.WriteVarString(writer, "a"); stream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[stream.Length]; diff --git a/tests/Neo.UnitTests/IO/UT_MemoryReader.cs b/tests/Neo.UnitTests/IO/UT_MemoryReader.cs new file mode 100644 index 0000000000..a045a0b688 --- /dev/null +++ b/tests/Neo.UnitTests/IO/UT_MemoryReader.cs @@ -0,0 +1,53 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_MemoryReader.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.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using System.IO; +using System.Text; + +namespace Neo.UnitTests.IO +{ + [TestClass] + public class UT_MemoryReader + { + [TestMethod] + public void TestReadFixedString() + { + using MemoryStream stream = new(); + using BinaryWriter writer = new(stream); + writer.WriteFixedString("AA", Encoding.UTF8.GetBytes("AA").Length + 1); + MemoryReader reader = new(stream.ToArray()); + string result = reader.ReadFixedString(Encoding.UTF8.GetBytes("AA").Length + 1); + Assert.AreEqual("AA", result); + } + + [TestMethod] + public void TestReadVarString() + { + using MemoryStream stream = new(); + using BinaryWriter writer = new(stream); + writer.WriteVarString("AAAAAAA"); + MemoryReader reader = new(stream.ToArray()); + string result = reader.ReadVarString(10); + Assert.AreEqual("AAAAAAA", result); + } + + [TestMethod] + public void TestReadNullableArray() + { + byte[] bs = "0400000000".HexToBytes(); + MemoryReader reader = new(bs); + var n = reader.ReadNullableArray(); + Assert.AreEqual(5, reader.Position); + } + } +} diff --git a/tests/Neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/Neo.UnitTests/Ledger/UT_Blockchain.cs new file mode 100644 index 0000000000..491b4fe21e --- /dev/null +++ b/tests/Neo.UnitTests/Ledger/UT_Blockchain.cs @@ -0,0 +1,202 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Blockchain.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 Akka.TestKit; +using Akka.TestKit.Xunit2; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.Wallets; +using Neo.Wallets.NEP6; +using System; +using System.Linq; +using System.Numerics; + +namespace Neo.UnitTests.Ledger +{ + [TestClass] + public class UT_Blockchain : TestKit + { + private NeoSystem system; + private Transaction txSample; + private TestProbe senderProbe; + + [TestInitialize] + public void Initialize() + { + system = TestBlockchain.TheNeoSystem; + senderProbe = CreateTestProbe(); + txSample = new Transaction() + { + Attributes = Array.Empty(), + Script = Array.Empty(), + Signers = new Signer[] { new Signer() { Account = UInt160.Zero } }, + Witnesses = Array.Empty() + }; + system.MemPool.TryAdd(txSample, TestBlockchain.GetTestSnapshot()); + } + + [TestCleanup] + public void Clean() + { + TestBlockchain.ResetStore(); + } + + [TestMethod] + public void TestValidTransaction() + { + var snapshot = TestBlockchain.TheNeoSystem.GetSnapshot(); + var walletA = TestUtils.GenerateTestWallet("123"); + var acc = walletA.CreateAccount(); + + // Fake balance + + var key = new KeyBuilder(NativeContract.GAS.Id, 20).Add(acc.ScriptHash); + var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); + entry.GetInteroperable().Balance = 100_000_000 * NativeContract.GAS.Factor; + snapshot.Commit(); + + // Make transaction + + var tx = CreateValidTx(snapshot, walletA, acc.ScriptHash, 0); + + senderProbe.Send(system.Blockchain, tx); + senderProbe.ExpectMsg(p => p.Result == VerifyResult.Succeed); + + senderProbe.Send(system.Blockchain, tx); + senderProbe.ExpectMsg(p => p.Result == VerifyResult.AlreadyInPool); + } + + internal static StorageKey CreateStorageKey(byte prefix, byte[] key = null) + { + byte[] buffer = GC.AllocateUninitializedArray(sizeof(byte) + (key?.Length ?? 0)); + buffer[0] = prefix; + key?.CopyTo(buffer.AsSpan(1)); + return new() + { + Id = NativeContract.NEO.Id, + Key = buffer + }; + } + + private static Transaction CreateValidTx(DataCache snapshot, NEP6Wallet wallet, UInt160 account, uint nonce) + { + var tx = wallet.MakeTransaction(snapshot, new TransferOutput[] + { + new TransferOutput() + { + AssetId = NativeContract.GAS.Hash, + ScriptHash = account, + Value = new BigDecimal(BigInteger.One,8) + } + }, + account); + + tx.Nonce = nonce; + + var data = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); + Assert.IsNull(data.GetSignatures(tx.Sender)); + Assert.IsTrue(wallet.Sign(data)); + Assert.IsTrue(data.Completed); + Assert.AreEqual(1, data.GetSignatures(tx.Sender).Count()); + + tx.Witnesses = data.GetWitnesses(); + return tx; + } + + [TestMethod] + public void TestMaliciousOnChainConflict() + { + var snapshot = TestBlockchain.TheNeoSystem.GetSnapshot(); + var walletA = TestUtils.GenerateTestWallet("123"); + var accA = walletA.CreateAccount(); + var walletB = TestUtils.GenerateTestWallet("456"); + var accB = walletB.CreateAccount(); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: long.MaxValue); + engine.LoadScript(Array.Empty()); + + // Fake balance for accounts A and B. + var key = new KeyBuilder(NativeContract.GAS.Id, 20).Add(accA.ScriptHash); + var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); + entry.GetInteroperable().Balance = 100_000_000 * NativeContract.GAS.Factor; + snapshot.Commit(); + + key = new KeyBuilder(NativeContract.GAS.Id, 20).Add(accB.ScriptHash); + entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); + entry.GetInteroperable().Balance = 100_000_000 * NativeContract.GAS.Factor; + snapshot.Commit(); + + // Create transactions: + // tx1 conflicts with tx2 and has the same sender (thus, it's a valid conflict and must prevent tx2 from entering the chain); + // tx2 conflicts with tx3 and has different sender (thus, this conflict is invalid and must not prevent tx3 from entering the chain). + var tx1 = CreateValidTx(snapshot, walletA, accA.ScriptHash, 0); + var tx2 = CreateValidTx(snapshot, walletA, accA.ScriptHash, 1); + var tx3 = CreateValidTx(snapshot, walletB, accB.ScriptHash, 2); + + tx1.Attributes = new TransactionAttribute[] { new Conflicts() { Hash = tx2.Hash }, new Conflicts() { Hash = tx3.Hash } }; + + // Persist tx1. + var block = new Block + { + Header = new Header() + { + Index = 5, // allow tx1, tx2 and tx3 to fit into MaxValidUntilBlockIncrement. + MerkleRoot = UInt256.Zero, + NextConsensus = UInt160.Zero, + PrevHash = UInt256.Zero, + Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() } + }, + Transactions = new Transaction[] { tx1 }, + }; + byte[] onPersistScript; + using (ScriptBuilder sb = new()) + { + sb.EmitSysCall(ApplicationEngine.System_Contract_NativeOnPersist); + onPersistScript = sb.ToArray(); + } + using (ApplicationEngine engine2 = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, block, TestBlockchain.TheNeoSystem.Settings, 0)) + { + engine2.LoadScript(onPersistScript); + if (engine2.Execute() != VMState.HALT) throw engine2.FaultException; + engine2.Snapshot.Commit(); + } + snapshot.Commit(); + + // Run PostPersist to update current block index in native Ledger. + // Relevant current block index is needed for conflict records checks. + byte[] postPersistScript; + using (ScriptBuilder sb = new()) + { + sb.EmitSysCall(ApplicationEngine.System_Contract_NativePostPersist); + postPersistScript = sb.ToArray(); + } + using (ApplicationEngine engine2 = ApplicationEngine.Create(TriggerType.PostPersist, null, snapshot, block, TestBlockchain.TheNeoSystem.Settings, 0)) + { + engine2.LoadScript(postPersistScript); + if (engine2.Execute() != VMState.HALT) throw engine2.FaultException; + engine2.Snapshot.Commit(); + } + snapshot.Commit(); + + // Add tx2: must fail because valid conflict is alredy on chain (tx1). + senderProbe.Send(TestBlockchain.TheNeoSystem.Blockchain, tx2); + senderProbe.ExpectMsg(p => p.Result == VerifyResult.HasConflicts); + + // Add tx3: must succeed because on-chain conflict is invalid (doesn't have proper signer). + senderProbe.Send(TestBlockchain.TheNeoSystem.Blockchain, tx3); + senderProbe.ExpectMsg(p => p.Result == VerifyResult.Succeed); + } + } +} diff --git a/tests/neo.UnitTests/Ledger/UT_HashIndexState.cs b/tests/Neo.UnitTests/Ledger/UT_HashIndexState.cs similarity index 54% rename from tests/neo.UnitTests/Ledger/UT_HashIndexState.cs rename to tests/Neo.UnitTests/Ledger/UT_HashIndexState.cs index 8b3bcd60be..5351f17268 100644 --- a/tests/neo.UnitTests/Ledger/UT_HashIndexState.cs +++ b/tests/Neo.UnitTests/Ledger/UT_HashIndexState.cs @@ -1,9 +1,20 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_HashIndexState.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; -using System.IO; namespace Neo.UnitTests.Ledger { @@ -25,15 +36,11 @@ public void Initialize() [TestMethod] public void TestDeserialize() { - using MemoryStream ms = new MemoryStream(1024); - using BinaryReader reader = new BinaryReader(ms); - - var data = BinarySerializer.Serialize(((IInteroperable)origin).ToStackItem(null), 1024); - ms.Write(data); - ms.Seek(0, SeekOrigin.Begin); + var data = BinarySerializer.Serialize(((IInteroperable)origin).ToStackItem(null), ExecutionEngineLimits.Default); + var reader = new MemoryReader(data); - HashIndexState dest = new HashIndexState(); - ((IInteroperable)dest).FromStackItem(BinarySerializer.Deserialize(reader, ExecutionEngineLimits.Default, null)); + HashIndexState dest = new(); + ((IInteroperable)dest).FromStackItem(BinarySerializer.Deserialize(ref reader, ExecutionEngineLimits.Default, null)); dest.Hash.Should().Be(origin.Hash); dest.Index.Should().Be(origin.Index); diff --git a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/Neo.UnitTests/Ledger/UT_MemoryPool.cs similarity index 53% rename from tests/neo.UnitTests/Ledger/UT_MemoryPool.cs rename to tests/Neo.UnitTests/Ledger/UT_MemoryPool.cs index f76b022176..1063413073 100644 --- a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/Neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_MemoryPool.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 Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -7,7 +18,6 @@ using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; -using Neo.Plugins; using Neo.SmartContract; using Neo.SmartContract.Native; using System; @@ -19,13 +29,6 @@ namespace Neo.UnitTests.Ledger { - internal class TestIMemoryPoolTxObserverPlugin : Plugin, IMemoryPoolTxObserverPlugin - { - protected override void Configure() { } - public void TransactionAdded(NeoSystem system, Transaction tx) { } - public void TransactionsRemoved(NeoSystem system, MemoryPoolTxRemovalReason reason, IEnumerable transactions) { } - } - [TestClass] public class UT_MemoryPool : TestKit { @@ -35,8 +38,6 @@ public class UT_MemoryPool : TestKit private const byte Prefix_FeePerByte = 10; private readonly UInt160 senderAccount = UInt160.Zero; private MemoryPool _unit; - private MemoryPool _unit2; - private TestIMemoryPoolTxObserverPlugin plugin; [ClassInitialize] public static void TestSetup(TestContext ctx) @@ -56,7 +57,7 @@ public void TestSetup() TimeProvider.ResetToDefault(); // Create a MemoryPool with capacity of 100 - _unit = new MemoryPool(new NeoSystem(ProtocolSettings.Default with { MemoryPoolMaxTransactions = 100 })); + _unit = new MemoryPool(new NeoSystem(TestProtocolSettings.Default with { MemoryPoolMaxTransactions = 100 }, storageProvider: (string)null)); // Verify capacity equals the amount specified _unit.Capacity.Should().Be(100); @@ -64,14 +65,6 @@ public void TestSetup() _unit.VerifiedCount.Should().Be(0); _unit.UnVerifiedCount.Should().Be(0); _unit.Count.Should().Be(0); - _unit2 = new MemoryPool(new NeoSystem(ProtocolSettings.Default with { MemoryPoolMaxTransactions = 0 })); - plugin = new TestIMemoryPoolTxObserverPlugin(); - } - - [TestCleanup] - public void CleanUp() - { - Plugin.TxObserverPlugins.Remove(plugin); } private static long LongRandom(long min, long max, Random rand) @@ -87,7 +80,7 @@ private Transaction CreateTransactionWithFee(long fee) var randomBytes = new byte[16]; random.NextBytes(randomBytes); Mock mock = new(); - mock.Setup(p => p.VerifyStateDependent(It.IsAny(), It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); + mock.Setup(p => p.VerifyStateDependent(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())).Returns(VerifyResult.Succeed); mock.Setup(p => p.VerifyStateIndependent(It.IsAny())).Returns(VerifyResult.Succeed); mock.Object.Script = randomBytes; mock.Object.NetworkFee = fee; @@ -111,7 +104,7 @@ private Transaction CreateTransactionWithFeeAndBalanceVerify(long fee) random.NextBytes(randomBytes); Mock mock = new(); UInt160 sender = senderAccount; - mock.Setup(p => p.VerifyStateDependent(It.IsAny(), It.IsAny(), It.IsAny())).Returns((ProtocolSettings settings, DataCache snapshot, TransactionVerificationContext context) => context.CheckTransaction(mock.Object, snapshot) ? VerifyResult.Succeed : VerifyResult.InsufficientFunds); + mock.Setup(p => p.VerifyStateDependent(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())).Returns((ProtocolSettings settings, DataCache snapshot, TransactionVerificationContext context, IEnumerable conflictsList) => context.CheckTransaction(mock.Object, conflictsList, snapshot) ? VerifyResult.Succeed : VerifyResult.InsufficientFunds); mock.Setup(p => p.VerifyStateIndependent(It.IsAny())).Returns(VerifyResult.Succeed); mock.Object.Script = randomBytes; mock.Object.NetworkFee = fee; @@ -227,11 +220,12 @@ public async Task BlockPersistAndReverificationWillAbandonTxAsBalanceTransfered( var snapshot = GetSnapshot(); BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, senderAccount); ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: long.MaxValue); + engine.LoadScript(Array.Empty()); await NativeContract.GAS.Burn(engine, UInt160.Zero, balance); _ = NativeContract.GAS.Mint(engine, UInt160.Zero, 70, true); long txFee = 1; - AddTransactionsWithBalanceVerify(70, txFee, snapshot); + AddTransactionsWithBalanceVerify(70, txFee, engine.Snapshot); _unit.SortedTxCount.Should().Be(70); @@ -245,11 +239,12 @@ public async Task BlockPersistAndReverificationWillAbandonTxAsBalanceTransfered( UInt160 sender = block.Transactions[0].Sender; ApplicationEngine applicationEngine = ApplicationEngine.Create(TriggerType.All, block, snapshot, block, settings: TestBlockchain.TheNeoSystem.Settings, gas: (long)balance); + applicationEngine.LoadScript(Array.Empty()); await NativeContract.GAS.Burn(applicationEngine, sender, NativeContract.GAS.BalanceOf(snapshot, sender)); _ = NativeContract.GAS.Mint(applicationEngine, sender, txFee * 30, true); // Set the balance to meet 30 txs only // Persist block and reverify all the txs in mempool, but half of the txs will be discarded - _unit.UpdatePoolForBlockPersisted(block, snapshot); + _unit.UpdatePoolForBlockPersisted(block, applicationEngine.Snapshot); _unit.SortedTxCount.Should().Be(30); _unit.UnverifiedSortedTxCount.Should().Be(0); @@ -258,6 +253,215 @@ public async Task BlockPersistAndReverificationWillAbandonTxAsBalanceTransfered( _ = NativeContract.GAS.Mint(applicationEngine, sender, balance, true); } + [TestMethod] + public async Task UpdatePoolForBlockPersisted_RemoveBlockConflicts() + { + // Arrange: prepare mempooled and in-bock txs conflicting with each other. + long txFee = 1; + var snapshot = GetSnapshot(); + BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, senderAccount); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: long.MaxValue); + engine.LoadScript(Array.Empty()); + await NativeContract.GAS.Burn(engine, UInt160.Zero, balance); + _ = NativeContract.GAS.Mint(engine, UInt160.Zero, 7, true); // balance enough for 7 mempooled txs + + var mp1 = CreateTransactionWithFeeAndBalanceVerify(txFee); // mp1 doesn't conflict with anyone + _unit.TryAdd(mp1, engine.Snapshot).Should().Be(VerifyResult.Succeed); + var tx1 = CreateTransactionWithFeeAndBalanceVerify(txFee); // but in-block tx1 conflicts with mempooled mp1 => mp1 should be removed from pool after persist + tx1.Attributes = new TransactionAttribute[] { new Conflicts() { Hash = mp1.Hash } }; + + var mp2 = CreateTransactionWithFeeAndBalanceVerify(txFee); // mp1 and mp2 don't conflict with anyone + _unit.TryAdd(mp2, engine.Snapshot); + var mp3 = CreateTransactionWithFeeAndBalanceVerify(txFee); + _unit.TryAdd(mp3, engine.Snapshot); + var tx2 = CreateTransactionWithFeeAndBalanceVerify(txFee); // in-block tx2 conflicts with mempooled mp2 and mp3 => mp2 and mp3 should be removed from pool after persist + tx2.Attributes = new TransactionAttribute[] { new Conflicts() { Hash = mp2.Hash }, new Conflicts() { Hash = mp3.Hash } }; + + var tx3 = CreateTransactionWithFeeAndBalanceVerify(txFee); // in-block tx3 doesn't conflict with anyone + var mp4 = CreateTransactionWithFeeAndBalanceVerify(txFee); // mp4 conflicts with in-block tx3 => mp4 should be removed from pool after persist + mp4.Attributes = new TransactionAttribute[] { new Conflicts() { Hash = tx3.Hash } }; + _unit.TryAdd(mp4, engine.Snapshot); + + var tx4 = CreateTransactionWithFeeAndBalanceVerify(txFee); // in-block tx4 and tx5 don't conflict with anyone + var tx5 = CreateTransactionWithFeeAndBalanceVerify(txFee); + var mp5 = CreateTransactionWithFeeAndBalanceVerify(txFee); // mp5 conflicts with in-block tx4 and tx5 => mp5 should be removed from pool after persist + mp5.Attributes = new TransactionAttribute[] { new Conflicts() { Hash = tx4.Hash }, new Conflicts() { Hash = tx5.Hash } }; + _unit.TryAdd(mp5, engine.Snapshot); + + var mp6 = CreateTransactionWithFeeAndBalanceVerify(txFee); // mp6 doesn't conflict with anyone and noone conflicts with mp6 => mp6 should be left in the pool after persist + _unit.TryAdd(mp6, engine.Snapshot); + + _unit.SortedTxCount.Should().Be(6); + _unit.UnverifiedSortedTxCount.Should().Be(0); + + var mp7 = CreateTransactionWithFeeAndBalanceVerify(txFee); // mp7 doesn't conflict with anyone + var tx6 = CreateTransactionWithFeeAndBalanceVerify(txFee); // in-block tx6 conflicts with mp7, but doesn't include sender of mp7 into signers list => even if tx6 is included into block, mp7 shouldn't be removed from the pool + tx6.Signers = new Signer[] { new Signer() { Account = new UInt160(Crypto.Hash160(new byte[] { 1, 2, 3 })) }, new Signer() { Account = new UInt160(Crypto.Hash160(new byte[] { 4, 5, 6 })) } }; + tx6.Attributes = new TransactionAttribute[] { new Conflicts() { Hash = mp7.Hash } }; + _unit.TryAdd(mp7, engine.Snapshot); + + // Act: persist block and reverify all mempooled txs. + var block = new Block + { + Header = new Header(), + Transactions = new Transaction[] { tx1, tx2, tx3, tx4, tx5, tx6 }, + }; + _unit.UpdatePoolForBlockPersisted(block, engine.Snapshot); + + // Assert: conflicting txs should be removed from the pool; the only mp6 that doesn't conflict with anyone should be left. + _unit.SortedTxCount.Should().Be(2); + _unit.GetSortedVerifiedTransactions().Select(tx => tx.Hash).Should().Contain(mp6.Hash); + _unit.GetSortedVerifiedTransactions().Select(tx => tx.Hash).Should().Contain(mp7.Hash); + _unit.UnverifiedSortedTxCount.Should().Be(0); + + // Cleanup: revert the balance. + await NativeContract.GAS.Burn(engine, UInt160.Zero, txFee * 7); + _ = NativeContract.GAS.Mint(engine, UInt160.Zero, balance, true); + } + + [TestMethod] + public async Task TryAdd_AddRangeOfConflictingTransactions() + { + // Arrange: prepare mempooled txs that have conflicts. + long txFee = 1; + var maliciousSender = new UInt160(Crypto.Hash160(new byte[] { 1, 2, 3 })); + var snapshot = GetSnapshot(); + BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, senderAccount); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: long.MaxValue); + engine.LoadScript(Array.Empty()); + await NativeContract.GAS.Burn(engine, UInt160.Zero, balance); + _ = NativeContract.GAS.Mint(engine, UInt160.Zero, 100, true); // balance enough for all mempooled txs + _ = NativeContract.GAS.Mint(engine, maliciousSender, 100, true); // balance enough for all mempooled txs + + var mp1 = CreateTransactionWithFeeAndBalanceVerify(txFee); // mp1 doesn't conflict with anyone and not in the pool yet + + var mp2_1 = CreateTransactionWithFeeAndBalanceVerify(txFee); // mp2_1 conflicts with mp1 and has the same network fee + mp2_1.Attributes = new TransactionAttribute[] { new Conflicts() { Hash = mp1.Hash } }; + _unit.TryAdd(mp2_1, engine.Snapshot); + var mp2_2 = CreateTransactionWithFeeAndBalanceVerify(txFee); // mp2_2 also conflicts with mp1 and has the same network fee as mp1 and mp2_1 + mp2_2.Attributes = new TransactionAttribute[] { new Conflicts() { Hash = mp1.Hash } }; + _unit.TryAdd(mp2_2, engine.Snapshot); + + var mp3 = CreateTransactionWithFeeAndBalanceVerify(2 * txFee); // mp3 conflicts with mp1 and has larger network fee + mp3.Attributes = new TransactionAttribute[] { new Conflicts() { Hash = mp1.Hash } }; + _unit.TryAdd(mp3, engine.Snapshot); + + var mp4 = CreateTransactionWithFeeAndBalanceVerify(3 * txFee); // mp4 conflicts with mp3 and has larger network fee + mp4.Attributes = new TransactionAttribute[] { new Conflicts() { Hash = mp3.Hash } }; + + var malicious = CreateTransactionWithFeeAndBalanceVerify(3 * txFee); // malicious conflicts with mp3 and has larger network fee, but different sender + malicious.Attributes = new TransactionAttribute[] { new Conflicts() { Hash = mp3.Hash } }; + malicious.Signers = new Signer[] { new Signer() { Account = new UInt160(Crypto.Hash160(new byte[] { 1, 2, 3 })), Scopes = WitnessScope.None } }; + + var mp5 = CreateTransactionWithFeeAndBalanceVerify(2 * txFee); // mp5 conflicts with mp4 and has smaller network fee + mp5.Attributes = new TransactionAttribute[] { new Conflicts() { Hash = mp4.Hash } }; + + var mp6 = CreateTransactionWithFeeAndBalanceVerify(mp2_1.NetworkFee + mp2_2.NetworkFee + 1); // mp6 conflicts with mp2_1 and mp2_2 and has larger network fee. + mp6.Attributes = new TransactionAttribute[] { new Conflicts() { Hash = mp2_1.Hash }, new Conflicts() { Hash = mp2_2.Hash } }; + + var mp7 = CreateTransactionWithFeeAndBalanceVerify(txFee * 2 + 1); // mp7 doesn't conflicts with anyone, but mp8, mp9 and mp10malicious has smaller sum network fee and conflict with mp7. + var mp8 = CreateTransactionWithFeeAndBalanceVerify(txFee); + mp8.Attributes = new TransactionAttribute[] { new Conflicts() { Hash = mp7.Hash } }; + var mp9 = CreateTransactionWithFeeAndBalanceVerify(txFee); + mp9.Attributes = new TransactionAttribute[] { new Conflicts() { Hash = mp7.Hash } }; + var mp10malicious = CreateTransactionWithFeeAndBalanceVerify(txFee); + mp10malicious.Attributes = new TransactionAttribute[] { new Conflicts() { Hash = mp7.Hash } }; + mp10malicious.Signers = new Signer[] { new Signer() { Account = maliciousSender, Scopes = WitnessScope.None } }; + + _unit.SortedTxCount.Should().Be(3); + _unit.UnverifiedSortedTxCount.Should().Be(0); + + // Act & Assert: try to add conlflicting transactions to the pool. + _unit.TryAdd(mp1, engine.Snapshot).Should().Be(VerifyResult.HasConflicts); // mp1 conflicts with mp2_1, mp2_2 and mp3 but has lower network fee than mp3 => mp1 fails to be added + _unit.SortedTxCount.Should().Be(3); + _unit.GetVerifiedTransactions().Should().Contain(new List() { mp2_1, mp2_2, mp3 }); + + _unit.TryAdd(malicious, engine.Snapshot).Should().Be(VerifyResult.HasConflicts); // malicious conflicts with mp3, has larger network fee but malicious (different) sender => mp3 shoould be left in pool + _unit.SortedTxCount.Should().Be(3); + _unit.GetVerifiedTransactions().Should().Contain(new List() { mp2_1, mp2_2, mp3 }); + + _unit.TryAdd(mp4, engine.Snapshot).Should().Be(VerifyResult.Succeed); // mp4 conflicts with mp3 and has larger network fee => mp3 shoould be removed from pool + _unit.SortedTxCount.Should().Be(3); + _unit.GetVerifiedTransactions().Should().Contain(new List() { mp2_1, mp2_2, mp4 }); + + _unit.TryAdd(mp1, engine.Snapshot).Should().Be(VerifyResult.HasConflicts); // mp1 conflicts with mp2_1 and mp2_2 and has same network fee => mp2_1 and mp2_2 should be left in pool. + _unit.SortedTxCount.Should().Be(3); + _unit.GetVerifiedTransactions().Should().Contain(new List() { mp2_1, mp2_2, mp4 }); + + _unit.TryAdd(mp6, engine.Snapshot).Should().Be(VerifyResult.Succeed); // mp6 conflicts with mp2_1 and mp2_2 and has larger network fee than the sum of mp2_1 and mp2_2 fees => mp6 should be added. + _unit.SortedTxCount.Should().Be(2); + _unit.GetVerifiedTransactions().Should().Contain(new List() { mp6, mp4 }); + + _unit.TryAdd(mp1, engine.Snapshot).Should().Be(VerifyResult.Succeed); // mp1 conflicts with mp2_1 and mp2_2, but they are not in the pool now => mp1 should be added. + _unit.SortedTxCount.Should().Be(3); + _unit.GetVerifiedTransactions().Should().Contain(new List() { mp1, mp6, mp4 }); + + _unit.TryAdd(mp2_1, engine.Snapshot).Should().Be(VerifyResult.HasConflicts); // mp2_1 conflicts with mp1 and has same network fee => mp2_1 shouldn't be added to the pool. + _unit.SortedTxCount.Should().Be(3); + _unit.GetVerifiedTransactions().Should().Contain(new List() { mp1, mp6, mp4 }); + + _unit.TryAdd(mp5, engine.Snapshot).Should().Be(VerifyResult.HasConflicts); // mp5 conflicts with mp4 and has smaller network fee => mp5 fails to be added. + _unit.SortedTxCount.Should().Be(3); + _unit.GetVerifiedTransactions().Should().Contain(new List() { mp1, mp6, mp4 }); + + _unit.TryAdd(mp8, engine.Snapshot).Should().Be(VerifyResult.Succeed); // mp8, mp9 and mp10malicious conflict with mp7, but mo7 is not in the pool yet. + _unit.TryAdd(mp9, engine.Snapshot).Should().Be(VerifyResult.Succeed); + _unit.TryAdd(mp10malicious, engine.Snapshot).Should().Be(VerifyResult.Succeed); + _unit.SortedTxCount.Should().Be(6); + _unit.GetVerifiedTransactions().Should().Contain(new List() { mp1, mp6, mp4, mp8, mp9, mp10malicious }); + _unit.TryAdd(mp7, engine.Snapshot).Should().Be(VerifyResult.Succeed); // mp7 has larger network fee than the sum of mp8 and mp9 fees => should be added to the pool. + _unit.SortedTxCount.Should().Be(4); + _unit.GetVerifiedTransactions().Should().Contain(new List() { mp1, mp6, mp4, mp7 }); + + // Cleanup: revert the balance. + await NativeContract.GAS.Burn(engine, UInt160.Zero, 100); + _ = NativeContract.GAS.Mint(engine, UInt160.Zero, balance, true); + await NativeContract.GAS.Burn(engine, maliciousSender, 100); + _ = NativeContract.GAS.Mint(engine, maliciousSender, balance, true); + } + + [TestMethod] + public async Task TryRemoveVerified_RemoveVerifiedTxWithConflicts() + { + // Arrange: prepare mempooled txs that have conflicts. + long txFee = 1; + var snapshot = GetSnapshot(); + BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, senderAccount); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: long.MaxValue); + engine.LoadScript(Array.Empty()); + await NativeContract.GAS.Burn(engine, UInt160.Zero, balance); + _ = NativeContract.GAS.Mint(engine, UInt160.Zero, 100, true); // balance enough for all mempooled txs + + var mp1 = CreateTransactionWithFeeAndBalanceVerify(txFee); // mp1 doesn't conflict with anyone and not in the pool yet + + var mp2 = CreateTransactionWithFeeAndBalanceVerify(2 * txFee); // mp2 conflicts with mp1 and has larger same network fee + mp2.Attributes = new TransactionAttribute[] { new Conflicts() { Hash = mp1.Hash } }; + _unit.TryAdd(mp2, engine.Snapshot); + + _unit.SortedTxCount.Should().Be(1); + _unit.UnverifiedSortedTxCount.Should().Be(0); + + _unit.TryAdd(mp1, engine.Snapshot).Should().Be(VerifyResult.HasConflicts); // mp1 conflicts with mp2 but has lower network fee + _unit.SortedTxCount.Should().Be(1); + _unit.GetVerifiedTransactions().Should().Contain(new List() { mp2 }); + + // Act & Assert: try to invalidate verified transactions and push conflicting one. + _unit.InvalidateVerifiedTransactions(); + _unit.TryAdd(mp1, engine.Snapshot).Should().Be(VerifyResult.Succeed); // mp1 conflicts with mp2 but mp2 is not verified anymore + _unit.SortedTxCount.Should().Be(1); + _unit.GetVerifiedTransactions().Should().Contain(new List() { mp1 }); + + var tx1 = CreateTransactionWithFeeAndBalanceVerify(txFee); // in-block tx1 doesn't conflict with anyone and is aimed to trigger reverification + var block = new Block + { + Header = new Header(), + Transactions = new Transaction[] { tx1 }, + }; + _unit.UpdatePoolForBlockPersisted(block, engine.Snapshot); + _unit.SortedTxCount.Should().Be(1); + _unit.GetVerifiedTransactions().Should().Contain(new List() { mp2 }); // after reverificaion mp2 should be back at verified list; mp1 should be completely kicked off + } + private static void VerifyTransactionsSortedDescending(IEnumerable transactions) { Transaction lastTransaction = null; @@ -444,7 +648,7 @@ public void TestGetVerifiedTransactions() [TestMethod] public void TestReVerifyTopUnverifiedTransactionsIfNeeded() { - _unit = new MemoryPool(new NeoSystem(ProtocolSettings.Default with { MemoryPoolMaxTransactions = 600 })); + _unit = new MemoryPool(new NeoSystem(TestProtocolSettings.Default with { MemoryPoolMaxTransactions = 600 }, storageProvider: (string)null)); AddTransaction(CreateTransaction(100000001)); AddTransaction(CreateTransaction(100000001)); @@ -484,7 +688,6 @@ public void TestTryAdd() var tx1 = CreateTransaction(); _unit.TryAdd(tx1, snapshot).Should().Be(VerifyResult.Succeed); _unit.TryAdd(tx1, snapshot).Should().NotBe(VerifyResult.Succeed); - _unit2.TryAdd(tx1, snapshot).Should().NotBe(VerifyResult.Succeed); } [TestMethod] @@ -518,10 +721,8 @@ public void TestUpdatePoolForBlockPersisted() { Value = feePerByte }; - var key1 = CreateStorageKey(Prefix_MaxTransactionsPerBlock); - var key2 = CreateStorageKey(Prefix_FeePerByte); - key1.Id = NativeContract.Policy.Id; - key2.Id = NativeContract.Policy.Id; + var key1 = CreateStorageKey(NativeContract.Policy.Id, Prefix_MaxTransactionsPerBlock); + var key2 = CreateStorageKey(NativeContract.Policy.Id, Prefix_FeePerByte); snapshot.Add(key1, item1); snapshot.Add(key2, item2); @@ -545,17 +746,16 @@ public void TestUpdatePoolForBlockPersisted() _unit.VerifiedCount.Should().Be(0); } - public static StorageKey CreateStorageKey(byte prefix, byte[] key = null) + public static StorageKey CreateStorageKey(int id, byte prefix, byte[] key = null) { - StorageKey storageKey = new() + byte[] buffer = GC.AllocateUninitializedArray(sizeof(byte) + (key?.Length ?? 0)); + buffer[0] = prefix; + key?.CopyTo(buffer.AsSpan(1)); + return new() { - Id = 0, - Key = new byte[sizeof(byte) + (key?.Length ?? 0)] + Id = id, + Key = buffer }; - storageKey.Key[0] = prefix; - if (key != null) - Buffer.BlockCopy(key, 0, storageKey.Key, 1, key.Length); - return storageKey; } } } diff --git a/tests/neo.UnitTests/Ledger/UT_PoolItem.cs b/tests/Neo.UnitTests/Ledger/UT_PoolItem.cs similarity index 92% rename from tests/neo.UnitTests/Ledger/UT_PoolItem.cs rename to tests/Neo.UnitTests/Ledger/UT_PoolItem.cs index 6933bfb292..683291eda9 100644 --- a/tests/neo.UnitTests/Ledger/UT_PoolItem.cs +++ b/tests/Neo.UnitTests/Ledger/UT_PoolItem.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_PoolItem.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; diff --git a/tests/neo.UnitTests/Ledger/UT_StorageItem.cs b/tests/Neo.UnitTests/Ledger/UT_StorageItem.cs similarity index 71% rename from tests/neo.UnitTests/Ledger/UT_StorageItem.cs rename to tests/Neo.UnitTests/Ledger/UT_StorageItem.cs index e750c18782..8ee55919da 100644 --- a/tests/neo.UnitTests/Ledger/UT_StorageItem.cs +++ b/tests/Neo.UnitTests/Ledger/UT_StorageItem.cs @@ -1,5 +1,17 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_StorageItem.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using Neo.SmartContract; using System.IO; using System.Text; @@ -20,7 +32,7 @@ public void TestSetup() [TestMethod] public void Value_Get() { - uut.Value.Should().BeNull(); + uut.Value.IsEmpty.Should().BeTrue(); } [TestMethod] @@ -29,8 +41,8 @@ public void Value_Set() byte[] val = new byte[] { 0x42, 0x32 }; uut.Value = val; uut.Value.Length.Should().Be(2); - uut.Value[0].Should().Be(val[0]); - uut.Value[1].Should().Be(val[1]); + uut.Value.Span[0].Should().Be(val[0]); + uut.Value.Span[1].Should().Be(val[1]); } [TestMethod] @@ -53,11 +65,12 @@ public void Clone() uut.Value = TestUtils.GetByteArray(10, 0x42); StorageItem newSi = uut.Clone(); - newSi.Value.Length.Should().Be(10); - newSi.Value[0].Should().Be(0x42); + var span = newSi.Value.Span; + span.Length.Should().Be(10); + span[0].Should().Be(0x42); for (int i = 1; i < 10; i++) { - newSi.Value[i].Should().Be(0x20); + span[i].Should().Be(0x20); } } @@ -65,19 +78,14 @@ public void Clone() public void Deserialize() { byte[] data = new byte[] { 66, 32, 32, 32, 32, 32, 32, 32, 32, 32 }; - int index = 0; - using (MemoryStream ms = new MemoryStream(data, index, data.Length - index, false)) - { - using (BinaryReader reader = new BinaryReader(ms)) - { - uut.Deserialize(reader); - } - } - uut.Value.Length.Should().Be(10); - uut.Value[0].Should().Be(0x42); + MemoryReader reader = new(data); + uut.Deserialize(ref reader); + var span = uut.Value.Span; + span.Length.Should().Be(10); + span[0].Should().Be(0x42); for (int i = 1; i < 10; i++) { - uut.Value[i].Should().Be(0x20); + span[i].Should().Be(0x20); } } diff --git a/tests/neo.UnitTests/Ledger/UT_StorageKey.cs b/tests/Neo.UnitTests/Ledger/UT_StorageKey.cs similarity index 53% rename from tests/neo.UnitTests/Ledger/UT_StorageKey.cs rename to tests/Neo.UnitTests/Ledger/UT_StorageKey.cs index 1aa347eb5e..f2d9300fee 100644 --- a/tests/neo.UnitTests/Ledger/UT_StorageKey.cs +++ b/tests/Neo.UnitTests/Ledger/UT_StorageKey.cs @@ -1,74 +1,59 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_StorageKey.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; using Neo.SmartContract; -using System.IO; namespace Neo.UnitTests.Ledger { [TestClass] public class UT_StorageKey { - StorageKey uut; - - [TestInitialize] - public void TestSetup() - { - uut = new StorageKey(); - } - [TestMethod] public void Id_Get() { - uut.Id.Should().Be(0); - } - - [TestMethod] - public void Size() - { - var ut = new StorageKey() { Key = new byte[17], Id = 0 }; - ut.ToArray().Length.Should().Be(((ISerializable)ut).Size); - - ut = new StorageKey() { Key = new byte[0], Id = 0 }; - ut.ToArray().Length.Should().Be(((ISerializable)ut).Size); - - ut = new StorageKey() { Key = new byte[16], Id = 0 }; - ut.ToArray().Length.Should().Be(((ISerializable)ut).Size); + var uut = new StorageKey { Id = 1, Key = new byte[] { 0x01 } }; + uut.Id.Should().Be(1); } [TestMethod] public void Id_Set() { int val = 1; - uut.Id = val; + StorageKey uut = new() { Id = val }; uut.Id.Should().Be(val); } - [TestMethod] - public void Key_Get() - { - uut.Key.Should().BeNull(); - } - [TestMethod] public void Key_Set() { byte[] val = new byte[] { 0x42, 0x32 }; - uut.Key = val; + StorageKey uut = new() { Key = val }; uut.Key.Length.Should().Be(2); - uut.Key[0].Should().Be(val[0]); - uut.Key[1].Should().Be(val[1]); + uut.Key.Span[0].Should().Be(val[0]); + uut.Key.Span[1].Should().Be(val[1]); } [TestMethod] public void Equals_SameObj() { + StorageKey uut = new(); uut.Equals(uut).Should().BeTrue(); } [TestMethod] public void Equals_Null() { + StorageKey uut = new(); uut.Equals(null).Should().BeFalse(); } @@ -82,9 +67,7 @@ public void Equals_SameHash_SameKey() Id = val, Key = keyVal }; - uut.Id = val; - uut.Key = keyVal; - + StorageKey uut = new() { Id = val, Key = keyVal }; uut.Equals(newSk).Should().BeTrue(); } @@ -98,13 +81,10 @@ public void Equals_DiffHash_SameKey() Id = val, Key = keyVal }; - uut.Id = 0x78000000; - uut.Key = keyVal; - + StorageKey uut = new() { Id = 0x78000000, Key = keyVal }; uut.Equals(newSk).Should().BeFalse(); } - [TestMethod] public void Equals_SameHash_DiffKey() { @@ -115,43 +95,23 @@ public void Equals_SameHash_DiffKey() Id = val, Key = keyVal }; - uut.Id = val; - uut.Key = TestUtils.GetByteArray(10, 0x88); - + StorageKey uut = new() { Id = val, Key = TestUtils.GetByteArray(10, 0x88) }; uut.Equals(newSk).Should().BeFalse(); } [TestMethod] public void GetHashCode_Get() { - uut.Id = 0x42000000; - uut.Key = TestUtils.GetByteArray(10, 0x42); + StorageKey uut = new() { Id = 0x42000000, Key = TestUtils.GetByteArray(10, 0x42) }; uut.GetHashCode().Should().Be(1374529787); } [TestMethod] public void Equals_Obj() { + StorageKey uut = new(); uut.Equals(1u).Should().BeFalse(); uut.Equals((object)uut).Should().BeTrue(); } - - [TestMethod] - public void TestDeserialize() - { - using (MemoryStream ms = new MemoryStream(1024)) - using (BinaryWriter writer = new BinaryWriter(ms)) - using (BinaryReader reader = new BinaryReader(ms)) - { - uut.Id = 0x42000000; - uut.Key = TestUtils.GetByteArray(10, 0x42); - ((ISerializable)uut).Serialize(writer); - ms.Seek(0, SeekOrigin.Begin); - StorageKey dest = new StorageKey(); - ((ISerializable)dest).Deserialize(reader); - dest.Id.Should().Be(uut.Id); - dest.Key.Should().BeEquivalentTo(uut.Key); - } - } } } diff --git a/tests/Neo.UnitTests/Ledger/UT_TransactionState.cs b/tests/Neo.UnitTests/Ledger/UT_TransactionState.cs new file mode 100644 index 0000000000..0a5f1a102d --- /dev/null +++ b/tests/Neo.UnitTests/Ledger/UT_TransactionState.cs @@ -0,0 +1,81 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_TransactionState.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 FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using System; + +namespace Neo.UnitTests.Ledger +{ + [TestClass] + public class UT_TransactionState + { + TransactionState origin; + + TransactionState originTrimmed; + + [TestInitialize] + public void Initialize() + { + origin = new TransactionState + { + BlockIndex = 1, + Transaction = new Transaction() + { + Attributes = Array.Empty(), + Script = new byte[] { (byte)OpCode.PUSH1 }, + Signers = new Signer[] { new Signer() { Account = UInt160.Zero } }, + Witnesses = new Witness[] { new Witness() { + InvocationScript=Array.Empty(), + VerificationScript=Array.Empty() + } } + } + }; + originTrimmed = new TransactionState + { + BlockIndex = 1, + }; + } + + [TestMethod] + public void TestDeserialize() + { + var data = BinarySerializer.Serialize(((IInteroperable)origin).ToStackItem(null), ExecutionEngineLimits.Default); + var reader = new MemoryReader(data); + + TransactionState dest = new(); + ((IInteroperable)dest).FromStackItem(BinarySerializer.Deserialize(ref reader, ExecutionEngineLimits.Default, null)); + + dest.BlockIndex.Should().Be(origin.BlockIndex); + dest.Transaction.Hash.Should().Be(origin.Transaction.Hash); + dest.Transaction.Should().NotBeNull(); + } + + [TestMethod] + public void TestDeserializeTrimmed() + { + var data = BinarySerializer.Serialize(((IInteroperable)originTrimmed).ToStackItem(null), ExecutionEngineLimits.Default); + var reader = new MemoryReader(data); + + TransactionState dest = new(); + ((IInteroperable)dest).FromStackItem(BinarySerializer.Deserialize(ref reader, ExecutionEngineLimits.Default, null)); + + dest.BlockIndex.Should().Be(originTrimmed.BlockIndex); + dest.Transaction.Should().Be(null); + dest.Transaction.Should().BeNull(); + } + } +} diff --git a/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs b/tests/Neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs similarity index 56% rename from tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs rename to tests/Neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs index 27d584dca9..32a0bd6237 100644 --- a/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs +++ b/tests/Neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_TransactionVerificationContext.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -7,6 +18,7 @@ using Neo.SmartContract; using Neo.SmartContract.Native; using System; +using System.Collections.Generic; using System.Numerics; using System.Threading.Tasks; @@ -27,7 +39,7 @@ private Transaction CreateTransactionWithFee(long networkFee, long systemFee) var randomBytes = new byte[16]; random.NextBytes(randomBytes); Mock mock = new(); - mock.Setup(p => p.VerifyStateDependent(It.IsAny(), It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); + mock.Setup(p => p.VerifyStateDependent(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())).Returns(VerifyResult.Succeed); mock.Setup(p => p.VerifyStateIndependent(It.IsAny())).Returns(VerifyResult.Succeed); mock.Object.Script = randomBytes; mock.Object.NetworkFee = networkFee; @@ -60,12 +72,13 @@ public async Task TestDuplicateOracle() TransactionVerificationContext verificationContext = new(); var tx = CreateTransactionWithFee(1, 2); tx.Attributes = new TransactionAttribute[] { new OracleResponse() { Code = OracleResponseCode.ConsensusUnreachable, Id = 1, Result = Array.Empty() } }; - verificationContext.CheckTransaction(tx, snapshot).Should().BeTrue(); + var conflicts = new List(); + verificationContext.CheckTransaction(tx, conflicts, snapshot).Should().BeTrue(); verificationContext.AddTransaction(tx); tx = CreateTransactionWithFee(2, 1); tx.Attributes = new TransactionAttribute[] { new OracleResponse() { Code = OracleResponseCode.ConsensusUnreachable, Id = 1, Result = Array.Empty() } }; - verificationContext.CheckTransaction(tx, snapshot).Should().BeFalse(); + verificationContext.CheckTransaction(tx, conflicts, snapshot).Should().BeFalse(); } [TestMethod] @@ -79,15 +92,40 @@ public async Task TestTransactionSenderFee() TransactionVerificationContext verificationContext = new(); var tx = CreateTransactionWithFee(1, 2); - verificationContext.CheckTransaction(tx, snapshot).Should().BeTrue(); + var conflicts = new List(); + verificationContext.CheckTransaction(tx, conflicts, snapshot).Should().BeTrue(); verificationContext.AddTransaction(tx); - verificationContext.CheckTransaction(tx, snapshot).Should().BeTrue(); + verificationContext.CheckTransaction(tx, conflicts, snapshot).Should().BeTrue(); verificationContext.AddTransaction(tx); - verificationContext.CheckTransaction(tx, snapshot).Should().BeFalse(); + verificationContext.CheckTransaction(tx, conflicts, snapshot).Should().BeFalse(); verificationContext.RemoveTransaction(tx); - verificationContext.CheckTransaction(tx, snapshot).Should().BeTrue(); + verificationContext.CheckTransaction(tx, conflicts, snapshot).Should().BeTrue(); + verificationContext.AddTransaction(tx); + verificationContext.CheckTransaction(tx, conflicts, snapshot).Should().BeFalse(); + } + + [TestMethod] + public async Task TestTransactionSenderFeeWithConflicts() + { + var snapshot = TestBlockchain.GetTestSnapshot(); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: long.MaxValue); + BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, UInt160.Zero); + await NativeContract.GAS.Burn(engine, UInt160.Zero, balance); + _ = NativeContract.GAS.Mint(engine, UInt160.Zero, 3 + 3 + 1, true); // balance is enough for 2 transactions and 1 GAS is left. + + TransactionVerificationContext verificationContext = new(); + var tx = CreateTransactionWithFee(1, 2); + var conflictingTx = CreateTransactionWithFee(1, 1); // costs 2 GAS + + var conflicts = new List(); + verificationContext.CheckTransaction(tx, conflicts, snapshot).Should().BeTrue(); verificationContext.AddTransaction(tx); - verificationContext.CheckTransaction(tx, snapshot).Should().BeFalse(); + verificationContext.CheckTransaction(tx, conflicts, snapshot).Should().BeTrue(); + verificationContext.AddTransaction(tx); + verificationContext.CheckTransaction(tx, conflicts, snapshot).Should().BeFalse(); + + conflicts.Add(conflictingTx); + verificationContext.CheckTransaction(tx, conflicts, snapshot).Should().BeTrue(); // 1 GAS is left on the balance + 2 GAS is free after conflicts removal => enough for one more trasnaction. } } } diff --git a/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs b/tests/Neo.UnitTests/Ledger/UT_TrimmedBlock.cs similarity index 81% rename from tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs rename to tests/Neo.UnitTests/Ledger/UT_TrimmedBlock.cs index 4ed02ba24a..7942847f94 100644 --- a/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs +++ b/tests/Neo.UnitTests/Ledger/UT_TrimmedBlock.cs @@ -1,5 +1,17 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_TrimmedBlock.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.SmartContract.Native; using Neo.UnitTests.SmartContract; @@ -69,8 +81,8 @@ public void TestGetBlock() block.MerkleRoot.Should().Be(UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff02")); block.Transactions.Length.Should().Be(2); block.Transactions[0].Hash.Should().Be(tx1.Hash); - block.Witness.InvocationScript.ToHexString().Should().Be(tblock.Header.Witness.InvocationScript.ToHexString()); - block.Witness.VerificationScript.ToHexString().Should().Be(tblock.Header.Witness.VerificationScript.ToHexString()); + block.Witness.InvocationScript.Span.ToHexString().Should().Be(tblock.Header.Witness.InvocationScript.Span.ToHexString()); + block.Witness.VerificationScript.Span.ToHexString().Should().Be(tblock.Header.Witness.VerificationScript.Span.ToHexString()); } [TestMethod] @@ -98,14 +110,13 @@ public void TestDeserialize() var newBlock = new TrimmedBlock(); using (MemoryStream ms = new(1024)) using (BinaryWriter writer = new(ms)) - using (BinaryReader reader = new(ms)) { tblock.Serialize(writer); - ms.Seek(0, SeekOrigin.Begin); - newBlock.Deserialize(reader); + MemoryReader reader = new(ms.ToArray()); + newBlock.Deserialize(ref reader); } tblock.Hashes.Length.Should().Be(newBlock.Hashes.Length); - tblock.Header.ToJson(ProtocolSettings.Default).ToString().Should().Be(newBlock.Header.ToJson(ProtocolSettings.Default).ToString()); + tblock.Header.ToJson(TestProtocolSettings.Default).ToString().Should().Be(newBlock.Header.ToJson(ProtocolSettings.Default).ToString()); } } } diff --git a/tests/Neo.UnitTests/Neo.UnitTests.csproj b/tests/Neo.UnitTests/Neo.UnitTests.csproj new file mode 100644 index 0000000000..2e61bedbcc --- /dev/null +++ b/tests/Neo.UnitTests/Neo.UnitTests.csproj @@ -0,0 +1,26 @@ + + + + net7.0 + true + false + + + + + + + + + + + PreserveNewest + PreserveNewest + + + + + + + + diff --git a/tests/neo.UnitTests/Network/P2P/Capabilities/UT_FullNodeCapability.cs b/tests/Neo.UnitTests/Network/P2P/Capabilities/UT_FullNodeCapability.cs similarity index 62% rename from tests/neo.UnitTests/Network/P2P/Capabilities/UT_FullNodeCapability.cs rename to tests/Neo.UnitTests/Network/P2P/Capabilities/UT_FullNodeCapability.cs index 0c18c1fb44..c3c5c2c86f 100644 --- a/tests/neo.UnitTests/Network/P2P/Capabilities/UT_FullNodeCapability.cs +++ b/tests/Neo.UnitTests/Network/P2P/Capabilities/UT_FullNodeCapability.cs @@ -1,8 +1,18 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_FullNodeCapability.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.Network.P2P.Capabilities; -using System.IO; namespace Neo.UnitTests.Network.P2P.Capabilities { @@ -25,8 +35,8 @@ public void DeserializeAndSerialize() var test = new FullNodeCapability() { StartHeight = uint.MaxValue }; var buffer = test.ToArray(); - using var br = new BinaryReader(new MemoryStream(buffer)); - var clone = (FullNodeCapability)NodeCapability.DeserializeFrom(br); + var br = new MemoryReader(buffer); + var clone = (FullNodeCapability)NodeCapability.DeserializeFrom(ref br); Assert.AreEqual(test.StartHeight, clone.StartHeight); } diff --git a/tests/Neo.UnitTests/Network/P2P/Capabilities/UT_ServerCapability.cs b/tests/Neo.UnitTests/Network/P2P/Capabilities/UT_ServerCapability.cs new file mode 100644 index 0000000000..b113d59da7 --- /dev/null +++ b/tests/Neo.UnitTests/Network/P2P/Capabilities/UT_ServerCapability.cs @@ -0,0 +1,77 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ServerCapability.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 FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.Network.P2P.Capabilities; +using System; + +namespace Neo.UnitTests.Network.P2P.Capabilities +{ + [TestClass] + public class UT_ServerCapability + { + [TestMethod] + public void Size_Get() + { + var test = new ServerCapability(NodeCapabilityType.TcpServer) { Port = 1 }; + test.Size.Should().Be(3); + +#pragma warning disable CS0612 // Type or member is obsolete + test = new ServerCapability(NodeCapabilityType.WsServer) { Port = 2 }; +#pragma warning restore CS0612 // Type or member is obsolete + test.Size.Should().Be(3); + } + + [TestMethod] + public void DeserializeAndSerialize() + { +#pragma warning disable CS0612 // Type or member is obsolete + var test = new ServerCapability(NodeCapabilityType.WsServer) { Port = 2 }; + var buffer = test.ToArray(); + + var br = new MemoryReader(buffer); + var clone = (ServerCapability)NodeCapability.DeserializeFrom(ref br); + + Assert.AreEqual(test.Port, clone.Port); + Assert.AreEqual(test.Type, clone.Type); + + clone = new ServerCapability(NodeCapabilityType.WsServer, 123); +#pragma warning restore CS0612 // Type or member is obsolete + br = new MemoryReader(buffer); + ((ISerializable)clone).Deserialize(ref br); + + Assert.AreEqual(test.Port, clone.Port); + Assert.AreEqual(test.Type, clone.Type); + + clone = new ServerCapability(NodeCapabilityType.TcpServer, 123); + + Assert.ThrowsException(() => + { + var br2 = new MemoryReader(buffer); + ((ISerializable)clone).Deserialize(ref br2); + }); + Assert.ThrowsException(() => + { + _ = new ServerCapability(NodeCapabilityType.FullNode); + }); + + // Wrong type + buffer[0] = 0xFF; + Assert.ThrowsException(() => + { + var br2 = new MemoryReader(buffer); + NodeCapability.DeserializeFrom(ref br2); + }); + } + } +} diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_AddrPayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_AddrPayload.cs similarity index 77% rename from tests/neo.UnitTests/Network/P2P/Payloads/UT_AddrPayload.cs rename to tests/Neo.UnitTests/Network/P2P/Payloads/UT_AddrPayload.cs index fa19860ff2..6e189ad3ec 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_AddrPayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_AddrPayload.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_AddrPayload.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Block.cs similarity index 85% rename from tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs rename to tests/Neo.UnitTests/Network/P2P/Payloads/UT_Block.cs index 5a4ed4b176..2021e5a187 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Block.cs @@ -1,9 +1,19 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Block.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Network.P2P.Payloads; -using System.IO; namespace Neo.UnitTests.Network.P2P.Payloads { @@ -60,7 +70,7 @@ public void Size_Get_1_Transaction() TestUtils.GetTransaction(UInt160.Zero) }; - uut.Size.Should().Be(167); // 159 + nonce + uut.Size.Should().Be(167); // 159 + nonce } [TestMethod] @@ -97,9 +107,8 @@ public void Deserialize() var hex = "0000000000000000000000000000000000000000000000000000000000000000000000006c23be5d32679baa9c5c2aa0d329fd2a2441d7875d0f34d42f58f70428fbbbb9e913ff854c00000000000000000000000000000000000000000000000000000000000000000000000001000111010000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000001000112010000"; - using MemoryStream ms = new(hex.HexToBytes(), false); - using BinaryReader reader = new(ms); - uut.Deserialize(reader); + MemoryReader reader = new(hex.HexToBytes()); + uut.Deserialize(ref reader); UInt256 merkRoot = uut.MerkleRoot; AssertStandardBlockTestVals(val256, merkRoot, val160, timestampVal, indexVal, nonceVal, scriptVal, transactionsVal); @@ -115,7 +124,7 @@ private void AssertStandardBlockTestVals(UInt256 val256, UInt256 merkRoot, UInt1 uut.NextConsensus.Should().Be(val160); uut.Witness.InvocationScript.Length.Should().Be(0); uut.Witness.Size.Should().Be(scriptVal.Size); - uut.Witness.VerificationScript[0].Should().Be(scriptVal.VerificationScript[0]); + uut.Witness.VerificationScript.Span[0].Should().Be(scriptVal.VerificationScript.Span[0]); if (testTransactions) { uut.Transactions.Length.Should().Be(1); @@ -164,7 +173,7 @@ public void ToJson() UInt256 val256 = UInt256.Zero; TestUtils.SetupBlockWithValues(uut, val256, out _, out _, out var timeVal, out var indexVal, out var nonceVal, out _, out _, 1); - JObject jObj = uut.ToJson(ProtocolSettings.Default); + JObject jObj = uut.ToJson(TestProtocolSettings.Default); jObj.Should().NotBeNull(); jObj["hash"].AsString().Should().Be("0x60193a05005c433787d8a9b95da332bbeebb311e904525e9fb1bacc34ff1ead7"); jObj["size"].AsNumber().Should().Be(167); // 159 + nonce @@ -176,17 +185,17 @@ public void ToJson() jObj["index"].AsNumber().Should().Be(indexVal); jObj["nextconsensus"].AsString().Should().Be("NKuyBkoGdZZSLyPbJEetheRhMjeznFZszf"); - JObject scObj = ((JArray)jObj["witnesses"])[0]; + JObject scObj = (JObject)jObj["witnesses"][0]; scObj["invocation"].AsString().Should().Be(""); scObj["verification"].AsString().Should().Be("EQ=="); jObj["tx"].Should().NotBeNull(); - JArray txObj = (JArray)jObj["tx"]; - txObj[0]["hash"].AsString().Should().Be("0xb9bbfb2804f7582fd4340f5d87d741242afd29d3a02a5c9caa9b67325dbe236c"); - txObj[0]["size"].AsNumber().Should().Be(53); - txObj[0]["version"].AsNumber().Should().Be(0); - ((JArray)txObj[0]["attributes"]).Count.Should().Be(0); - txObj[0]["netfee"].AsString().Should().Be("0"); + JObject txObj = (JObject)jObj["tx"][0]; + txObj["hash"].AsString().Should().Be("0xb9bbfb2804f7582fd4340f5d87d741242afd29d3a02a5c9caa9b67325dbe236c"); + txObj["size"].AsNumber().Should().Be(53); + txObj["version"].AsNumber().Should().Be(0); + ((JArray)txObj["attributes"]).Count.Should().Be(0); + txObj["netfee"].AsString().Should().Be("0"); } } } diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Conflicts.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Conflicts.cs new file mode 100644 index 0000000000..ce46c5106d --- /dev/null +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Conflicts.cs @@ -0,0 +1,100 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Conflicts.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 FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using System; + +namespace Neo.UnitTests.Network.P2P.Payloads +{ + [TestClass] + public class UT_Conflicts + { + private const byte Prefix_Transaction = 11; + private static readonly UInt256 _u = new UInt256(new byte[32] { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01 + }); + + [TestMethod] + public void Size_Get() + { + var test = new Conflicts() { Hash = _u }; + test.Size.Should().Be(1 + 32); + } + + [TestMethod] + public void ToJson() + { + var test = new Conflicts() { Hash = _u }; + var json = test.ToJson().ToString(); + Assert.AreEqual(@"{""type"":""Conflicts"",""hash"":""0x0101010101010101010101010101010101010101010101010101010101010101""}", json); + } + + [TestMethod] + public void DeserializeAndSerialize() + { + var test = new Conflicts() { Hash = _u }; + + var clone = test.ToArray().AsSerializable(); + Assert.AreEqual(clone.Type, test.Type); + + // As transactionAttribute + byte[] buffer = test.ToArray(); + var reader = new MemoryReader(buffer); + clone = TransactionAttribute.DeserializeFrom(ref reader) as Conflicts; + Assert.AreEqual(clone.Type, test.Type); + + // Wrong type + buffer[0] = 0xff; + Assert.ThrowsException(() => + { + var reader = new MemoryReader(buffer); + TransactionAttribute.DeserializeFrom(ref reader); + }); + } + + [TestMethod] + public void Verify() + { + var test = new Conflicts() { Hash = _u }; + var snapshot = TestBlockchain.GetTestSnapshot(); + var key = Ledger.UT_MemoryPool.CreateStorageKey(NativeContract.Ledger.Id, Prefix_Transaction, _u.ToArray()); + + // Conflicting transaction is in the Conflicts attribute of some other on-chain transaction. + var conflict = new TransactionState(); + snapshot.Add(key, new StorageItem(conflict)); + Assert.IsTrue(test.Verify(snapshot, new Transaction())); + + // Conflicting transaction is on-chain. + snapshot.Delete(key); + conflict = new TransactionState + { + BlockIndex = 123, + Transaction = new Transaction(), + State = VMState.NONE + }; + snapshot.Add(key, new StorageItem(conflict)); + Assert.IsFalse(test.Verify(snapshot, new Transaction())); + + // There's no conflicting transaction at all. + snapshot.Delete(key); + Assert.IsTrue(test.Verify(snapshot, new Transaction())); + } + } +} diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs similarity index 79% rename from tests/neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs rename to tests/Neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs index 77663bda41..1a0ad19a9a 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ExtensiblePayload.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_FilterAddPayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_FilterAddPayload.cs similarity index 59% rename from tests/neo.UnitTests/Network/P2P/Payloads/UT_FilterAddPayload.cs rename to tests/Neo.UnitTests/Network/P2P/Payloads/UT_FilterAddPayload.cs index 6a0afc6cdf..76eff8e198 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_FilterAddPayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_FilterAddPayload.cs @@ -1,7 +1,19 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_FilterAddPayload.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.Network.P2P.Payloads; +using System; namespace Neo.UnitTests.Network.P2P.Payloads { @@ -24,7 +36,7 @@ public void DeserializeAndSerialize() var test = new FilterAddPayload() { Data = new byte[] { 1, 2, 3 } }; var clone = test.ToArray().AsSerializable(); - CollectionAssert.AreEqual(test.Data, clone.Data); + Assert.IsTrue(test.Data.Span.SequenceEqual(clone.Data.Span)); } } } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_FilterLoadPayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_FilterLoadPayload.cs similarity index 54% rename from tests/neo.UnitTests/Network/P2P/Payloads/UT_FilterLoadPayload.cs rename to tests/Neo.UnitTests/Network/P2P/Payloads/UT_FilterLoadPayload.cs index 1b1832d8d9..f73e9b5595 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_FilterLoadPayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_FilterLoadPayload.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_FilterLoadPayload.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; @@ -12,7 +23,7 @@ public class UT_FilterLoadPayload [TestMethod] public void Size_Get() { - var test = new FilterLoadPayload() { Filter = new byte[0], K = 1, Tweak = uint.MaxValue }; + var test = new FilterLoadPayload() { Filter = Array.Empty(), K = 1, Tweak = uint.MaxValue }; test.Size.Should().Be(6); test = FilterLoadPayload.Create(new Neo.Cryptography.BloomFilter(8, 10, 123456)); @@ -25,11 +36,11 @@ public void DeserializeAndSerialize() var test = FilterLoadPayload.Create(new Neo.Cryptography.BloomFilter(8, 10, 123456)); var clone = test.ToArray().AsSerializable(); - CollectionAssert.AreEqual(test.Filter, clone.Filter); + CollectionAssert.AreEqual(test.Filter.ToArray(), clone.Filter.ToArray()); Assert.AreEqual(test.K, clone.K); Assert.AreEqual(test.Tweak, clone.Tweak); - Assert.ThrowsException(() => new FilterLoadPayload() { Filter = new byte[0], K = 51, Tweak = uint.MaxValue }.ToArray().AsSerializable()); + Assert.ThrowsException(() => new FilterLoadPayload() { Filter = Array.Empty(), K = 51, Tweak = uint.MaxValue }.ToArray().AsSerializable()); } } } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_GetBlockByIndexPayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_GetBlockByIndexPayload.cs similarity index 78% rename from tests/neo.UnitTests/Network/P2P/Payloads/UT_GetBlockByIndexPayload.cs rename to tests/Neo.UnitTests/Network/P2P/Payloads/UT_GetBlockByIndexPayload.cs index 76ed1df9f9..3aa4049d87 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_GetBlockByIndexPayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_GetBlockByIndexPayload.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_GetBlockByIndexPayload.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_GetBlocksPayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_GetBlocksPayload.cs similarity index 77% rename from tests/neo.UnitTests/Network/P2P/Payloads/UT_GetBlocksPayload.cs rename to tests/Neo.UnitTests/Network/P2P/Payloads/UT_GetBlocksPayload.cs index 4c09147d8d..6752525ac0 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_GetBlocksPayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_GetBlocksPayload.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_GetBlocksPayload.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Header.cs similarity index 85% rename from tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs rename to tests/Neo.UnitTests/Network/P2P/Payloads/UT_Header.cs index b978b74156..df33505965 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Header.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Header.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; @@ -5,7 +16,6 @@ using Neo.SmartContract.Native; using Neo.UnitTests.SmartContract; using System; -using System.IO; namespace Neo.UnitTests.Network.P2P.Payloads { @@ -68,7 +78,8 @@ public void TrimTest() header.Timestamp.Should().Be(uut.Timestamp); header.Index.Should().Be(uut.Index); header.NextConsensus.Should().Be(uut.NextConsensus); - header.Witness.Should().BeEquivalentTo(uut.Witness); + header.Witness.InvocationScript.Span.SequenceEqual(uut.Witness.InvocationScript.Span).Should().BeTrue(); + header.Witness.VerificationScript.Span.SequenceEqual(uut.Witness.VerificationScript.Span).Should().BeTrue(); trim.Hashes.Length.Should().Be(0); } @@ -82,9 +93,8 @@ public void Deserialize() var hex = "0000000000000000000000000000000000000000000000000000000000000000000000007227ba7b747f1a98f68679d4a98b68927646ab195a6f56b542ca5a0e6a412662e913ff854c00000000000000000000000000000000000000000000000000000000000000000000000001000111"; - using MemoryStream ms = new(hex.HexToBytes(), false); - using BinaryReader reader = new(ms); - uut.Deserialize(reader); + MemoryReader reader = new(hex.HexToBytes()); + uut.Deserialize(ref reader); AssertStandardHeaderTestVals(val256, merkRoot, val160, timestampVal, nonceVal, indexVal, scriptVal); } @@ -99,7 +109,7 @@ private void AssertStandardHeaderTestVals(UInt256 val256, UInt256 merkRoot, UInt uut.NextConsensus.Should().Be(val160); uut.Witness.InvocationScript.Length.Should().Be(0); uut.Witness.Size.Should().Be(scriptVal.Size); - uut.Witness.VerificationScript[0].Should().Be(scriptVal.VerificationScript[0]); + uut.Witness.VerificationScript.Span[0].Should().Be(scriptVal.VerificationScript.Span[0]); } [TestMethod] diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_HeadersPayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_HeadersPayload.cs similarity index 72% rename from tests/neo.UnitTests/Network/P2P/Payloads/UT_HeadersPayload.cs rename to tests/Neo.UnitTests/Network/P2P/Payloads/UT_HeadersPayload.cs index b099403429..2ac486ef06 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_HeadersPayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_HeadersPayload.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_HeadersPayload.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs similarity index 61% rename from tests/neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs rename to tests/Neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs index 4b383bb44a..76808072d5 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs @@ -1,10 +1,20 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_HighPriorityAttribute.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.SmartContract.Native; using System; -using System.IO; namespace Neo.UnitTests.Network.P2P.Payloads { @@ -36,27 +46,28 @@ public void DeserializeAndSerialize() // As transactionAttribute - using var msRead = new MemoryStream(); - using var msWrite = new MemoryStream(); - using (var stream = new BinaryWriter(msWrite)) - { - var data = (test as TransactionAttribute).ToArray(); - msRead.Write(data); - msRead.Seek(0, SeekOrigin.Begin); - } - - using var reader = new BinaryReader(msRead); - clone = TransactionAttribute.DeserializeFrom(reader) as HighPriorityAttribute; + byte[] buffer = test.ToArray(); + var reader = new MemoryReader(buffer); + clone = TransactionAttribute.DeserializeFrom(ref reader) as HighPriorityAttribute; Assert.AreEqual(clone.Type, test.Type); // Wrong type - msRead.Seek(0, SeekOrigin.Begin); - msRead.WriteByte(0xff); - msRead.Seek(0, SeekOrigin.Begin); - Assert.ThrowsException(() => TransactionAttribute.DeserializeFrom(reader)); - msRead.Seek(0, SeekOrigin.Begin); - Assert.ThrowsException(() => new HighPriorityAttribute().Deserialize(reader)); + buffer[0] = 0xff; + reader = new MemoryReader(buffer); + try + { + TransactionAttribute.DeserializeFrom(ref reader); + Assert.Fail(); + } + catch (FormatException) { } + reader = new MemoryReader(buffer); + try + { + new HighPriorityAttribute().Deserialize(ref reader); + Assert.Fail(); + } + catch (FormatException) { } } [TestMethod] diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_InvPayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_InvPayload.cs similarity index 82% rename from tests/neo.UnitTests/Network/P2P/Payloads/UT_InvPayload.cs rename to tests/Neo.UnitTests/Network/P2P/Payloads/UT_InvPayload.cs index 9bd5127af9..4735fddfe1 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_InvPayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_InvPayload.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_InvPayload.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs similarity index 68% rename from tests/neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs rename to tests/Neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs index 92a7d3eda1..f491d46749 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs @@ -1,7 +1,19 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_MerkleBlockPayload.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.Network.P2P.Payloads; +using System; using System.Collections; namespace Neo.UnitTests.Network.P2P.Payloads @@ -28,7 +40,7 @@ public void DeserializeAndSerialize() Assert.AreEqual(test.TxCount, clone.TxCount); Assert.AreEqual(test.Hashes.Length, clone.TxCount); CollectionAssert.AreEqual(test.Hashes, clone.Hashes); - CollectionAssert.AreEqual(test.Flags, clone.Flags); + Assert.IsTrue(test.Flags.Span.SequenceEqual(clone.Flags.Span)); } } } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_NetworkAddressWithTime.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_NetworkAddressWithTime.cs similarity index 80% rename from tests/neo.UnitTests/Network/P2P/Payloads/UT_NetworkAddressWithTime.cs rename to tests/Neo.UnitTests/Network/P2P/Payloads/UT_NetworkAddressWithTime.cs index 239896acf8..2ed86e4da7 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_NetworkAddressWithTime.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_NetworkAddressWithTime.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_NetworkAddressWithTime.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_NotValidBefore.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_NotValidBefore.cs new file mode 100644 index 0000000000..0b51550f83 --- /dev/null +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_NotValidBefore.cs @@ -0,0 +1,86 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_NotValidBefore.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 FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract.Native; +using System; + +namespace Neo.UnitTests.Network.P2P.Payloads +{ + [TestClass] + public class UT_NotValidBefore + { + [TestMethod] + public void Size_Get() + { + var test = new NotValidBefore(); + test.Size.Should().Be(5); + } + + [TestMethod] + public void ToJson() + { + var test = new NotValidBefore(); + test.Height = 42; + var json = test.ToJson().ToString(); + Assert.AreEqual(@"{""type"":""NotValidBefore"",""height"":42}", json); + } + + [TestMethod] + public void DeserializeAndSerialize() + { + var test = new NotValidBefore(); + + var clone = test.ToArray().AsSerializable(); + Assert.AreEqual(clone.Type, test.Type); + + // As transactionAttribute + + byte[] buffer = test.ToArray(); + var reader = new MemoryReader(buffer); + clone = TransactionAttribute.DeserializeFrom(ref reader) as NotValidBefore; + Assert.AreEqual(clone.Type, test.Type); + + // Wrong type + + buffer[0] = 0xff; + reader = new MemoryReader(buffer); + try + { + TransactionAttribute.DeserializeFrom(ref reader); + Assert.Fail(); + } + catch (FormatException) { } + reader = new MemoryReader(buffer); + try + { + new NotValidBefore().Deserialize(ref reader); + Assert.Fail(); + } + catch (FormatException) { } + } + + [TestMethod] + public void Verify() + { + var test = new NotValidBefore(); + var snapshot = TestBlockchain.GetTestSnapshot(); + test.Height = NativeContract.Ledger.CurrentIndex(snapshot) + 1; + + Assert.IsFalse(test.Verify(snapshot, new Transaction())); + test.Height--; + Assert.IsTrue(test.Verify(snapshot, new Transaction())); + } + } +} diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Signers.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Signers.cs similarity index 95% rename from tests/neo.UnitTests/Network/P2P/Payloads/UT_Signers.cs rename to tests/Neo.UnitTests/Network/P2P/Payloads/UT_Signers.cs index 8b1adae4ee..44d337fd1c 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Signers.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Signers.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Signers.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs similarity index 91% rename from tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs rename to tests/Neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 2e5980e66a..43755374d3 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -1,8 +1,19 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Transaction.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract; @@ -10,7 +21,7 @@ using Neo.VM; using Neo.Wallets; using System; -using System.IO; +using System.Collections.Generic; using System.Linq; using System.Numerics; @@ -27,10 +38,16 @@ public void TestSetup() uut = new Transaction(); } + [TestCleanup] + public void Clean() + { + TestBlockchain.ResetStore(); + } + [TestMethod] public void Script_Get() { - uut.Script.Should().BeNull(); + uut.Script.IsEmpty.Should().BeTrue(); } [TestMethod] @@ -57,10 +74,11 @@ public void Script_Set() { byte[] val = TestUtils.GetByteArray(32, 0x42); uut.Script = val; - uut.Script.Length.Should().Be(32); + var span = uut.Script.Span; + span.Length.Should().Be(32); for (int i = 0; i < val.Length; i++) { - uut.Script[i].Should().Be(val[i]); + span[i].Should().Be(val[i]); } } @@ -102,12 +120,10 @@ public void Size_Get() [TestMethod] public void FeeIsMultiSigContract() { - var walletA = TestUtils.GenerateTestWallet(); - var walletB = TestUtils.GenerateTestWallet(); + var walletA = TestUtils.GenerateTestWallet("123"); + var walletB = TestUtils.GenerateTestWallet("123"); var snapshot = TestBlockchain.GetTestSnapshot(); - using var unlockA = walletA.Unlock("123"); - using var unlockB = walletB.Unlock("123"); var a = walletA.CreateAccount(); var b = walletB.CreateAccount(); @@ -146,10 +162,10 @@ public void FeeIsMultiSigContract() // Sign - var wrongData = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network + 1); + var wrongData = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network + 1); Assert.IsFalse(walletA.Sign(wrongData)); - var data = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + var data = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); Assert.IsTrue(walletA.Sign(data)); Assert.IsTrue(walletB.Sign(data)); Assert.IsTrue(data.Completed); @@ -158,7 +174,7 @@ public void FeeIsMultiSigContract() // Fast check - Assert.IsTrue(tx.VerifyWitnesses(ProtocolSettings.Default, snapshot, tx.NetworkFee)); + Assert.IsTrue(tx.VerifyWitnesses(TestProtocolSettings.Default, snapshot, tx.NetworkFee)); // Check @@ -183,10 +199,8 @@ public void FeeIsMultiSigContract() [TestMethod] public void FeeIsSignatureContractDetailed() { - var wallet = TestUtils.GenerateTestWallet(); + var wallet = TestUtils.GenerateTestWallet("123"); var snapshot = TestBlockchain.GetTestSnapshot(); - - using var unlock = wallet.Unlock("123"); var acc = wallet.CreateAccount(); // Fake balance @@ -222,7 +236,7 @@ public void FeeIsSignatureContractDetailed() // Sign // ---- - var data = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + var data = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); // 'from' is always required as witness // if not included on cosigner with a scope, its scope should be considered 'CalledByEntry' data.ScriptHashes.Count.Should().Be(1); @@ -236,7 +250,7 @@ public void FeeIsSignatureContractDetailed() // Fast check - Assert.IsTrue(tx.VerifyWitnesses(ProtocolSettings.Default, snapshot, tx.NetworkFee)); + Assert.IsTrue(tx.VerifyWitnesses(TestProtocolSettings.Default, snapshot, tx.NetworkFee)); // Check @@ -288,11 +302,8 @@ public void FeeIsSignatureContractDetailed() [TestMethod] public void FeeIsSignatureContract_TestScope_Global() { - var wallet = TestUtils.GenerateTestWallet(); + var wallet = TestUtils.GenerateTestWallet(""); var snapshot = TestBlockchain.GetTestSnapshot(); - - // no password on this wallet - using var unlock = wallet.Unlock(""); var acc = wallet.CreateAccount(); // Fake balance @@ -336,7 +347,7 @@ public void FeeIsSignatureContract_TestScope_Global() // Sign // ---- - var data = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + var data = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); bool signed = wallet.Sign(data); Assert.IsTrue(signed); @@ -345,7 +356,7 @@ public void FeeIsSignatureContract_TestScope_Global() tx.Witnesses.Length.Should().Be(1); // Fast check - Assert.IsTrue(tx.VerifyWitnesses(ProtocolSettings.Default, snapshot, tx.NetworkFee)); + Assert.IsTrue(tx.VerifyWitnesses(TestProtocolSettings.Default, snapshot, tx.NetworkFee)); // Check long verificationGas = 0; @@ -370,11 +381,8 @@ public void FeeIsSignatureContract_TestScope_Global() [TestMethod] public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() { - var wallet = TestUtils.GenerateTestWallet(); + var wallet = TestUtils.GenerateTestWallet(""); var snapshot = TestBlockchain.GetTestSnapshot(); - - // no password on this wallet - using var unlock = wallet.Unlock(""); var acc = wallet.CreateAccount(); // Fake balance @@ -419,7 +427,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() // Sign // ---- - var data = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + var data = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); bool signed = wallet.Sign(data); Assert.IsTrue(signed); @@ -428,7 +436,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() tx.Witnesses.Length.Should().Be(1); // Fast check - Assert.IsTrue(tx.VerifyWitnesses(ProtocolSettings.Default, snapshot, tx.NetworkFee)); + Assert.IsTrue(tx.VerifyWitnesses(TestProtocolSettings.Default, snapshot, tx.NetworkFee)); // Check long verificationGas = 0; @@ -453,11 +461,8 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() [TestMethod] public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() { - var wallet = TestUtils.GenerateTestWallet(); + var wallet = TestUtils.GenerateTestWallet(""); var snapshot = TestBlockchain.GetTestSnapshot(); - - // no password on this wallet - using var unlock = wallet.Unlock(""); var acc = wallet.CreateAccount(); // Fake balance @@ -505,7 +510,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() // Sign // ---- - var data = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + var data = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); bool signed = wallet.Sign(data); Assert.IsTrue(signed); @@ -514,7 +519,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() tx.Witnesses.Length.Should().Be(1); // Fast check - Assert.IsTrue(tx.VerifyWitnesses(ProtocolSettings.Default, snapshot, tx.NetworkFee)); + Assert.IsTrue(tx.VerifyWitnesses(TestProtocolSettings.Default, snapshot, tx.NetworkFee)); // Check long verificationGas = 0; @@ -539,11 +544,8 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() [TestMethod] public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_FAULT() { - var wallet = TestUtils.GenerateTestWallet(); + var wallet = TestUtils.GenerateTestWallet(""); var snapshot = TestBlockchain.GetTestSnapshot(); - - // no password on this wallet - using var unlock = wallet.Unlock(""); var acc = wallet.CreateAccount(); // Fake balance @@ -587,11 +589,8 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_FAULT() [TestMethod] public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() { - var wallet = TestUtils.GenerateTestWallet(); + var wallet = TestUtils.GenerateTestWallet(""); var snapshot = TestBlockchain.GetTestSnapshot(); - - // no password on this wallet - using var unlock = wallet.Unlock(""); var acc = wallet.CreateAccount(); // Fake balance @@ -636,7 +635,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() // Sign // ---- - var data = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + var data = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); bool signed = wallet.Sign(data); Assert.IsTrue(signed); @@ -650,7 +649,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() tx.Signers.Length.Should().Be(1); // Fast check - Assert.IsTrue(tx.VerifyWitnesses(ProtocolSettings.Default, snapshot, tx.NetworkFee)); + Assert.IsTrue(tx.VerifyWitnesses(TestProtocolSettings.Default, snapshot, tx.NetworkFee)); // Check long verificationGas = 0; @@ -675,11 +674,8 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() [TestMethod] public void FeeIsSignatureContract_TestScope_NoScopeFAULT() { - var wallet = TestUtils.GenerateTestWallet(); + var wallet = TestUtils.GenerateTestWallet(""); var snapshot = TestBlockchain.GetTestSnapshot(); - - // no password on this wallet - using var unlock = wallet.Unlock(""); var acc = wallet.CreateAccount(); // Fake balance @@ -724,11 +720,8 @@ public void FeeIsSignatureContract_TestScope_NoScopeFAULT() [TestMethod] public void FeeIsSignatureContract_UnexistingVerificationContractFAULT() { - var wallet = TestUtils.GenerateTestWallet(); + var wallet = TestUtils.GenerateTestWallet(""); var snapshot = TestBlockchain.GetTestSnapshot(); - - // no password on this wallet - using var unlock = wallet.Unlock(""); var acc = wallet.CreateAccount(); // Fake balance @@ -762,7 +755,7 @@ public void FeeIsSignatureContract_UnexistingVerificationContractFAULT() } }; // creating new wallet with missing account for test - var walletWithoutAcc = TestUtils.GenerateTestWallet(); + var walletWithoutAcc = TestUtils.GenerateTestWallet(""); // using this... @@ -782,7 +775,7 @@ public void Transaction_Reverify_Hashes_Length_Unequal_To_Witnesses_Length() { Version = 0x00, Nonce = 0x01020304, - SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS + SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS NetworkFee = 0x0000000000000001, ValidUntilBlock = 0x01020304, Attributes = Array.Empty(), @@ -798,7 +791,7 @@ public void Transaction_Reverify_Hashes_Length_Unequal_To_Witnesses_Length() }; UInt160[] hashes = txSimple.GetScriptHashesForVerifying(snapshot); Assert.AreEqual(1, hashes.Length); - Assert.AreNotEqual(VerifyResult.Succeed, txSimple.VerifyStateDependent(ProtocolSettings.Default, snapshot, new TransactionVerificationContext())); + Assert.AreNotEqual(VerifyResult.Succeed, txSimple.VerifyStateDependent(TestProtocolSettings.Default, snapshot, new TransactionVerificationContext(), new List())); } [TestMethod] @@ -809,7 +802,7 @@ public void Transaction_Serialize_Deserialize_Simple() { Version = 0x00, Nonce = 0x01020304, - SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS + SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS NetworkFee = 0x0000000000000001, ValidUntilBlock = 0x01020304, Signers = new Signer[] { new Signer() { Account = UInt160.Zero } }, @@ -826,7 +819,7 @@ public void Transaction_Serialize_Deserialize_Simple() "04030201" + // nonce "00e1f50500000000" + // system fee (1 GAS) "0100000000000000" + // network fee (1 satoshi) - "04030201" + // timelimit + "04030201" + // timelimit "01000000000000000000000000000000000000000000" + // empty signer "00" + // no attributes "0111" + // push1 script @@ -852,8 +845,9 @@ public void Transaction_Serialize_Deserialize_Simple() Rules = Array.Empty() } }); - tx2.Script.Should().BeEquivalentTo(new byte[] { (byte)OpCode.PUSH1 }); - tx2.Witnesses.Should().BeEquivalentTo(new Witness[] { new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() } }); + tx2.Script.Span.SequenceEqual(new byte[] { (byte)OpCode.PUSH1 }).Should().BeTrue(); + tx2.Witnesses[0].InvocationScript.Span.IsEmpty.Should().BeTrue(); + tx2.Witnesses[0].VerificationScript.Span.IsEmpty.Should().BeTrue(); } [TestMethod] @@ -865,7 +859,7 @@ public void Transaction_Serialize_Deserialize_DistinctCosigners() { Version = 0x00, Nonce = 0x01020304, - SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS + SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS NetworkFee = 0x0000000000000001, ValidUntilBlock = 0x01020304, Attributes = Array.Empty(), @@ -927,7 +921,7 @@ public void Transaction_Serialize_Deserialize_MaxSizeCosigners() { Version = 0x00, Nonce = 0x01020304, - SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS + SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS NetworkFee = 0x0000000000000001, ValidUntilBlock = 0x01020304, Attributes = Array.Empty(), @@ -960,7 +954,7 @@ public void Transaction_Serialize_Deserialize_MaxSizeCosigners() { Version = 0x00, Nonce = 0x01020304, - SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS + SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS NetworkFee = 0x0000000000000001, ValidUntilBlock = 0x01020304, Attributes = Array.Empty(), @@ -987,11 +981,8 @@ public void FeeIsSignatureContract_TestScope_FeeOnly_Default() Signer cosigner = new(); cosigner.Scopes.Should().Be(WitnessScope.None); - var wallet = TestUtils.GenerateTestWallet(); + var wallet = TestUtils.GenerateTestWallet(""); var snapshot = TestBlockchain.GetTestSnapshot(); - - // no password on this wallet - using var unlock = wallet.Unlock(""); var acc = wallet.CreateAccount(); // Fake balance @@ -1038,7 +1029,7 @@ public void FeeIsSignatureContract_TestScope_FeeOnly_Default() // Sign // ---- - var data = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + var data = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); bool signed = wallet.Sign(data); Assert.IsTrue(signed); @@ -1047,7 +1038,7 @@ public void FeeIsSignatureContract_TestScope_FeeOnly_Default() tx.Witnesses.Length.Should().Be(1); // Fast check - Assert.IsTrue(tx.VerifyWitnesses(ProtocolSettings.Default, snapshot, tx.NetworkFee)); + Assert.IsTrue(tx.VerifyWitnesses(TestProtocolSettings.Default, snapshot, tx.NetworkFee)); // Check long verificationGas = 0; @@ -1143,16 +1134,14 @@ public void Test_VerifyStateIndependent() } } }; - tx.VerifyStateIndependent(ProtocolSettings.Default).Should().Be(VerifyResult.OverSize); + tx.VerifyStateIndependent(TestProtocolSettings.Default).Should().Be(VerifyResult.OverSize); tx.Script = Array.Empty(); - tx.VerifyStateIndependent(ProtocolSettings.Default).Should().Be(VerifyResult.Succeed); + tx.VerifyStateIndependent(TestProtocolSettings.Default).Should().Be(VerifyResult.Succeed); - var walletA = TestUtils.GenerateTestWallet(); - var walletB = TestUtils.GenerateTestWallet(); + var walletA = TestUtils.GenerateTestWallet("123"); + var walletB = TestUtils.GenerateTestWallet("123"); var snapshot = TestBlockchain.GetTestSnapshot(); - using var unlockA = walletA.Unlock("123"); - using var unlockB = walletB.Unlock("123"); var a = walletA.CreateAccount(); var b = walletB.CreateAccount(); @@ -1189,13 +1178,13 @@ public void Test_VerifyStateIndependent() // Sign - var data = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + var data = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); Assert.IsTrue(walletA.Sign(data)); Assert.IsTrue(walletB.Sign(data)); Assert.IsTrue(data.Completed); tx.Witnesses = data.GetWitnesses(); - tx.VerifyStateIndependent(ProtocolSettings.Default).Should().Be(VerifyResult.Succeed); + tx.VerifyStateIndependent(TestProtocolSettings.Default).Should().Be(VerifyResult.Succeed); // Different hash @@ -1204,7 +1193,7 @@ public void Test_VerifyStateIndependent() VerificationScript = walletB.GetAccounts().First().Contract.Script, InvocationScript = tx.Witnesses[0].InvocationScript.ToArray() }; - tx.VerifyStateIndependent(ProtocolSettings.Default).Should().Be(VerifyResult.Invalid); + tx.VerifyStateIndependent(TestProtocolSettings.Default).Should().Be(VerifyResult.Invalid); } [TestMethod] @@ -1233,17 +1222,16 @@ public void Test_VerifyStateDependent() var key = NativeContract.GAS.CreateStorageKey(20, tx.Sender); var balance = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); balance.GetInteroperable().Balance = tx.NetworkFee; + var conflicts = new List(); - tx.VerifyStateDependent(ProtocolSettings.Default, snapshot, new TransactionVerificationContext()).Should().Be(VerifyResult.Invalid); + tx.VerifyStateDependent(ProtocolSettings.Default, snapshot, new TransactionVerificationContext(), conflicts).Should().Be(VerifyResult.Invalid); balance.GetInteroperable().Balance = 0; tx.SystemFee = 10; - tx.VerifyStateDependent(ProtocolSettings.Default, snapshot, new TransactionVerificationContext()).Should().Be(VerifyResult.InsufficientFunds); + tx.VerifyStateDependent(ProtocolSettings.Default, snapshot, new TransactionVerificationContext(), conflicts).Should().Be(VerifyResult.InsufficientFunds); - var walletA = TestUtils.GenerateTestWallet(); - var walletB = TestUtils.GenerateTestWallet(); + var walletA = TestUtils.GenerateTestWallet("123"); + var walletB = TestUtils.GenerateTestWallet("123"); - using var unlockA = walletA.Unlock("123"); - using var unlockB = walletB.Unlock("123"); var a = walletA.CreateAccount(); var b = walletB.CreateAccount(); @@ -1279,13 +1267,13 @@ public void Test_VerifyStateDependent() // Sign - var data = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + var data = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); Assert.IsTrue(walletA.Sign(data)); Assert.IsTrue(walletB.Sign(data)); Assert.IsTrue(data.Completed); tx.Witnesses = data.GetWitnesses(); - tx.VerifyStateDependent(ProtocolSettings.Default, snapshot, new TransactionVerificationContext()).Should().Be(VerifyResult.Succeed); + tx.VerifyStateDependent(TestProtocolSettings.Default, snapshot, new TransactionVerificationContext(), new List()).Should().Be(VerifyResult.Succeed); } [TestMethod] @@ -1295,7 +1283,8 @@ public void Test_VerifyStateInDependent_Multi() "AHXd31W0NlsAAAAAAJRGawAAAAAA3g8CAAGSs5x3qmDym1fBc87ZF/F/0yGm6wEAXwsDAOQLVAIAAAAMFLqZBJj+L0XZPXNHHM9MBfCza5HnDBSSs5x3qmDym1fBc87ZF/F/0yGm6xTAHwwIdHJhbnNmZXIMFM924ovQBixKR47jVWEBExnzz6TSQWJ9W1I5Af1KAQxAnZvOQOCdkM+j22dS5SdEncZVYVVi1F26MhheNzNImTD4Ekw5kFR6Fojs7gD57Bdeuo8tLS1UXpzflmKcQ3pniAxAYvGgxtokrk6PVdduxCBwVbdfie+ZxiaDsjK0FYregl24cDr2v5cTLHrURVfJJ1is+4G6Jaer7nB1JrDrw+Qt6QxATA5GdR4rKFPPPQQ24+42OP2tz0HylG1LlANiOtIdag3ZPkUfZiBfEGoOteRD1O0UnMdJP4Su7PFhDuCdHu4MlwxAuGFEk2m/rdruleBGYz8DIzExJtwb/TsFxZdHxo4VV8ktv2Nh71Fwhg2bhW2tq8hV6RK2GFXNAU72KAgf/Qv6BQxA0j3srkwY333KvGNtw7ZvSG8X36Tqu000CEtDx4SMOt8qhVYGMr9PClsUVcYFHdrJaodilx8ewXDHNIq+OnS7SfwVDCEDAJt1QOEPJWLl/Y+snq7CUWaliybkEjSP9ahpJ7+sIqIMIQMCBenO+upaHfxYCvIMjVqiRouwFI8aXkYF/GIsgOYEugwhAhS68M7qOmbxfn4eg56iX9i+1s2C5rtuaCUBiQZfRP8BDCECPpsy6om5TQZuZJsST9UOOW7pE2no4qauGxHBcNAiJW0MIQNAjc1BY5b2R4OsWH6h4Vk8V9n+qIDIpqGSDpKiWUd4BgwhAqeDS+mzLimB0VfLW706y0LP0R6lw7ECJNekTpjFkQ8bDCECuixw9ZlvNXpDGYcFhZ+uLP6hPhFyligAdys9WIqdSr0XQZ7Q3Do="); var tx = new Transaction(); - ((ISerializable)tx).Deserialize(new BinaryReader(new MemoryStream(txData))); + MemoryReader reader = new(txData); + ((ISerializable)tx).Deserialize(ref reader); var settings = new ProtocolSettings() { Network = 844378958 }; var result = tx.VerifyStateIndependent(settings); diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_VersionPayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_VersionPayload.cs similarity index 80% rename from tests/neo.UnitTests/Network/P2P/Payloads/UT_VersionPayload.cs rename to tests/Neo.UnitTests/Network/P2P/Payloads/UT_VersionPayload.cs index 30fdb1f5a8..a77740a1e5 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_VersionPayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_VersionPayload.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_VersionPayload.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs similarity index 83% rename from tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs rename to tests/Neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs index 57d14d7e12..74898eb4fb 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs @@ -1,7 +1,18 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Witness.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.Wallets; @@ -25,20 +36,18 @@ public void TestSetup() [TestMethod] public void InvocationScript_Get() { - uut.InvocationScript.Should().BeNull(); + uut.InvocationScript.IsEmpty.Should().BeTrue(); } private static Witness PrepareDummyWitness(int pubKeys, int m) { var address = new WalletAccount[pubKeys]; var wallets = new NEP6Wallet[pubKeys]; - var walletsUnlocks = new IDisposable[pubKeys]; var snapshot = TestBlockchain.GetTestSnapshot(); for (int x = 0; x < pubKeys; x++) { - wallets[x] = TestUtils.GenerateTestWallet(); - walletsUnlocks[x] = wallets[x].Unlock("123"); + wallets[x] = TestUtils.GenerateTestWallet("123"); address[x] = wallets[x].CreateAccount(); } @@ -68,7 +77,7 @@ private static Witness PrepareDummyWitness(int pubKeys, int m) ValidUntilBlock = 0, Version = 0, Witnesses = Array.Empty() - }, ProtocolSettings.Default.Network); + }, TestProtocolSettings.Default.Network); for (int x = 0; x < m; x++) { @@ -92,8 +101,8 @@ public void MaxSize_OK() var copy = witness.ToArray().AsSerializable(); - CollectionAssert.AreEqual(witness.InvocationScript, copy.InvocationScript); - CollectionAssert.AreEqual(witness.VerificationScript, copy.VerificationScript); + Assert.IsTrue(witness.InvocationScript.Span.SequenceEqual(copy.InvocationScript.Span)); + Assert.IsTrue(witness.VerificationScript.Span.SequenceEqual(copy.VerificationScript.Span)); } [TestMethod] @@ -122,7 +131,7 @@ public void InvocationScript_Set() byte[] dataArray = new byte[] { 0, 32, 32, 20, 32, 32 }; uut.InvocationScript = dataArray; uut.InvocationScript.Length.Should().Be(6); - Assert.AreEqual(uut.InvocationScript.ToHexString(), "002020142020"); + Assert.AreEqual(uut.InvocationScript.Span.ToHexString(), "002020142020"); } private static void SetupWitnessWithValues(Witness uut, int lenghtInvocation, int lengthVerification, out byte[] invocationScript, out byte[] verificationScript) diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_WitnessContition.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_WitnessContition.cs similarity index 82% rename from tests/neo.UnitTests/Network/P2P/Payloads/UT_WitnessContition.cs rename to tests/Neo.UnitTests/Network/P2P/Payloads/UT_WitnessContition.cs index 4078d504ff..113d667296 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_WitnessContition.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_WitnessContition.cs @@ -1,6 +1,17 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_WitnessContition.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.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; -using Neo.IO.Json; +using Neo.Json; using Neo.Network.P2P.Payloads.Conditions; namespace Neo.UnitTests.Network.P2P.Payloads @@ -22,7 +33,7 @@ public void TestFromJson1() } }; var json = condition.ToJson(); - var new_condi = WitnessCondition.FromJson(json); + var new_condi = WitnessCondition.FromJson(json, 2); Assert.IsTrue(new_condi is OrCondition); var or_condi = (OrCondition)new_condi; Assert.AreEqual(2, or_condi.Expressions.Length); @@ -41,8 +52,8 @@ public void TestFromJson2() var hash1 = UInt160.Zero; var hash2 = UInt160.Parse("0xd2a4cff31913016155e38e474a2c06d08be276cf"); var jstr = "{\"type\":\"Or\",\"expressions\":[{\"type\":\"And\",\"expressions\":[{\"type\":\"CalledByContract\",\"hash\":\"0x0000000000000000000000000000000000000000\"},{\"type\":\"ScriptHash\",\"hash\":\"0xd2a4cff31913016155e38e474a2c06d08be276cf\"}]},{\"type\":\"Or\",\"expressions\":[{\"type\":\"CalledByGroup\",\"group\":\"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c\"},{\"type\":\"Boolean\",\"expression\":true}]}]}"; - var json = JObject.Parse(jstr); - var condi = WitnessCondition.FromJson(json); + var json = (JObject)JToken.Parse(jstr); + var condi = WitnessCondition.FromJson(json, 2); var or_condi = (OrCondition)condi; Assert.AreEqual(2, or_condi.Expressions.Length); var and_condi = (AndCondition)or_condi.Expressions[0]; diff --git a/tests/neo.UnitTests/Network/P2P/UT_ChannelsConfig.cs b/tests/Neo.UnitTests/Network/P2P/UT_ChannelsConfig.cs similarity index 63% rename from tests/neo.UnitTests/Network/P2P/UT_ChannelsConfig.cs rename to tests/Neo.UnitTests/Network/P2P/UT_ChannelsConfig.cs index 5613364319..5180f33c88 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_ChannelsConfig.cs +++ b/tests/Neo.UnitTests/Network/P2P/UT_ChannelsConfig.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ChannelsConfig.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Network.P2P; @@ -14,17 +25,16 @@ public void CreateTest() var config = new ChannelsConfig(); config.Tcp.Should().BeNull(); - config.WebSocket.Should().BeNull(); config.MinDesiredConnections.Should().Be(10); config.MaxConnections.Should().Be(40); config.MaxConnectionsPerAddress.Should().Be(3); - config.Tcp = config.WebSocket = new IPEndPoint(IPAddress.Any, 21); + config.Tcp = config.Tcp = new IPEndPoint(IPAddress.Any, 21); config.MaxConnectionsPerAddress++; config.MaxConnections++; config.MinDesiredConnections++; - config.Tcp.Should().BeSameAs(config.WebSocket); + config.Tcp.Should().BeSameAs(config.Tcp); config.Tcp.Address.Should().BeEquivalentTo(IPAddress.Any); config.Tcp.Port.Should().Be(21); config.MinDesiredConnections.Should().Be(11); diff --git a/tests/neo.UnitTests/Network/P2P/UT_LocalNode.cs b/tests/Neo.UnitTests/Network/P2P/UT_LocalNode.cs similarity index 74% rename from tests/neo.UnitTests/Network/P2P/UT_LocalNode.cs rename to tests/Neo.UnitTests/Network/P2P/UT_LocalNode.cs index 1e4da7590b..cd307cdc51 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_LocalNode.cs +++ b/tests/Neo.UnitTests/Network/P2P/UT_LocalNode.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_LocalNode.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 Akka.TestKit.Xunit2; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Network.P2P; @@ -26,7 +37,6 @@ public void TestDefaults() var localnode = senderProbe.ExpectMsg(); Assert.AreEqual(0, localnode.ListenerTcpPort); - Assert.AreEqual(0, localnode.ListenerWsPort); Assert.AreEqual(3, localnode.MaxConnectionsPerAddress); Assert.AreEqual(10, localnode.MinDesiredConnections); Assert.AreEqual(40, localnode.MaxConnections); diff --git a/tests/neo.UnitTests/Network/P2P/UT_Message.cs b/tests/Neo.UnitTests/Network/P2P/UT_Message.cs similarity index 82% rename from tests/neo.UnitTests/Network/P2P/UT_Message.cs rename to tests/Neo.UnitTests/Network/P2P/UT_Message.cs index dbcfb652c9..35f99fc44f 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_Message.cs +++ b/tests/Neo.UnitTests/Network/P2P/UT_Message.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Message.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 Akka.IO; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -31,6 +42,28 @@ public void Serialize_Deserialize() payloadCopy.Timestamp.Should().Be(payload.Timestamp); } + [TestMethod] + public void Serialize_Deserialize_WithoutPayload() + { + var msg = Message.Create(MessageCommand.GetAddr); + var buffer = msg.ToArray(); + var copy = buffer.AsSerializable(); + + copy.Command.Should().Be(msg.Command); + copy.Flags.Should().Be(msg.Flags); + copy.Payload.Should().Be(null); + } + + [TestMethod] + public void ToArray() + { + var payload = PingPayload.Create(uint.MaxValue); + var msg = Message.Create(MessageCommand.Ping, payload); + _ = msg.ToArray(); + + msg.Size.Should().Be(payload.Size + 3); + } + [TestMethod] public void Serialize_Deserialize_ByteString() { @@ -52,15 +85,10 @@ public void Serialize_Deserialize_ByteString() } [TestMethod] - public void Serialize_Deserialize_WithoutPayload() + public void ToArray_WithoutPayload() { var msg = Message.Create(MessageCommand.GetAddr); - var buffer = msg.ToArray(); - var copy = buffer.AsSerializable(); - - copy.Command.Should().Be(msg.Command); - copy.Flags.Should().Be(msg.Flags); - copy.Payload.Should().Be(null); + _ = msg.ToArray(); } [TestMethod] @@ -142,20 +170,19 @@ public void Compression() buffer.Length.Should().Be(56); - payload.Script = new byte[100]; - for (int i = 0; i < payload.Script.Length; i++) payload.Script[i] = (byte)OpCode.PUSH2; + byte[] script = new byte[100]; + Array.Fill(script, (byte)OpCode.PUSH2); + payload.Script = script; msg = Message.Create(MessageCommand.Transaction, payload); buffer = msg.ToArray(); buffer.Length.Should().Be(30); + msg.Flags.HasFlag(MessageFlags.Compressed).Should().BeTrue(); - var copy = buffer.AsSerializable(); - var payloadCopy = (Transaction)copy.Payload; - - copy.Command.Should().Be(msg.Command); - copy.Flags.Should().HaveFlag(MessageFlags.Compressed); + _ = Message.TryDeserialize(ByteString.CopyFrom(msg.ToArray()), out var copy); + Assert.IsNotNull(copy); - payloadCopy.ToArray().ToHexString().Should().Be(payload.ToArray().ToHexString()); + copy.Flags.HasFlag(MessageFlags.Compressed).Should().BeTrue(); } } } diff --git a/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs b/tests/Neo.UnitTests/Network/P2P/UT_RemoteNode.cs similarity index 85% rename from tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs rename to tests/Neo.UnitTests/Network/P2P/UT_RemoteNode.cs index 4e514e99a3..9f114e2708 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs +++ b/tests/Neo.UnitTests/Network/P2P/UT_RemoteNode.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_RemoteNode.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 Akka.IO; using Akka.TestKit.Xunit2; using FluentAssertions; @@ -61,7 +72,7 @@ public void RemoteNode_Test_Accept_IfSameNetwork() { UserAgent = "Unit Test".PadLeft(1024, '0'), Nonce = 1, - Network = ProtocolSettings.Default.Network, + Network = TestProtocolSettings.Default.Network, Timestamp = 5, Version = 6, Capabilities = new NodeCapability[] diff --git a/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs b/tests/Neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs similarity index 95% rename from tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs rename to tests/Neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs index a5174d6cfe..23e0dad367 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs +++ b/tests/Neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_RemoteNodeMailbox.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 Akka.IO; using Akka.TestKit.Xunit2; using FluentAssertions; diff --git a/tests/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs b/tests/Neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs similarity index 80% rename from tests/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs rename to tests/Neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs index 755bf979c1..41b4635d8c 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs +++ b/tests/Neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_TaskManagerMailbox.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 Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/tests/neo.UnitTests/Network/P2P/UT_TaskSession.cs b/tests/Neo.UnitTests/Network/P2P/UT_TaskSession.cs similarity index 70% rename from tests/neo.UnitTests/Network/P2P/UT_TaskSession.cs rename to tests/Neo.UnitTests/Network/P2P/UT_TaskSession.cs index bee2bc8efe..4614ba47aa 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_TaskSession.cs +++ b/tests/Neo.UnitTests/Network/P2P/UT_TaskSession.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_TaskSession.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.VisualStudio.TestTools.UnitTesting; using Neo.Network.P2P; using Neo.Network.P2P.Capabilities; diff --git a/tests/neo.UnitTests/Network/UT_UPnP.cs b/tests/Neo.UnitTests/Network/UT_UPnP.cs similarity index 62% rename from tests/neo.UnitTests/Network/UT_UPnP.cs rename to tests/Neo.UnitTests/Network/UT_UPnP.cs index 897d48ffa2..f63fb43d93 100644 --- a/tests/neo.UnitTests/Network/UT_UPnP.cs +++ b/tests/Neo.UnitTests/Network/UT_UPnP.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_UPnP.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.VisualStudio.TestTools.UnitTesting; using Neo.Network; using System; diff --git a/tests/neo.UnitTests/Persistence/UT_MemoryStore.cs b/tests/Neo.UnitTests/Persistence/UT_MemoryStore.cs similarity index 73% rename from tests/neo.UnitTests/Persistence/UT_MemoryStore.cs rename to tests/Neo.UnitTests/Persistence/UT_MemoryStore.cs index 7dd698e1c2..88d48ba548 100644 --- a/tests/neo.UnitTests/Persistence/UT_MemoryStore.cs +++ b/tests/Neo.UnitTests/Persistence/UT_MemoryStore.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_MemoryStore.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.VisualStudio.TestTools.UnitTesting; using Neo.Persistence; using System; @@ -8,6 +19,12 @@ namespace Neo.UnitTests.Persistence [TestClass] public class UT_MemoryStore { + [TestMethod] + public void LoadStoreTest() + { + Assert.IsInstanceOfType(TestBlockchain.TheNeoSystem.LoadStore("abc")); + } + [TestMethod] public void StoreTest() { diff --git a/tests/neo.UnitTests/Plugins/TestLogPlugin.cs b/tests/Neo.UnitTests/Plugins/TestPlugin.cs similarity index 52% rename from tests/neo.UnitTests/Plugins/TestLogPlugin.cs rename to tests/Neo.UnitTests/Plugins/TestPlugin.cs index cb73ce4106..dde500b927 100644 --- a/tests/neo.UnitTests/Plugins/TestLogPlugin.cs +++ b/tests/Neo.UnitTests/Plugins/TestPlugin.cs @@ -1,21 +1,25 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TestPlugin.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.Extensions.Configuration; using Neo.Plugins; namespace Neo.UnitTests.Plugins { - public class TestLogPlugin : Plugin, ILogPlugin + public class TestPlugin : Plugin { - public TestLogPlugin() : base() { } - - public string Output { set; get; } + public TestPlugin() : base() { } protected override void Configure() { } - void ILogPlugin.Log(string source, LogLevel level, object message) - { - Output = source + "_" + level.ToString() + "_" + message; - } - public void LogMessage(string message) { Log(message); diff --git a/tests/Neo.UnitTests/Plugins/UT_Plugin.cs b/tests/Neo.UnitTests/Plugins/UT_Plugin.cs new file mode 100644 index 0000000000..e5e8cb6e49 --- /dev/null +++ b/tests/Neo.UnitTests/Plugins/UT_Plugin.cs @@ -0,0 +1,67 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Plugin.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 FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Plugins; +using System; + +namespace Neo.UnitTests.Plugins +{ + [TestClass] + public class UT_Plugin + { + private static readonly object locker = new(); + + [TestMethod] + public void TestGetConfigFile() + { + var pp = new TestPlugin(); + var file = pp.ConfigFile; + file.EndsWith("config.json").Should().BeTrue(); + } + + [TestMethod] + public void TestGetName() + { + var pp = new TestPlugin(); + pp.Name.Should().Be("TestPlugin"); + } + + [TestMethod] + public void TestGetVersion() + { + var pp = new TestPlugin(); + Action action = () => pp.Version.ToString(); + action.Should().NotThrow(); + } + + [TestMethod] + public void TestSendMessage() + { + lock (locker) + { + Plugin.Plugins.Clear(); + Plugin.SendMessage("hey1").Should().BeFalse(); + + var lp = new TestPlugin(); + Plugin.SendMessage("hey2").Should().BeTrue(); + } + } + + [TestMethod] + public void TestGetConfiguration() + { + var pp = new TestPlugin(); + pp.TestGetConfiguration().Key.Should().Be("PluginConfiguration"); + } + } +} diff --git a/tests/neo.UnitTests/README.md b/tests/Neo.UnitTests/README.md similarity index 100% rename from tests/neo.UnitTests/README.md rename to tests/Neo.UnitTests/README.md diff --git a/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs b/tests/Neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs similarity index 68% rename from tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs rename to tests/Neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs index 3d68ebe791..16f6b11973 100644 --- a/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs +++ b/tests/Neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_StorageIterator.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; @@ -14,7 +25,7 @@ public class UT_StorageIterator [TestMethod] public void TestGeneratorAndDispose() { - StorageIterator storageIterator = new(new List<(StorageKey, StorageItem)>().GetEnumerator(), 0, FindOptions.None, null); + StorageIterator storageIterator = new(new List<(StorageKey, StorageItem)>().GetEnumerator(), 0, FindOptions.None); Assert.IsNotNull(storageIterator); Action action = () => storageIterator.Dispose(); action.Should().NotThrow(); @@ -33,9 +44,9 @@ public void TestKeyAndValueAndNext() Value = new byte[1] }; list.Add((storageKey, storageItem)); - StorageIterator storageIterator = new(list.GetEnumerator(), 0, FindOptions.ValuesOnly, null); + StorageIterator storageIterator = new(list.GetEnumerator(), 0, FindOptions.ValuesOnly); storageIterator.Next(); - Assert.AreEqual(new ByteString(new byte[1]), storageIterator.Value()); + Assert.AreEqual(new ByteString(new byte[1]), storageIterator.Value(null)); } } } diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractEventDescriptor.cs b/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractEventDescriptor.cs similarity index 61% rename from tests/neo.UnitTests/SmartContract/Manifest/UT_ContractEventDescriptor.cs rename to tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractEventDescriptor.cs index a829354e34..c04f4a009e 100644 --- a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractEventDescriptor.cs +++ b/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractEventDescriptor.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ContractEventDescriptor.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.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract.Manifest; diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs b/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs similarity index 82% rename from tests/neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs rename to tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs index 27094d8cef..e2715d8ceb 100644 --- a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs +++ b/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ContractGroup.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.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using Neo.SmartContract; diff --git a/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs b/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs new file mode 100644 index 0000000000..4e2e866281 --- /dev/null +++ b/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs @@ -0,0 +1,178 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ContractManifest.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.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.Json; +using Neo.SmartContract; +using Neo.SmartContract.Manifest; +using Neo.VM; +using System; + +namespace Neo.UnitTests.SmartContract.Manifest +{ + [TestClass] + public class UT_ContractManifest + { + [TestMethod] + public void TestMainnetContract() + { + // 0x6f1837723768f27a6f6a14452977e3e0e264f2cc in mainnet + var json = @"{""name"":""Test"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""a"",""parameters"":[{""name"":""amountIn"",""type"":""Integer""}],""returntype"":""Void"",""offset"":0,""safe"":false},{""name"":""a0"",""parameters"":[],""returntype"":""Integer"",""offset"":77,""safe"":false},{""name"":""a10"",""parameters"":[],""returntype"":""Void"",""offset"":378,""safe"":false},{""name"":""a11"",""parameters"":[],""returntype"":""Void"",""offset"":398,""safe"":false},{""name"":""a12"",""parameters"":[],""returntype"":""Void"",""offset"":418,""safe"":false},{""name"":""a13"",""parameters"":[],""returntype"":""Void"",""offset"":438,""safe"":false},{""name"":""a14"",""parameters"":[],""returntype"":""Void"",""offset"":458,""safe"":false},{""name"":""a15"",""parameters"":[],""returntype"":""Void"",""offset"":478,""safe"":false},{""name"":""a16"",""parameters"":[],""returntype"":""Void"",""offset"":498,""safe"":false},{""name"":""a17"",""parameters"":[],""returntype"":""Void"",""offset"":518,""safe"":false},{""name"":""a18"",""parameters"":[],""returntype"":""Void"",""offset"":539,""safe"":false},{""name"":""a19"",""parameters"":[],""returntype"":""Void"",""offset"":560,""safe"":false},{""name"":""a20"",""parameters"":[],""returntype"":""Void"",""offset"":581,""safe"":false},{""name"":""a21"",""parameters"":[],""returntype"":""Void"",""offset"":602,""safe"":false},{""name"":""a22"",""parameters"":[],""returntype"":""Void"",""offset"":623,""safe"":false},{""name"":""a23"",""parameters"":[],""returntype"":""Void"",""offset"":644,""safe"":false},{""name"":""a24"",""parameters"":[],""returntype"":""Void"",""offset"":665,""safe"":false},{""name"":""a25"",""parameters"":[],""returntype"":""Void"",""offset"":686,""safe"":false},{""name"":""a26"",""parameters"":[],""returntype"":""Void"",""offset"":707,""safe"":false},{""name"":""a27"",""parameters"":[],""returntype"":""Void"",""offset"":728,""safe"":false},{""name"":""a28"",""parameters"":[],""returntype"":""Void"",""offset"":749,""safe"":false},{""name"":""a29"",""parameters"":[],""returntype"":""Void"",""offset"":770,""safe"":false},{""name"":""a30"",""parameters"":[],""returntype"":""Void"",""offset"":791,""safe"":false},{""name"":""a31"",""parameters"":[],""returntype"":""Void"",""offset"":812,""safe"":false},{""name"":""a32"",""parameters"":[],""returntype"":""Void"",""offset"":833,""safe"":false},{""name"":""a33"",""parameters"":[],""returntype"":""Void"",""offset"":854,""safe"":false},{""name"":""a34"",""parameters"":[],""returntype"":""Void"",""offset"":875,""safe"":false},{""name"":""a35"",""parameters"":[],""returntype"":""Void"",""offset"":896,""safe"":false},{""name"":""a36"",""parameters"":[],""returntype"":""Void"",""offset"":917,""safe"":false},{""name"":""a37"",""parameters"":[],""returntype"":""Void"",""offset"":938,""safe"":false},{""name"":""a38"",""parameters"":[],""returntype"":""Void"",""offset"":959,""safe"":false},{""name"":""a39"",""parameters"":[],""returntype"":""Void"",""offset"":980,""safe"":false},{""name"":""a40"",""parameters"":[],""returntype"":""Void"",""offset"":1001,""safe"":false},{""name"":""a41"",""parameters"":[],""returntype"":""Void"",""offset"":1022,""safe"":false},{""name"":""a42"",""parameters"":[],""returntype"":""Void"",""offset"":1043,""safe"":false},{""name"":""a43"",""parameters"":[],""returntype"":""Void"",""offset"":1064,""safe"":false},{""name"":""a44"",""parameters"":[],""returntype"":""Void"",""offset"":1085,""safe"":false},{""name"":""a45"",""parameters"":[],""returntype"":""Void"",""offset"":1106,""safe"":false},{""name"":""a46"",""parameters"":[],""returntype"":""Void"",""offset"":1127,""safe"":false},{""name"":""a47"",""parameters"":[],""returntype"":""Void"",""offset"":1148,""safe"":false},{""name"":""a48"",""parameters"":[],""returntype"":""Void"",""offset"":1169,""safe"":false},{""name"":""a49"",""parameters"":[],""returntype"":""Void"",""offset"":1190,""safe"":false},{""name"":""a50"",""parameters"":[],""returntype"":""Void"",""offset"":1211,""safe"":false},{""name"":""b"",""parameters"":[{""name"":""amountIn"",""type"":""Integer""}],""returntype"":""Void"",""offset"":1232,""safe"":false},{""name"":""b0"",""parameters"":[],""returntype"":""Integer"",""offset"":1251,""safe"":false},{""name"":""b10"",""parameters"":[],""returntype"":""Void"",""offset"":1275,""safe"":false},{""name"":""b11"",""parameters"":[],""returntype"":""Void"",""offset"":1295,""safe"":false},{""name"":""b12"",""parameters"":[],""returntype"":""Void"",""offset"":1315,""safe"":false},{""name"":""b13"",""parameters"":[],""returntype"":""Void"",""offset"":1335,""safe"":false},{""name"":""b14"",""parameters"":[],""returntype"":""Void"",""offset"":1355,""safe"":false},{""name"":""b15"",""parameters"":[],""returntype"":""Void"",""offset"":1375,""safe"":false},{""name"":""b16"",""parameters"":[],""returntype"":""Void"",""offset"":1395,""safe"":false},{""name"":""b17"",""parameters"":[],""returntype"":""Void"",""offset"":1415,""safe"":false},{""name"":""b18"",""parameters"":[],""returntype"":""Void"",""offset"":1436,""safe"":false},{""name"":""b19"",""parameters"":[],""returntype"":""Void"",""offset"":1457,""safe"":false},{""name"":""b20"",""parameters"":[],""returntype"":""Void"",""offset"":1478,""safe"":false},{""name"":""b21"",""parameters"":[],""returntype"":""Void"",""offset"":1499,""safe"":false},{""name"":""b22"",""parameters"":[],""returntype"":""Void"",""offset"":1520,""safe"":false},{""name"":""b23"",""parameters"":[],""returntype"":""Void"",""offset"":1541,""safe"":false},{""name"":""b24"",""parameters"":[],""returntype"":""Void"",""offset"":1562,""safe"":false},{""name"":""b25"",""parameters"":[],""returntype"":""Void"",""offset"":1583,""safe"":false},{""name"":""b26"",""parameters"":[],""returntype"":""Void"",""offset"":1604,""safe"":false},{""name"":""b27"",""parameters"":[],""returntype"":""Void"",""offset"":1625,""safe"":false},{""name"":""b28"",""parameters"":[],""returntype"":""Void"",""offset"":1646,""safe"":false},{""name"":""b29"",""parameters"":[],""returntype"":""Void"",""offset"":1667,""safe"":false},{""name"":""b30"",""parameters"":[],""returntype"":""Void"",""offset"":1688,""safe"":false},{""name"":""b31"",""parameters"":[],""returntype"":""Void"",""offset"":1709,""safe"":false},{""name"":""b32"",""parameters"":[],""returntype"":""Void"",""offset"":1730,""safe"":false},{""name"":""b33"",""parameters"":[],""returntype"":""Void"",""offset"":1751,""safe"":false},{""name"":""b34"",""parameters"":[],""returntype"":""Void"",""offset"":1772,""safe"":false},{""name"":""b35"",""parameters"":[],""returntype"":""Void"",""offset"":1793,""safe"":false},{""name"":""b36"",""parameters"":[],""returntype"":""Void"",""offset"":1814,""safe"":false},{""name"":""b37"",""parameters"":[],""returntype"":""Void"",""offset"":1835,""safe"":false},{""name"":""b38"",""parameters"":[],""returntype"":""Void"",""offset"":1856,""safe"":false},{""name"":""b39"",""parameters"":[],""returntype"":""Void"",""offset"":1877,""safe"":false},{""name"":""b40"",""parameters"":[],""returntype"":""Void"",""offset"":1898,""safe"":false},{""name"":""b41"",""parameters"":[],""returntype"":""Void"",""offset"":1919,""safe"":false},{""name"":""b42"",""parameters"":[],""returntype"":""Void"",""offset"":1940,""safe"":false},{""name"":""b43"",""parameters"":[],""returntype"":""Void"",""offset"":1961,""safe"":false},{""name"":""b44"",""parameters"":[],""returntype"":""Void"",""offset"":1982,""safe"":false},{""name"":""b45"",""parameters"":[],""returntype"":""Void"",""offset"":2003,""safe"":false},{""name"":""b46"",""parameters"":[],""returntype"":""Void"",""offset"":2024,""safe"":false},{""name"":""b47"",""parameters"":[],""returntype"":""Void"",""offset"":2045,""safe"":false},{""name"":""b48"",""parameters"":[],""returntype"":""Void"",""offset"":2066,""safe"":false},{""name"":""b49"",""parameters"":[],""returntype"":""Void"",""offset"":2087,""safe"":false},{""name"":""b50"",""parameters"":[],""returntype"":""Void"",""offset"":2108,""safe"":false},{""name"":""c"",""parameters"":[{""name"":""amountIn"",""type"":""Integer""}],""returntype"":""Void"",""offset"":2129,""safe"":false},{""name"":""c0"",""parameters"":[],""returntype"":""Integer"",""offset"":2148,""safe"":false},{""name"":""c10"",""parameters"":[],""returntype"":""Void"",""offset"":2172,""safe"":false},{""name"":""c11"",""parameters"":[],""returntype"":""Void"",""offset"":2192,""safe"":false},{""name"":""c12"",""parameters"":[],""returntype"":""Void"",""offset"":2212,""safe"":false},{""name"":""c13"",""parameters"":[],""returntype"":""Void"",""offset"":2232,""safe"":false},{""name"":""c14"",""parameters"":[],""returntype"":""Void"",""offset"":2252,""safe"":false},{""name"":""c15"",""parameters"":[],""returntype"":""Void"",""offset"":2272,""safe"":false},{""name"":""c16"",""parameters"":[],""returntype"":""Void"",""offset"":2292,""safe"":false},{""name"":""c17"",""parameters"":[],""returntype"":""Void"",""offset"":2312,""safe"":false},{""name"":""c18"",""parameters"":[],""returntype"":""Void"",""offset"":2333,""safe"":false},{""name"":""c19"",""parameters"":[],""returntype"":""Void"",""offset"":2354,""safe"":false},{""name"":""c20"",""parameters"":[],""returntype"":""Void"",""offset"":2375,""safe"":false},{""name"":""c21"",""parameters"":[],""returntype"":""Void"",""offset"":2396,""safe"":false},{""name"":""c22"",""parameters"":[],""returntype"":""Void"",""offset"":2417,""safe"":false},{""name"":""c23"",""parameters"":[],""returntype"":""Void"",""offset"":2438,""safe"":false},{""name"":""c24"",""parameters"":[],""returntype"":""Void"",""offset"":2459,""safe"":false},{""name"":""c25"",""parameters"":[],""returntype"":""Void"",""offset"":2480,""safe"":false},{""name"":""c26"",""parameters"":[],""returntype"":""Void"",""offset"":2501,""safe"":false},{""name"":""c27"",""parameters"":[],""returntype"":""Void"",""offset"":2522,""safe"":false},{""name"":""c28"",""parameters"":[],""returntype"":""Void"",""offset"":2543,""safe"":false},{""name"":""c29"",""parameters"":[],""returntype"":""Void"",""offset"":2564,""safe"":false},{""name"":""c30"",""parameters"":[],""returntype"":""Void"",""offset"":2585,""safe"":false},{""name"":""c31"",""parameters"":[],""returntype"":""Void"",""offset"":2606,""safe"":false},{""name"":""c32"",""parameters"":[],""returntype"":""Void"",""offset"":2627,""safe"":false},{""name"":""c33"",""parameters"":[],""returntype"":""Void"",""offset"":2648,""safe"":false},{""name"":""c34"",""parameters"":[],""returntype"":""Void"",""offset"":2669,""safe"":false},{""name"":""c35"",""parameters"":[],""returntype"":""Void"",""offset"":2690,""safe"":false},{""name"":""c36"",""parameters"":[],""returntype"":""Void"",""offset"":2711,""safe"":false},{""name"":""c37"",""parameters"":[],""returntype"":""Void"",""offset"":2732,""safe"":false},{""name"":""c38"",""parameters"":[],""returntype"":""Void"",""offset"":2753,""safe"":false},{""name"":""c39"",""parameters"":[],""returntype"":""Void"",""offset"":2774,""safe"":false},{""name"":""c40"",""parameters"":[],""returntype"":""Void"",""offset"":2795,""safe"":false},{""name"":""c41"",""parameters"":[],""returntype"":""Void"",""offset"":2816,""safe"":false},{""name"":""c42"",""parameters"":[],""returntype"":""Void"",""offset"":2837,""safe"":false},{""name"":""c43"",""parameters"":[],""returntype"":""Void"",""offset"":2858,""safe"":false},{""name"":""c44"",""parameters"":[],""returntype"":""Void"",""offset"":2879,""safe"":false},{""name"":""c45"",""parameters"":[],""returntype"":""Void"",""offset"":2900,""safe"":false},{""name"":""c46"",""parameters"":[],""returntype"":""Void"",""offset"":2921,""safe"":false},{""name"":""c47"",""parameters"":[],""returntype"":""Void"",""offset"":2942,""safe"":false},{""name"":""c48"",""parameters"":[],""returntype"":""Void"",""offset"":2963,""safe"":false},{""name"":""c49"",""parameters"":[],""returntype"":""Void"",""offset"":2984,""safe"":false},{""name"":""c50"",""parameters"":[],""returntype"":""Void"",""offset"":3005,""safe"":false},{""name"":""verify"",""parameters"":[],""returntype"":""Boolean"",""offset"":3026,""safe"":false},{""name"":""d"",""parameters"":[{""name"":""amountIn"",""type"":""Integer""}],""returntype"":""Void"",""offset"":3039,""safe"":false},{""name"":""d0"",""parameters"":[],""returntype"":""Integer"",""offset"":3058,""safe"":false},{""name"":""d10"",""parameters"":[],""returntype"":""Void"",""offset"":3082,""safe"":false},{""name"":""d11"",""parameters"":[],""returntype"":""Void"",""offset"":3102,""safe"":false},{""name"":""d12"",""parameters"":[],""returntype"":""Void"",""offset"":3122,""safe"":false},{""name"":""d13"",""parameters"":[],""returntype"":""Void"",""offset"":3142,""safe"":false},{""name"":""d14"",""parameters"":[],""returntype"":""Void"",""offset"":3162,""safe"":false},{""name"":""d15"",""parameters"":[],""returntype"":""Void"",""offset"":3182,""safe"":false},{""name"":""d16"",""parameters"":[],""returntype"":""Void"",""offset"":3202,""safe"":false},{""name"":""d17"",""parameters"":[],""returntype"":""Void"",""offset"":3222,""safe"":false},{""name"":""d18"",""parameters"":[],""returntype"":""Void"",""offset"":3243,""safe"":false},{""name"":""d19"",""parameters"":[],""returntype"":""Void"",""offset"":3264,""safe"":false},{""name"":""d20"",""parameters"":[],""returntype"":""Void"",""offset"":3285,""safe"":false},{""name"":""d21"",""parameters"":[],""returntype"":""Void"",""offset"":3306,""safe"":false},{""name"":""d22"",""parameters"":[],""returntype"":""Void"",""offset"":3327,""safe"":false},{""name"":""d23"",""parameters"":[],""returntype"":""Void"",""offset"":3348,""safe"":false},{""name"":""d24"",""parameters"":[],""returntype"":""Void"",""offset"":3369,""safe"":false},{""name"":""d25"",""parameters"":[],""returntype"":""Void"",""offset"":3390,""safe"":false},{""name"":""d26"",""parameters"":[],""returntype"":""Void"",""offset"":3411,""safe"":false},{""name"":""d27"",""parameters"":[],""returntype"":""Void"",""offset"":3432,""safe"":false},{""name"":""d28"",""parameters"":[],""returntype"":""Void"",""offset"":3453,""safe"":false},{""name"":""d29"",""parameters"":[],""returntype"":""Void"",""offset"":3474,""safe"":false},{""name"":""d30"",""parameters"":[],""returntype"":""Void"",""offset"":3495,""safe"":false},{""name"":""d31"",""parameters"":[],""returntype"":""Void"",""offset"":3516,""safe"":false},{""name"":""d32"",""parameters"":[],""returntype"":""Void"",""offset"":3537,""safe"":false},{""name"":""d33"",""parameters"":[],""returntype"":""Void"",""offset"":3558,""safe"":false},{""name"":""d34"",""parameters"":[],""returntype"":""Void"",""offset"":3579,""safe"":false},{""name"":""d35"",""parameters"":[],""returntype"":""Void"",""offset"":3600,""safe"":false},{""name"":""d36"",""parameters"":[],""returntype"":""Void"",""offset"":3621,""safe"":false},{""name"":""d37"",""parameters"":[],""returntype"":""Void"",""offset"":3642,""safe"":false},{""name"":""d38"",""parameters"":[],""returntype"":""Void"",""offset"":3663,""safe"":false},{""name"":""d39"",""parameters"":[],""returntype"":""Void"",""offset"":3684,""safe"":false},{""name"":""d40"",""parameters"":[],""returntype"":""Void"",""offset"":3705,""safe"":false},{""name"":""d41"",""parameters"":[],""returntype"":""Void"",""offset"":3726,""safe"":false},{""name"":""d42"",""parameters"":[],""returntype"":""Void"",""offset"":3747,""safe"":false},{""name"":""d43"",""parameters"":[],""returntype"":""Void"",""offset"":3768,""safe"":false},{""name"":""d44"",""parameters"":[],""returntype"":""Void"",""offset"":3789,""safe"":false},{""name"":""d45"",""parameters"":[],""returntype"":""Void"",""offset"":3810,""safe"":false},{""name"":""d46"",""parameters"":[],""returntype"":""Void"",""offset"":3831,""safe"":false},{""name"":""d47"",""parameters"":[],""returntype"":""Void"",""offset"":3852,""safe"":false},{""name"":""d48"",""parameters"":[],""returntype"":""Void"",""offset"":3873,""safe"":false},{""name"":""d49"",""parameters"":[],""returntype"":""Void"",""offset"":3894,""safe"":false},{""name"":""d50"",""parameters"":[],""returntype"":""Void"",""offset"":3915,""safe"":false},{""name"":""e"",""parameters"":[{""name"":""amountIn"",""type"":""Integer""}],""returntype"":""Void"",""offset"":3936,""safe"":false},{""name"":""e0"",""parameters"":[],""returntype"":""Integer"",""offset"":3955,""safe"":false},{""name"":""e10"",""parameters"":[],""returntype"":""Void"",""offset"":3979,""safe"":false},{""name"":""e11"",""parameters"":[],""returntype"":""Void"",""offset"":3999,""safe"":false},{""name"":""e12"",""parameters"":[],""returntype"":""Void"",""offset"":4019,""safe"":false},{""name"":""e13"",""parameters"":[],""returntype"":""Void"",""offset"":4039,""safe"":false},{""name"":""e14"",""parameters"":[],""returntype"":""Void"",""offset"":4059,""safe"":false},{""name"":""e15"",""parameters"":[],""returntype"":""Void"",""offset"":4079,""safe"":false},{""name"":""e16"",""parameters"":[],""returntype"":""Void"",""offset"":4099,""safe"":false},{""name"":""e17"",""parameters"":[],""returntype"":""Void"",""offset"":4119,""safe"":false},{""name"":""e18"",""parameters"":[],""returntype"":""Void"",""offset"":4140,""safe"":false},{""name"":""e19"",""parameters"":[],""returntype"":""Void"",""offset"":4161,""safe"":false},{""name"":""e20"",""parameters"":[],""returntype"":""Void"",""offset"":4182,""safe"":false},{""name"":""e21"",""parameters"":[],""returntype"":""Void"",""offset"":4203,""safe"":false},{""name"":""e22"",""parameters"":[],""returntype"":""Void"",""offset"":4224,""safe"":false},{""name"":""e23"",""parameters"":[],""returntype"":""Void"",""offset"":4245,""safe"":false},{""name"":""e24"",""parameters"":[],""returntype"":""Void"",""offset"":4266,""safe"":false},{""name"":""e25"",""parameters"":[],""returntype"":""Void"",""offset"":4287,""safe"":false},{""name"":""e26"",""parameters"":[],""returntype"":""Void"",""offset"":4308,""safe"":false},{""name"":""e27"",""parameters"":[],""returntype"":""Void"",""offset"":4329,""safe"":false},{""name"":""e28"",""parameters"":[],""returntype"":""Void"",""offset"":4350,""safe"":false},{""name"":""e29"",""parameters"":[],""returntype"":""Void"",""offset"":4371,""safe"":false},{""name"":""e30"",""parameters"":[],""returntype"":""Void"",""offset"":4392,""safe"":false},{""name"":""e31"",""parameters"":[],""returntype"":""Void"",""offset"":4413,""safe"":false},{""name"":""e32"",""parameters"":[],""returntype"":""Void"",""offset"":4434,""safe"":false},{""name"":""e33"",""parameters"":[],""returntype"":""Void"",""offset"":4455,""safe"":false},{""name"":""e34"",""parameters"":[],""returntype"":""Void"",""offset"":4476,""safe"":false},{""name"":""e35"",""parameters"":[],""returntype"":""Void"",""offset"":4497,""safe"":false},{""name"":""e36"",""parameters"":[],""returntype"":""Void"",""offset"":4518,""safe"":false},{""name"":""e37"",""parameters"":[],""returntype"":""Void"",""offset"":4539,""safe"":false},{""name"":""e38"",""parameters"":[],""returntype"":""Void"",""offset"":4560,""safe"":false},{""name"":""e39"",""parameters"":[],""returntype"":""Void"",""offset"":4581,""safe"":false},{""name"":""e40"",""parameters"":[],""returntype"":""Void"",""offset"":4602,""safe"":false},{""name"":""e41"",""parameters"":[],""returntype"":""Void"",""offset"":4623,""safe"":false},{""name"":""e42"",""parameters"":[],""returntype"":""Void"",""offset"":4644,""safe"":false},{""name"":""e43"",""parameters"":[],""returntype"":""Void"",""offset"":4665,""safe"":false},{""name"":""e44"",""parameters"":[],""returntype"":""Void"",""offset"":4686,""safe"":false},{""name"":""e45"",""parameters"":[],""returntype"":""Void"",""offset"":4707,""safe"":false},{""name"":""e46"",""parameters"":[],""returntype"":""Void"",""offset"":4728,""safe"":false},{""name"":""e47"",""parameters"":[],""returntype"":""Void"",""offset"":4749,""safe"":false},{""name"":""e48"",""parameters"":[],""returntype"":""Void"",""offset"":4770,""safe"":false},{""name"":""e49"",""parameters"":[],""returntype"":""Void"",""offset"":4791,""safe"":false},{""name"":""e50"",""parameters"":[],""returntype"":""Void"",""offset"":4812,""safe"":false},{""name"":""f"",""parameters"":[{""name"":""amountIn"",""type"":""Integer""}],""returntype"":""Void"",""offset"":4833,""safe"":false},{""name"":""f0"",""parameters"":[],""returntype"":""Integer"",""offset"":4852,""safe"":false},{""name"":""f10"",""parameters"":[],""returntype"":""Void"",""offset"":4876,""safe"":false},{""name"":""f11"",""parameters"":[],""returntype"":""Void"",""offset"":4896,""safe"":false},{""name"":""f12"",""parameters"":[],""returntype"":""Void"",""offset"":4916,""safe"":false},{""name"":""f13"",""parameters"":[],""returntype"":""Void"",""offset"":4936,""safe"":false},{""name"":""f14"",""parameters"":[],""returntype"":""Void"",""offset"":4956,""safe"":false},{""name"":""f15"",""parameters"":[],""returntype"":""Void"",""offset"":4976,""safe"":false},{""name"":""f16"",""parameters"":[],""returntype"":""Void"",""offset"":4996,""safe"":false},{""name"":""f17"",""parameters"":[],""returntype"":""Void"",""offset"":5016,""safe"":false},{""name"":""f18"",""parameters"":[],""returntype"":""Void"",""offset"":5037,""safe"":false},{""name"":""f19"",""parameters"":[],""returntype"":""Void"",""offset"":5058,""safe"":false},{""name"":""f20"",""parameters"":[],""returntype"":""Void"",""offset"":5079,""safe"":false},{""name"":""f21"",""parameters"":[],""returntype"":""Void"",""offset"":5100,""safe"":false},{""name"":""f22"",""parameters"":[],""returntype"":""Void"",""offset"":5121,""safe"":false},{""name"":""f23"",""parameters"":[],""returntype"":""Void"",""offset"":5142,""safe"":false},{""name"":""f24"",""parameters"":[],""returntype"":""Void"",""offset"":5163,""safe"":false},{""name"":""f25"",""parameters"":[],""returntype"":""Void"",""offset"":5184,""safe"":false},{""name"":""f26"",""parameters"":[],""returntype"":""Void"",""offset"":5205,""safe"":false},{""name"":""f27"",""parameters"":[],""returntype"":""Void"",""offset"":5226,""safe"":false},{""name"":""f28"",""parameters"":[],""returntype"":""Void"",""offset"":5247,""safe"":false},{""name"":""f29"",""parameters"":[],""returntype"":""Void"",""offset"":5268,""safe"":false},{""name"":""f30"",""parameters"":[],""returntype"":""Void"",""offset"":5289,""safe"":false},{""name"":""f31"",""parameters"":[],""returntype"":""Void"",""offset"":5310,""safe"":false},{""name"":""f32"",""parameters"":[],""returntype"":""Void"",""offset"":5331,""safe"":false},{""name"":""f33"",""parameters"":[],""returntype"":""Void"",""offset"":5352,""safe"":false},{""name"":""f34"",""parameters"":[],""returntype"":""Void"",""offset"":5373,""safe"":false},{""name"":""f35"",""parameters"":[],""returntype"":""Void"",""offset"":5394,""safe"":false},{""name"":""f36"",""parameters"":[],""returntype"":""Void"",""offset"":5415,""safe"":false},{""name"":""f37"",""parameters"":[],""returntype"":""Void"",""offset"":5436,""safe"":false},{""name"":""f38"",""parameters"":[],""returntype"":""Void"",""offset"":5457,""safe"":false},{""name"":""f39"",""parameters"":[],""returntype"":""Void"",""offset"":5478,""safe"":false},{""name"":""f40"",""parameters"":[],""returntype"":""Void"",""offset"":5499,""safe"":false},{""name"":""f41"",""parameters"":[],""returntype"":""Void"",""offset"":5520,""safe"":false},{""name"":""f42"",""parameters"":[],""returntype"":""Void"",""offset"":5541,""safe"":false},{""name"":""f43"",""parameters"":[],""returntype"":""Void"",""offset"":5562,""safe"":false},{""name"":""f44"",""parameters"":[],""returntype"":""Void"",""offset"":5583,""safe"":false},{""name"":""f45"",""parameters"":[],""returntype"":""Void"",""offset"":5604,""safe"":false},{""name"":""f46"",""parameters"":[],""returntype"":""Void"",""offset"":5625,""safe"":false},{""name"":""f47"",""parameters"":[],""returntype"":""Void"",""offset"":5646,""safe"":false},{""name"":""f48"",""parameters"":[],""returntype"":""Void"",""offset"":5667,""safe"":false},{""name"":""f49"",""parameters"":[],""returntype"":""Void"",""offset"":5688,""safe"":false},{""name"":""f50"",""parameters"":[],""returntype"":""Void"",""offset"":5709,""safe"":false},{""name"":""g"",""parameters"":[{""name"":""amountIn"",""type"":""Integer""}],""returntype"":""Void"",""offset"":5730,""safe"":false},{""name"":""g0"",""parameters"":[],""returntype"":""Integer"",""offset"":5749,""safe"":false},{""name"":""g10"",""parameters"":[],""returntype"":""Void"",""offset"":5773,""safe"":false},{""name"":""g11"",""parameters"":[],""returntype"":""Void"",""offset"":5793,""safe"":false},{""name"":""g12"",""parameters"":[],""returntype"":""Void"",""offset"":5813,""safe"":false},{""name"":""g13"",""parameters"":[],""returntype"":""Void"",""offset"":5833,""safe"":false},{""name"":""g14"",""parameters"":[],""returntype"":""Void"",""offset"":5853,""safe"":false},{""name"":""g15"",""parameters"":[],""returntype"":""Void"",""offset"":5873,""safe"":false},{""name"":""g16"",""parameters"":[],""returntype"":""Void"",""offset"":5893,""safe"":false},{""name"":""g17"",""parameters"":[],""returntype"":""Void"",""offset"":5913,""safe"":false},{""name"":""g18"",""parameters"":[],""returntype"":""Void"",""offset"":5934,""safe"":false},{""name"":""g19"",""parameters"":[],""returntype"":""Void"",""offset"":5955,""safe"":false},{""name"":""g20"",""parameters"":[],""returntype"":""Void"",""offset"":5976,""safe"":false},{""name"":""g21"",""parameters"":[],""returntype"":""Void"",""offset"":5997,""safe"":false},{""name"":""g22"",""parameters"":[],""returntype"":""Void"",""offset"":6018,""safe"":false},{""name"":""g23"",""parameters"":[],""returntype"":""Void"",""offset"":6039,""safe"":false},{""name"":""g24"",""parameters"":[],""returntype"":""Void"",""offset"":6060,""safe"":false},{""name"":""g25"",""parameters"":[],""returntype"":""Void"",""offset"":6081,""safe"":false},{""name"":""g26"",""parameters"":[],""returntype"":""Void"",""offset"":6102,""safe"":false},{""name"":""g27"",""parameters"":[],""returntype"":""Void"",""offset"":6123,""safe"":false},{""name"":""g28"",""parameters"":[],""returntype"":""Void"",""offset"":6144,""safe"":false},{""name"":""g29"",""parameters"":[],""returntype"":""Void"",""offset"":6165,""safe"":false},{""name"":""g30"",""parameters"":[],""returntype"":""Void"",""offset"":6186,""safe"":false},{""name"":""g31"",""parameters"":[],""returntype"":""Void"",""offset"":6207,""safe"":false},{""name"":""g32"",""parameters"":[],""returntype"":""Void"",""offset"":6228,""safe"":false},{""name"":""g33"",""parameters"":[],""returntype"":""Void"",""offset"":6249,""safe"":false},{""name"":""g34"",""parameters"":[],""returntype"":""Void"",""offset"":6270,""safe"":false},{""name"":""g35"",""parameters"":[],""returntype"":""Void"",""offset"":6291,""safe"":false},{""name"":""g36"",""parameters"":[],""returntype"":""Void"",""offset"":6312,""safe"":false},{""name"":""g37"",""parameters"":[],""returntype"":""Void"",""offset"":6333,""safe"":false},{""name"":""g38"",""parameters"":[],""returntype"":""Void"",""offset"":6354,""safe"":false},{""name"":""g39"",""parameters"":[],""returntype"":""Void"",""offset"":6375,""safe"":false},{""name"":""g40"",""parameters"":[],""returntype"":""Void"",""offset"":6396,""safe"":false},{""name"":""g41"",""parameters"":[],""returntype"":""Void"",""offset"":6417,""safe"":false},{""name"":""g42"",""parameters"":[],""returntype"":""Void"",""offset"":6438,""safe"":false},{""name"":""g43"",""parameters"":[],""returntype"":""Void"",""offset"":6459,""safe"":false},{""name"":""g44"",""parameters"":[],""returntype"":""Void"",""offset"":6480,""safe"":false},{""name"":""g45"",""parameters"":[],""returntype"":""Void"",""offset"":6501,""safe"":false},{""name"":""g46"",""parameters"":[],""returntype"":""Void"",""offset"":6522,""safe"":false},{""name"":""g47"",""parameters"":[],""returntype"":""Void"",""offset"":6543,""safe"":false},{""name"":""g48"",""parameters"":[],""returntype"":""Void"",""offset"":6564,""safe"":false},{""name"":""g49"",""parameters"":[],""returntype"":""Void"",""offset"":6585,""safe"":false},{""name"":""g50"",""parameters"":[],""returntype"":""Void"",""offset"":6606,""safe"":false},{""name"":""h"",""parameters"":[{""name"":""amountIn"",""type"":""Integer""}],""returntype"":""Void"",""offset"":6627,""safe"":false},{""name"":""h0"",""parameters"":[],""returntype"":""Integer"",""offset"":6646,""safe"":false},{""name"":""h10"",""parameters"":[],""returntype"":""Void"",""offset"":6670,""safe"":false},{""name"":""h11"",""parameters"":[],""returntype"":""Void"",""offset"":6690,""safe"":false},{""name"":""h12"",""parameters"":[],""returntype"":""Void"",""offset"":6710,""safe"":false},{""name"":""h13"",""parameters"":[],""returntype"":""Void"",""offset"":6730,""safe"":false},{""name"":""h14"",""parameters"":[],""returntype"":""Void"",""offset"":6750,""safe"":false},{""name"":""h15"",""parameters"":[],""returntype"":""Void"",""offset"":6770,""safe"":false},{""name"":""h16"",""parameters"":[],""returntype"":""Void"",""offset"":6790,""safe"":false},{""name"":""h17"",""parameters"":[],""returntype"":""Void"",""offset"":6810,""safe"":false},{""name"":""h18"",""parameters"":[],""returntype"":""Void"",""offset"":6831,""safe"":false},{""name"":""h19"",""parameters"":[],""returntype"":""Void"",""offset"":6852,""safe"":false},{""name"":""h20"",""parameters"":[],""returntype"":""Void"",""offset"":6873,""safe"":false},{""name"":""h21"",""parameters"":[],""returntype"":""Void"",""offset"":6894,""safe"":false},{""name"":""h22"",""parameters"":[],""returntype"":""Void"",""offset"":6915,""safe"":false},{""name"":""h23"",""parameters"":[],""returntype"":""Void"",""offset"":6936,""safe"":false},{""name"":""h24"",""parameters"":[],""returntype"":""Void"",""offset"":6957,""safe"":false},{""name"":""h25"",""parameters"":[],""returntype"":""Void"",""offset"":6978,""safe"":false},{""name"":""h26"",""parameters"":[],""returntype"":""Void"",""offset"":6999,""safe"":false},{""name"":""h27"",""parameters"":[],""returntype"":""Void"",""offset"":7020,""safe"":false},{""name"":""h28"",""parameters"":[],""returntype"":""Void"",""offset"":7041,""safe"":false},{""name"":""h29"",""parameters"":[],""returntype"":""Void"",""offset"":7062,""safe"":false},{""name"":""h30"",""parameters"":[],""returntype"":""Void"",""offset"":7083,""safe"":false},{""name"":""h31"",""parameters"":[],""returntype"":""Void"",""offset"":7104,""safe"":false},{""name"":""h32"",""parameters"":[],""returntype"":""Void"",""offset"":7125,""safe"":false},{""name"":""h33"",""parameters"":[],""returntype"":""Void"",""offset"":7146,""safe"":false},{""name"":""h34"",""parameters"":[],""returntype"":""Void"",""offset"":7167,""safe"":false},{""name"":""h35"",""parameters"":[],""returntype"":""Void"",""offset"":7188,""safe"":false},{""name"":""h36"",""parameters"":[],""returntype"":""Void"",""offset"":7209,""safe"":false},{""name"":""h37"",""parameters"":[],""returntype"":""Void"",""offset"":7230,""safe"":false},{""name"":""h38"",""parameters"":[],""returntype"":""Void"",""offset"":7251,""safe"":false},{""name"":""h39"",""parameters"":[],""returntype"":""Void"",""offset"":7272,""safe"":false},{""name"":""h40"",""parameters"":[],""returntype"":""Void"",""offset"":7293,""safe"":false},{""name"":""h41"",""parameters"":[],""returntype"":""Void"",""offset"":7314,""safe"":false},{""name"":""h42"",""parameters"":[],""returntype"":""Void"",""offset"":7335,""safe"":false},{""name"":""h43"",""parameters"":[],""returntype"":""Void"",""offset"":7356,""safe"":false},{""name"":""h44"",""parameters"":[],""returntype"":""Void"",""offset"":7377,""safe"":false},{""name"":""h45"",""parameters"":[],""returntype"":""Void"",""offset"":7398,""safe"":false},{""name"":""h46"",""parameters"":[],""returntype"":""Void"",""offset"":7419,""safe"":false},{""name"":""h47"",""parameters"":[],""returntype"":""Void"",""offset"":7440,""safe"":false},{""name"":""h48"",""parameters"":[],""returntype"":""Void"",""offset"":7461,""safe"":false},{""name"":""h49"",""parameters"":[],""returntype"":""Void"",""offset"":7482,""safe"":false},{""name"":""h50"",""parameters"":[],""returntype"":""Void"",""offset"":7503,""safe"":false},{""name"":""i"",""parameters"":[{""name"":""amountIn"",""type"":""Integer""}],""returntype"":""Void"",""offset"":7524,""safe"":false},{""name"":""i0"",""parameters"":[],""returntype"":""Integer"",""offset"":7545,""safe"":false},{""name"":""i10"",""parameters"":[],""returntype"":""Void"",""offset"":7571,""safe"":false},{""name"":""i11"",""parameters"":[],""returntype"":""Void"",""offset"":7593,""safe"":false},{""name"":""i12"",""parameters"":[],""returntype"":""Void"",""offset"":7615,""safe"":false},{""name"":""i13"",""parameters"":[],""returntype"":""Void"",""offset"":7637,""safe"":false},{""name"":""i14"",""parameters"":[],""returntype"":""Void"",""offset"":7659,""safe"":false},{""name"":""i15"",""parameters"":[],""returntype"":""Void"",""offset"":7681,""safe"":false},{""name"":""i16"",""parameters"":[],""returntype"":""Void"",""offset"":7703,""safe"":false},{""name"":""i17"",""parameters"":[],""returntype"":""Void"",""offset"":7725,""safe"":false},{""name"":""i18"",""parameters"":[],""returntype"":""Void"",""offset"":7748,""safe"":false},{""name"":""i19"",""parameters"":[],""returntype"":""Void"",""offset"":7771,""safe"":false},{""name"":""i20"",""parameters"":[],""returntype"":""Void"",""offset"":7794,""safe"":false},{""name"":""i21"",""parameters"":[],""returntype"":""Void"",""offset"":7817,""safe"":false},{""name"":""i22"",""parameters"":[],""returntype"":""Void"",""offset"":7840,""safe"":false},{""name"":""i23"",""parameters"":[],""returntype"":""Void"",""offset"":7863,""safe"":false},{""name"":""i24"",""parameters"":[],""returntype"":""Void"",""offset"":7886,""safe"":false},{""name"":""i25"",""parameters"":[],""returntype"":""Void"",""offset"":7909,""safe"":false},{""name"":""i26"",""parameters"":[],""returntype"":""Void"",""offset"":7932,""safe"":false},{""name"":""i27"",""parameters"":[],""returntype"":""Void"",""offset"":7955,""safe"":false},{""name"":""i28"",""parameters"":[],""returntype"":""Void"",""offset"":7978,""safe"":false},{""name"":""i29"",""parameters"":[],""returntype"":""Void"",""offset"":8001,""safe"":false},{""name"":""i30"",""parameters"":[],""returntype"":""Void"",""offset"":8024,""safe"":false},{""name"":""i31"",""parameters"":[],""returntype"":""Void"",""offset"":8047,""safe"":false},{""name"":""i32"",""parameters"":[],""returntype"":""Void"",""offset"":8070,""safe"":false},{""name"":""i33"",""parameters"":[],""returntype"":""Void"",""offset"":8093,""safe"":false},{""name"":""i34"",""parameters"":[],""returntype"":""Void"",""offset"":8116,""safe"":false},{""name"":""i35"",""parameters"":[],""returntype"":""Void"",""offset"":8139,""safe"":false},{""name"":""i36"",""parameters"":[],""returntype"":""Void"",""offset"":8162,""safe"":false},{""name"":""i37"",""parameters"":[],""returntype"":""Void"",""offset"":8185,""safe"":false},{""name"":""i38"",""parameters"":[],""returntype"":""Void"",""offset"":8208,""safe"":false},{""name"":""i39"",""parameters"":[],""returntype"":""Void"",""offset"":8231,""safe"":false},{""name"":""i40"",""parameters"":[],""returntype"":""Void"",""offset"":8254,""safe"":false},{""name"":""i41"",""parameters"":[],""returntype"":""Void"",""offset"":8277,""safe"":false},{""name"":""i42"",""parameters"":[],""returntype"":""Void"",""offset"":8300,""safe"":false},{""name"":""i43"",""parameters"":[],""returntype"":""Void"",""offset"":8323,""safe"":false},{""name"":""i44"",""parameters"":[],""returntype"":""Void"",""offset"":8346,""safe"":false},{""name"":""i45"",""parameters"":[],""returntype"":""Void"",""offset"":8369,""safe"":false},{""name"":""i46"",""parameters"":[],""returntype"":""Void"",""offset"":8392,""safe"":false},{""name"":""i47"",""parameters"":[],""returntype"":""Void"",""offset"":8415,""safe"":false},{""name"":""i48"",""parameters"":[],""returntype"":""Void"",""offset"":8438,""safe"":false},{""name"":""i49"",""parameters"":[],""returntype"":""Void"",""offset"":8461,""safe"":false},{""name"":""i50"",""parameters"":[],""returntype"":""Void"",""offset"":8484,""safe"":false},{""name"":""update"",""parameters"":[{""name"":""nefFile"",""type"":""ByteArray""},{""name"":""manifest"",""type"":""String""}],""returntype"":""Void"",""offset"":8511,""safe"":false},{""name"":""j"",""parameters"":[{""name"":""amountIn"",""type"":""Integer""}],""returntype"":""Void"",""offset"":8578,""safe"":false},{""name"":""j0"",""parameters"":[],""returntype"":""Integer"",""offset"":8599,""safe"":false},{""name"":""j10"",""parameters"":[],""returntype"":""Void"",""offset"":8625,""safe"":false},{""name"":""j11"",""parameters"":[],""returntype"":""Void"",""offset"":8647,""safe"":false},{""name"":""j12"",""parameters"":[],""returntype"":""Void"",""offset"":8669,""safe"":false},{""name"":""j13"",""parameters"":[],""returntype"":""Void"",""offset"":8691,""safe"":false},{""name"":""j14"",""parameters"":[],""returntype"":""Void"",""offset"":8713,""safe"":false},{""name"":""j15"",""parameters"":[],""returntype"":""Void"",""offset"":8735,""safe"":false},{""name"":""j16"",""parameters"":[],""returntype"":""Void"",""offset"":8757,""safe"":false},{""name"":""j17"",""parameters"":[],""returntype"":""Void"",""offset"":8779,""safe"":false},{""name"":""j18"",""parameters"":[],""returntype"":""Void"",""offset"":8802,""safe"":false},{""name"":""j19"",""parameters"":[],""returntype"":""Void"",""offset"":8825,""safe"":false},{""name"":""j20"",""parameters"":[],""returntype"":""Void"",""offset"":8848,""safe"":false},{""name"":""j21"",""parameters"":[],""returntype"":""Void"",""offset"":8871,""safe"":false},{""name"":""j22"",""parameters"":[],""returntype"":""Void"",""offset"":8894,""safe"":false},{""name"":""j23"",""parameters"":[],""returntype"":""Void"",""offset"":8917,""safe"":false},{""name"":""j24"",""parameters"":[],""returntype"":""Void"",""offset"":8940,""safe"":false},{""name"":""j25"",""parameters"":[],""returntype"":""Void"",""offset"":8963,""safe"":false},{""name"":""j26"",""parameters"":[],""returntype"":""Void"",""offset"":8986,""safe"":false},{""name"":""j27"",""parameters"":[],""returntype"":""Void"",""offset"":9009,""safe"":false},{""name"":""j28"",""parameters"":[],""returntype"":""Void"",""offset"":9032,""safe"":false},{""name"":""j29"",""parameters"":[],""returntype"":""Void"",""offset"":9055,""safe"":false},{""name"":""j30"",""parameters"":[],""returntype"":""Void"",""offset"":9078,""safe"":false},{""name"":""j31"",""parameters"":[],""returntype"":""Void"",""offset"":9101,""safe"":false},{""name"":""j32"",""parameters"":[],""returntype"":""Void"",""offset"":9124,""safe"":false},{""name"":""j33"",""parameters"":[],""returntype"":""Void"",""offset"":9147,""safe"":false},{""name"":""j34"",""parameters"":[],""returntype"":""Void"",""offset"":9170,""safe"":false},{""name"":""j35"",""parameters"":[],""returntype"":""Void"",""offset"":9193,""safe"":false},{""name"":""j36"",""parameters"":[],""returntype"":""Void"",""offset"":9216,""safe"":false},{""name"":""j37"",""parameters"":[],""returntype"":""Void"",""offset"":9239,""safe"":false},{""name"":""j38"",""parameters"":[],""returntype"":""Void"",""offset"":9262,""safe"":false},{""name"":""j39"",""parameters"":[],""returntype"":""Void"",""offset"":9285,""safe"":false},{""name"":""j40"",""parameters"":[],""returntype"":""Void"",""offset"":9308,""safe"":false},{""name"":""j41"",""parameters"":[],""returntype"":""Void"",""offset"":9331,""safe"":false},{""name"":""j42"",""parameters"":[],""returntype"":""Void"",""offset"":9354,""safe"":false},{""name"":""j43"",""parameters"":[],""returntype"":""Void"",""offset"":9377,""safe"":false},{""name"":""j44"",""parameters"":[],""returntype"":""Void"",""offset"":9400,""safe"":false},{""name"":""j45"",""parameters"":[],""returntype"":""Void"",""offset"":9423,""safe"":false},{""name"":""j46"",""parameters"":[],""returntype"":""Void"",""offset"":9446,""safe"":false},{""name"":""j47"",""parameters"":[],""returntype"":""Void"",""offset"":9469,""safe"":false},{""name"":""j48"",""parameters"":[],""returntype"":""Void"",""offset"":9492,""safe"":false},{""name"":""j49"",""parameters"":[],""returntype"":""Void"",""offset"":9515,""safe"":false},{""name"":""j50"",""parameters"":[],""returntype"":""Void"",""offset"":9538,""safe"":false},{""name"":""k"",""parameters"":[{""name"":""amountIn"",""type"":""Integer""}],""returntype"":""Void"",""offset"":9561,""safe"":false},{""name"":""k0"",""parameters"":[],""returntype"":""Integer"",""offset"":9601,""safe"":false},{""name"":""k10"",""parameters"":[],""returntype"":""Void"",""offset"":9646,""safe"":false},{""name"":""k11"",""parameters"":[],""returntype"":""Void"",""offset"":9687,""safe"":false},{""name"":""k12"",""parameters"":[],""returntype"":""Void"",""offset"":9728,""safe"":false},{""name"":""k13"",""parameters"":[],""returntype"":""Void"",""offset"":9769,""safe"":false},{""name"":""k14"",""parameters"":[],""returntype"":""Void"",""offset"":9810,""safe"":false},{""name"":""k15"",""parameters"":[],""returntype"":""Void"",""offset"":9851,""safe"":false},{""name"":""k16"",""parameters"":[],""returntype"":""Void"",""offset"":9892,""safe"":false},{""name"":""k17"",""parameters"":[],""returntype"":""Void"",""offset"":9933,""safe"":false},{""name"":""k18"",""parameters"":[],""returntype"":""Void"",""offset"":9975,""safe"":false},{""name"":""k19"",""parameters"":[],""returntype"":""Void"",""offset"":10017,""safe"":false},{""name"":""k20"",""parameters"":[],""returntype"":""Void"",""offset"":10059,""safe"":false},{""name"":""k21"",""parameters"":[],""returntype"":""Void"",""offset"":10101,""safe"":false},{""name"":""k22"",""parameters"":[],""returntype"":""Void"",""offset"":10143,""safe"":false},{""name"":""k23"",""parameters"":[],""returntype"":""Void"",""offset"":10185,""safe"":false},{""name"":""k24"",""parameters"":[],""returntype"":""Void"",""offset"":10227,""safe"":false},{""name"":""k25"",""parameters"":[],""returntype"":""Void"",""offset"":10269,""safe"":false},{""name"":""k26"",""parameters"":[],""returntype"":""Void"",""offset"":10311,""safe"":false},{""name"":""k27"",""parameters"":[],""returntype"":""Void"",""offset"":10353,""safe"":false},{""name"":""k28"",""parameters"":[],""returntype"":""Void"",""offset"":10395,""safe"":false},{""name"":""k29"",""parameters"":[],""returntype"":""Void"",""offset"":10437,""safe"":false},{""name"":""k30"",""parameters"":[],""returntype"":""Void"",""offset"":10479,""safe"":false},{""name"":""k31"",""parameters"":[],""returntype"":""Void"",""offset"":10521,""safe"":false},{""name"":""k32"",""parameters"":[],""returntype"":""Void"",""offset"":10563,""safe"":false},{""name"":""k33"",""parameters"":[],""returntype"":""Void"",""offset"":10605,""safe"":false},{""name"":""k34"",""parameters"":[],""returntype"":""Void"",""offset"":10647,""safe"":false},{""name"":""k35"",""parameters"":[],""returntype"":""Void"",""offset"":10689,""safe"":false},{""name"":""k36"",""parameters"":[],""returntype"":""Void"",""offset"":10731,""safe"":false},{""name"":""k37"",""parameters"":[],""returntype"":""Void"",""offset"":10773,""safe"":false},{""name"":""k38"",""parameters"":[],""returntype"":""Void"",""offset"":10815,""safe"":false},{""name"":""k39"",""parameters"":[],""returntype"":""Void"",""offset"":10857,""safe"":false},{""name"":""k40"",""parameters"":[],""returntype"":""Void"",""offset"":10899,""safe"":false},{""name"":""k41"",""parameters"":[],""returntype"":""Void"",""offset"":10941,""safe"":false},{""name"":""k42"",""parameters"":[],""returntype"":""Void"",""offset"":10983,""safe"":false},{""name"":""k43"",""parameters"":[],""returntype"":""Void"",""offset"":11025,""safe"":false},{""name"":""k44"",""parameters"":[],""returntype"":""Void"",""offset"":11067,""safe"":false},{""name"":""k45"",""parameters"":[],""returntype"":""Void"",""offset"":11109,""safe"":false},{""name"":""k46"",""parameters"":[],""returntype"":""Void"",""offset"":11151,""safe"":false},{""name"":""k47"",""parameters"":[],""returntype"":""Void"",""offset"":11193,""safe"":false},{""name"":""k48"",""parameters"":[],""returntype"":""Void"",""offset"":11235,""safe"":false},{""name"":""k49"",""parameters"":[],""returntype"":""Void"",""offset"":11277,""safe"":false},{""name"":""k50"",""parameters"":[],""returntype"":""Void"",""offset"":11319,""safe"":false},{""name"":""l"",""parameters"":[{""name"":""amountIn"",""type"":""Integer""}],""returntype"":""Void"",""offset"":11361,""safe"":false},{""name"":""l0"",""parameters"":[],""returntype"":""Integer"",""offset"":11401,""safe"":false},{""name"":""l10"",""parameters"":[],""returntype"":""Void"",""offset"":11446,""safe"":false},{""name"":""l11"",""parameters"":[],""returntype"":""Void"",""offset"":11487,""safe"":false},{""name"":""l12"",""parameters"":[],""returntype"":""Void"",""offset"":11528,""safe"":false},{""name"":""l13"",""parameters"":[],""returntype"":""Void"",""offset"":11569,""safe"":false},{""name"":""l14"",""parameters"":[],""returntype"":""Void"",""offset"":11610,""safe"":false},{""name"":""l15"",""parameters"":[],""returntype"":""Void"",""offset"":11651,""safe"":false},{""name"":""l16"",""parameters"":[],""returntype"":""Void"",""offset"":11692,""safe"":false},{""name"":""l17"",""parameters"":[],""returntype"":""Void"",""offset"":11733,""safe"":false},{""name"":""l18"",""parameters"":[],""returntype"":""Void"",""offset"":11775,""safe"":false},{""name"":""l19"",""parameters"":[],""returntype"":""Void"",""offset"":11817,""safe"":false},{""name"":""l20"",""parameters"":[],""returntype"":""Void"",""offset"":11859,""safe"":false},{""name"":""l21"",""parameters"":[],""returntype"":""Void"",""offset"":11901,""safe"":false},{""name"":""l22"",""parameters"":[],""returntype"":""Void"",""offset"":11943,""safe"":false},{""name"":""l23"",""parameters"":[],""returntype"":""Void"",""offset"":11985,""safe"":false},{""name"":""l24"",""parameters"":[],""returntype"":""Void"",""offset"":12027,""safe"":false},{""name"":""l25"",""parameters"":[],""returntype"":""Void"",""offset"":12069,""safe"":false},{""name"":""l26"",""parameters"":[],""returntype"":""Void"",""offset"":12111,""safe"":false},{""name"":""l27"",""parameters"":[],""returntype"":""Void"",""offset"":12153,""safe"":false},{""name"":""l28"",""parameters"":[],""returntype"":""Void"",""offset"":12195,""safe"":false},{""name"":""l29"",""parameters"":[],""returntype"":""Void"",""offset"":12237,""safe"":false},{""name"":""l30"",""parameters"":[],""returntype"":""Void"",""offset"":12279,""safe"":false},{""name"":""l31"",""parameters"":[],""returntype"":""Void"",""offset"":12321,""safe"":false},{""name"":""l32"",""parameters"":[],""returntype"":""Void"",""offset"":12363,""safe"":false},{""name"":""l33"",""parameters"":[],""returntype"":""Void"",""offset"":12405,""safe"":false},{""name"":""l34"",""parameters"":[],""returntype"":""Void"",""offset"":12447,""safe"":false},{""name"":""l35"",""parameters"":[],""returntype"":""Void"",""offset"":12489,""safe"":false},{""name"":""l36"",""parameters"":[],""returntype"":""Void"",""offset"":12531,""safe"":false},{""name"":""l37"",""parameters"":[],""returntype"":""Void"",""offset"":12573,""safe"":false},{""name"":""l38"",""parameters"":[],""returntype"":""Void"",""offset"":12615,""safe"":false},{""name"":""l39"",""parameters"":[],""returntype"":""Void"",""offset"":12657,""safe"":false},{""name"":""l40"",""parameters"":[],""returntype"":""Void"",""offset"":12699,""safe"":false},{""name"":""l41"",""parameters"":[],""returntype"":""Void"",""offset"":12741,""safe"":false},{""name"":""l42"",""parameters"":[],""returntype"":""Void"",""offset"":12783,""safe"":false},{""name"":""l43"",""parameters"":[],""returntype"":""Void"",""offset"":12825,""safe"":false},{""name"":""l44"",""parameters"":[],""returntype"":""Void"",""offset"":12867,""safe"":false},{""name"":""l45"",""parameters"":[],""returntype"":""Void"",""offset"":12909,""safe"":false},{""name"":""l46"",""parameters"":[],""returntype"":""Void"",""offset"":12951,""safe"":false},{""name"":""l47"",""parameters"":[],""returntype"":""Void"",""offset"":12993,""safe"":false},{""name"":""l48"",""parameters"":[],""returntype"":""Void"",""offset"":13035,""safe"":false},{""name"":""l49"",""parameters"":[],""returntype"":""Void"",""offset"":13077,""safe"":false},{""name"":""l50"",""parameters"":[],""returntype"":""Void"",""offset"":13119,""safe"":false},{""name"":""m"",""parameters"":[{""name"":""amountIn"",""type"":""Integer""}],""returntype"":""Void"",""offset"":13161,""safe"":false},{""name"":""m0"",""parameters"":[],""returntype"":""Integer"",""offset"":13181,""safe"":false},{""name"":""m10"",""parameters"":[],""returntype"":""Void"",""offset"":13206,""safe"":false},{""name"":""m11"",""parameters"":[],""returntype"":""Void"",""offset"":13227,""safe"":false},{""name"":""m12"",""parameters"":[],""returntype"":""Void"",""offset"":13248,""safe"":false},{""name"":""m13"",""parameters"":[],""returntype"":""Void"",""offset"":13269,""safe"":false},{""name"":""m14"",""parameters"":[],""returntype"":""Void"",""offset"":13290,""safe"":false},{""name"":""m15"",""parameters"":[],""returntype"":""Void"",""offset"":13311,""safe"":false},{""name"":""m16"",""parameters"":[],""returntype"":""Void"",""offset"":13332,""safe"":false},{""name"":""m17"",""parameters"":[],""returntype"":""Void"",""offset"":13353,""safe"":false},{""name"":""m18"",""parameters"":[],""returntype"":""Void"",""offset"":13375,""safe"":false},{""name"":""m19"",""parameters"":[],""returntype"":""Void"",""offset"":13397,""safe"":false},{""name"":""m20"",""parameters"":[],""returntype"":""Void"",""offset"":13419,""safe"":false},{""name"":""m21"",""parameters"":[],""returntype"":""Void"",""offset"":13441,""safe"":false},{""name"":""m22"",""parameters"":[],""returntype"":""Void"",""offset"":13463,""safe"":false},{""name"":""m23"",""parameters"":[],""returntype"":""Void"",""offset"":13485,""safe"":false},{""name"":""m24"",""parameters"":[],""returntype"":""Void"",""offset"":13507,""safe"":false},{""name"":""m25"",""parameters"":[],""returntype"":""Void"",""offset"":13529,""safe"":false},{""name"":""m26"",""parameters"":[],""returntype"":""Void"",""offset"":13551,""safe"":false},{""name"":""m27"",""parameters"":[],""returntype"":""Void"",""offset"":13573,""safe"":false},{""name"":""m28"",""parameters"":[],""returntype"":""Void"",""offset"":13595,""safe"":false},{""name"":""m29"",""parameters"":[],""returntype"":""Void"",""offset"":13617,""safe"":false},{""name"":""m30"",""parameters"":[],""returntype"":""Void"",""offset"":13639,""safe"":false},{""name"":""m31"",""parameters"":[],""returntype"":""Void"",""offset"":13661,""safe"":false},{""name"":""m32"",""parameters"":[],""returntype"":""Void"",""offset"":13683,""safe"":false},{""name"":""m33"",""parameters"":[],""returntype"":""Void"",""offset"":13705,""safe"":false},{""name"":""m34"",""parameters"":[],""returntype"":""Void"",""offset"":13727,""safe"":false},{""name"":""m35"",""parameters"":[],""returntype"":""Void"",""offset"":13749,""safe"":false},{""name"":""m36"",""parameters"":[],""returntype"":""Void"",""offset"":13771,""safe"":false},{""name"":""m37"",""parameters"":[],""returntype"":""Void"",""offset"":13793,""safe"":false},{""name"":""m38"",""parameters"":[],""returntype"":""Void"",""offset"":13815,""safe"":false},{""name"":""m39"",""parameters"":[],""returntype"":""Void"",""offset"":13837,""safe"":false},{""name"":""m40"",""parameters"":[],""returntype"":""Void"",""offset"":13859,""safe"":false},{""name"":""m41"",""parameters"":[],""returntype"":""Void"",""offset"":13881,""safe"":false},{""name"":""m42"",""parameters"":[],""returntype"":""Void"",""offset"":13903,""safe"":false},{""name"":""m43"",""parameters"":[],""returntype"":""Void"",""offset"":13925,""safe"":false},{""name"":""m44"",""parameters"":[],""returntype"":""Void"",""offset"":13947,""safe"":false},{""name"":""m45"",""parameters"":[],""returntype"":""Void"",""offset"":13969,""safe"":false},{""name"":""m46"",""parameters"":[],""returntype"":""Void"",""offset"":13991,""safe"":false},{""name"":""m47"",""parameters"":[],""returntype"":""Void"",""offset"":14013,""safe"":false},{""name"":""m48"",""parameters"":[],""returntype"":""Void"",""offset"":14035,""safe"":false},{""name"":""m49"",""parameters"":[],""returntype"":""Void"",""offset"":14057,""safe"":false},{""name"":""m50"",""parameters"":[],""returntype"":""Void"",""offset"":14079,""safe"":false},{""name"":""n"",""parameters"":[{""name"":""amountIn"",""type"":""Integer""}],""returntype"":""Void"",""offset"":14101,""safe"":false},{""name"":""n0"",""parameters"":[],""returntype"":""Integer"",""offset"":14121,""safe"":false},{""name"":""n10"",""parameters"":[],""returntype"":""Void"",""offset"":14146,""safe"":false},{""name"":""n11"",""parameters"":[],""returntype"":""Void"",""offset"":14167,""safe"":false},{""name"":""n12"",""parameters"":[],""returntype"":""Void"",""offset"":14188,""safe"":false},{""name"":""n13"",""parameters"":[],""returntype"":""Void"",""offset"":14209,""safe"":false},{""name"":""n14"",""parameters"":[],""returntype"":""Void"",""offset"":14230,""safe"":false},{""name"":""n15"",""parameters"":[],""returntype"":""Void"",""offset"":14251,""safe"":false},{""name"":""n16"",""parameters"":[],""returntype"":""Void"",""offset"":14272,""safe"":false},{""name"":""n17"",""parameters"":[],""returntype"":""Void"",""offset"":14293,""safe"":false},{""name"":""n18"",""parameters"":[],""returntype"":""Void"",""offset"":14315,""safe"":false},{""name"":""n19"",""parameters"":[],""returntype"":""Void"",""offset"":14337,""safe"":false},{""name"":""n20"",""parameters"":[],""returntype"":""Void"",""offset"":14359,""safe"":false},{""name"":""n21"",""parameters"":[],""returntype"":""Void"",""offset"":14381,""safe"":false},{""name"":""n22"",""parameters"":[],""returntype"":""Void"",""offset"":14403,""safe"":false},{""name"":""n23"",""parameters"":[],""returntype"":""Void"",""offset"":14425,""safe"":false},{""name"":""n24"",""parameters"":[],""returntype"":""Void"",""offset"":14447,""safe"":false},{""name"":""n25"",""parameters"":[],""returntype"":""Void"",""offset"":14469,""safe"":false},{""name"":""n26"",""parameters"":[],""returntype"":""Void"",""offset"":14491,""safe"":false},{""name"":""n27"",""parameters"":[],""returntype"":""Void"",""offset"":14513,""safe"":false},{""name"":""n28"",""parameters"":[],""returntype"":""Void"",""offset"":14535,""safe"":false},{""name"":""n29"",""parameters"":[],""returntype"":""Void"",""offset"":14557,""safe"":false},{""name"":""n30"",""parameters"":[],""returntype"":""Void"",""offset"":14579,""safe"":false},{""name"":""n31"",""parameters"":[],""returntype"":""Void"",""offset"":14601,""safe"":false},{""name"":""n32"",""parameters"":[],""returntype"":""Void"",""offset"":14623,""safe"":false},{""name"":""n33"",""parameters"":[],""returntype"":""Void"",""offset"":14645,""safe"":false},{""name"":""n34"",""parameters"":[],""returntype"":""Void"",""offset"":14667,""safe"":false},{""name"":""n35"",""parameters"":[],""returntype"":""Void"",""offset"":14689,""safe"":false},{""name"":""n36"",""parameters"":[],""returntype"":""Void"",""offset"":14711,""safe"":false},{""name"":""n37"",""parameters"":[],""returntype"":""Void"",""offset"":14733,""safe"":false},{""name"":""n38"",""parameters"":[],""returntype"":""Void"",""offset"":14755,""safe"":false},{""name"":""n39"",""parameters"":[],""returntype"":""Void"",""offset"":14777,""safe"":false},{""name"":""n40"",""parameters"":[],""returntype"":""Void"",""offset"":14799,""safe"":false},{""name"":""n41"",""parameters"":[],""returntype"":""Void"",""offset"":14821,""safe"":false},{""name"":""n42"",""parameters"":[],""returntype"":""Void"",""offset"":14843,""safe"":false},{""name"":""n43"",""parameters"":[],""returntype"":""Void"",""offset"":14865,""safe"":false},{""name"":""n44"",""parameters"":[],""returntype"":""Void"",""offset"":14887,""safe"":false},{""name"":""n45"",""parameters"":[],""returntype"":""Void"",""offset"":14909,""safe"":false},{""name"":""n46"",""parameters"":[],""returntype"":""Void"",""offset"":14931,""safe"":false},{""name"":""n47"",""parameters"":[],""returntype"":""Void"",""offset"":14953,""safe"":false},{""name"":""n48"",""parameters"":[],""returntype"":""Void"",""offset"":14975,""safe"":false},{""name"":""n49"",""parameters"":[],""returntype"":""Void"",""offset"":14997,""safe"":false},{""name"":""n50"",""parameters"":[],""returntype"":""Void"",""offset"":15019,""safe"":false},{""name"":""_initialize"",""parameters"":[],""returntype"":""Void"",""offset"":15041,""safe"":false}],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":{""Author"":""Test"",""Email"":""Test@Test"",""Description"":""This is a Test Contract""}}"; + var manifest = ContractManifest.Parse(json); + + var counter = new ReferenceCounter(); + var item = manifest.ToStackItem(counter); + var data = BinarySerializer.Serialize(item, 1024 * 1024, 4096); + + Assert.ThrowsException(() => BinarySerializer.Deserialize(data, ExecutionEngineLimits.Default, counter)); + Assert.ThrowsException(() => BinarySerializer.Serialize(item, 1024 * 1024, 2048)); + + item = BinarySerializer.Deserialize(data, ExecutionEngineLimits.Default with { MaxStackSize = 4096 }, counter); + var copy = item.ToInteroperable(); + + Assert.AreEqual(manifest.ToJson().ToString(false), copy.ToJson().ToString(false)); + } + + [TestMethod] + public void ParseFromJson_Default() + { + var json = @"{""name"":""testManifest"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":null}"; + var manifest = ContractManifest.Parse(json); + + Assert.AreEqual(manifest.ToJson().ToString(), json); + Assert.AreEqual(manifest.ToJson().ToString(), TestUtils.CreateDefaultManifest().ToJson().ToString()); + Assert.IsTrue(manifest.IsValid(ExecutionEngineLimits.Default, UInt160.Zero)); + } + + [TestMethod] + public void ParseFromJson_Permissions() + { + var json = @"{""name"":""testManifest"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""0x0000000000000000000000000000000000000000"",""methods"":[""method1"",""method2""]}],""trusts"":[],""extra"":null}"; + var manifest = ContractManifest.Parse(json); + Assert.AreEqual(manifest.ToJson().ToString(), json); + + var check = TestUtils.CreateDefaultManifest(); + check.Permissions = new[] + { + new ContractPermission() + { + Contract = ContractPermissionDescriptor.Create(UInt160.Zero), + Methods = WildcardContainer.Create("method1", "method2") + } + }; + Assert.AreEqual(manifest.ToJson().ToString(), check.ToJson().ToString()); + } + + [TestMethod] + public void ParseFromJson_SafeMethods() + { + var json = @"{""name"":""testManifest"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":null}"; + var manifest = ContractManifest.Parse(json); + Assert.AreEqual(manifest.ToJson().ToString(), json); + + var check = TestUtils.CreateDefaultManifest(); + Assert.AreEqual(manifest.ToJson().ToString(), check.ToJson().ToString()); + } + + [TestMethod] + public void ParseFromJson_Trust() + { + var json = @"{""name"":""testManifest"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[""0x0000000000000000000000000000000000000001"",""*""],""extra"":null}"; + var manifest = ContractManifest.Parse(json); + Assert.AreEqual(manifest.ToJson().ToString(), json); + var check = TestUtils.CreateDefaultManifest(); + check.Trusts = WildcardContainer.Create(ContractPermissionDescriptor.Create(UInt160.Parse("0x0000000000000000000000000000000000000001")), ContractPermissionDescriptor.CreateWildcard()); + Assert.AreEqual(manifest.ToJson().ToString(), check.ToJson().ToString()); + } + + [TestMethod] + public void ToInteroperable_Trust() + { + var json = @"{""name"":""CallOracleContract-6"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""request"",""parameters"":[{""name"":""url"",""type"":""String""},{""name"":""filter"",""type"":""String""},{""name"":""gasForResponse"",""type"":""Integer""}],""returntype"":""Void"",""offset"":0,""safe"":false},{""name"":""callback"",""parameters"":[{""name"":""url"",""type"":""String""},{""name"":""userData"",""type"":""Any""},{""name"":""responseCode"",""type"":""Integer""},{""name"":""response"",""type"":""ByteArray""}],""returntype"":""Void"",""offset"":86,""safe"":false},{""name"":""getStoredUrl"",""parameters"":[],""returntype"":""String"",""offset"":129,""safe"":false},{""name"":""getStoredResponseCode"",""parameters"":[],""returntype"":""Integer"",""offset"":142,""safe"":false},{""name"":""getStoredResponse"",""parameters"":[],""returntype"":""ByteArray"",""offset"":165,""safe"":false}],""events"":[]},""permissions"":[{""contract"":""0xfe924b7cfe89ddd271abaf7210a80a7e11178758"",""methods"":""*""},{""contract"":""*"",""methods"":""*""}],""trusts"":[""0xfe924b7cfe89ddd271abaf7210a80a7e11178758"",""*""],""extra"":{}}"; + var manifest = ContractManifest.Parse(json); + var s = (VM.Types.Struct)manifest.ToStackItem(new ReferenceCounter()); + manifest = s.ToInteroperable(); + + Assert.IsFalse(manifest.Permissions[0].Contract.IsWildcard); + Assert.IsTrue(manifest.Permissions[0].Methods.IsWildcard); + Assert.IsTrue(manifest.Permissions[1].Contract.IsWildcard); + Assert.IsTrue(manifest.Permissions[1].Methods.IsWildcard); + + Assert.IsFalse(manifest.Trusts[0].IsWildcard); + Assert.IsTrue(manifest.Trusts[1].IsWildcard); + } + + [TestMethod] + public void ParseFromJson_Groups() + { + var json = @"{""name"":""testManifest"",""groups"":[{""pubkey"":""03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"",""signature"":""QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ==""}],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":null}"; + var manifest = ContractManifest.Parse(json); + Assert.AreEqual(manifest.ToJson().ToString(), json); + + var check = TestUtils.CreateDefaultManifest(); + check.Groups = new ContractGroup[] { new ContractGroup() { PubKey = ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", ECCurve.Secp256r1), Signature = "41414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141".HexToBytes() } }; + Assert.AreEqual(manifest.ToJson().ToString(), check.ToJson().ToString()); + } + + [TestMethod] + public void ParseFromJson_Extra() + { + var json = @"{""name"":""testManifest"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":{""key"":""value""}}"; + var manifest = ContractManifest.Parse(json); + Assert.AreEqual(json, json); + Assert.AreEqual("value", manifest.Extra["key"].AsString(), false); + } + + [TestMethod] + public void TestDeserializeAndSerialize() + { + var expected = TestUtils.CreateDefaultManifest(); + expected.Extra = (JObject)JToken.Parse(@"{""a"":123}"); + + var clone = new ContractManifest(); + ((IInteroperable)clone).FromStackItem(expected.ToStackItem(null)); + + Assert.AreEqual(expected.Extra.ToString(), @"{""a"":123}"); + Assert.AreEqual(expected.ToString(), clone.ToString()); + + expected.Extra = null; + clone = new ContractManifest(); + ((IInteroperable)clone).FromStackItem(expected.ToStackItem(null)); + + Assert.AreEqual(expected.Extra, clone.Extra); + Assert.AreEqual(expected.ToString(), clone.ToString()); + } + + [TestMethod] + public void TestSerializeTrusts() + { + var check = TestUtils.CreateDefaultManifest(); + check.Trusts = WildcardContainer.Create(ContractPermissionDescriptor.Create(UInt160.Parse("0x0000000000000000000000000000000000000001")), ContractPermissionDescriptor.CreateWildcard()); + var si = check.ToStackItem(null); + + var actualTrusts = ((VM.Types.Array)si)[6]; + + Assert.AreEqual(((VM.Types.Array)actualTrusts).Count, 2); + Assert.AreEqual(((VM.Types.Array)actualTrusts)[0], new VM.Types.ByteString(UInt160.Parse("0x0000000000000000000000000000000000000001").ToArray())); + // Wildcard trust should be represented as Null stackitem (not as zero-length ByteString): + Assert.AreEqual(((VM.Types.Array)actualTrusts)[1], VM.Types.StackItem.Null); + } + + [TestMethod] + public void TestGenerator() + { + ContractManifest contractManifest = new(); + Assert.IsNotNull(contractManifest); + } + } +} diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractPermission.cs b/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractPermission.cs similarity index 65% rename from tests/neo.UnitTests/SmartContract/Manifest/UT_ContractPermission.cs rename to tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractPermission.cs index 25f3c36720..972a06d56d 100644 --- a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractPermission.cs +++ b/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractPermission.cs @@ -1,7 +1,19 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ContractPermission.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.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; using Neo.SmartContract; using Neo.SmartContract.Manifest; +using Neo.VM.Types; using System; namespace Neo.UnitTests.SmartContract.Manifest @@ -9,6 +21,32 @@ namespace Neo.UnitTests.SmartContract.Manifest [TestClass] public class UT_ContractPermission { + [TestMethod] + public void TestDeserialize() + { + // null + ContractPermission contractPermission = ContractPermission.DefaultPermission; + Struct s = (Struct)contractPermission.ToStackItem(new VM.ReferenceCounter()); + + contractPermission = s.ToInteroperable(); + Assert.IsTrue(contractPermission.Contract.IsWildcard); + Assert.IsTrue(contractPermission.Methods.IsWildcard); + + // not null + contractPermission = new ContractPermission() + { + Contract = ContractPermissionDescriptor.Create(UInt160.Zero), + Methods = WildcardContainer.Create("test") + }; + s = (Struct)contractPermission.ToStackItem(new VM.ReferenceCounter()); + + contractPermission = s.ToInteroperable(); + Assert.IsFalse(contractPermission.Contract.IsWildcard); + Assert.IsFalse(contractPermission.Methods.IsWildcard); + Assert.AreEqual(UInt160.Zero, contractPermission.Contract.Hash); + Assert.AreEqual("test", contractPermission.Methods[0]); + } + [TestMethod] public void TestIsAllowed() { diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs b/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs similarity index 77% rename from tests/neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs rename to tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs index 972911b296..0dee8b0f67 100644 --- a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs +++ b/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ContractPermissionDescriptor.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.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract.Manifest; using Neo.Wallets; diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs b/tests/Neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs similarity index 86% rename from tests/neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs rename to tests/Neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs index f983d65f09..610126485c 100644 --- a/tests/neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs +++ b/tests/Neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs @@ -1,6 +1,17 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_WildCardContainer.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract.Manifest; using System; using System.Collections; @@ -59,7 +70,7 @@ public void TestGetItem() public void TestGetEnumerator() { string[] s = null; - IReadOnlyList rs = (IReadOnlyList)new string[0]; + IReadOnlyList rs = new string[0]; WildcardContainer container = WildcardContainer.Create(s); IEnumerator enumerator = container.GetEnumerator(); enumerator.Should().Be(rs.GetEnumerator()); diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_ContractEventAttribute.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_ContractEventAttribute.cs new file mode 100644 index 0000000000..ba8f703253 --- /dev/null +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_ContractEventAttribute.cs @@ -0,0 +1,151 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ContractEventAttribute.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.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract; +using Neo.SmartContract.Native; + +namespace Neo.UnitTests.SmartContract.Native +{ + [TestClass] + public class UT_ContractEventAttribute + { + [TestMethod] + public void TestConstructorOneArg() + { + var arg = new ContractEventAttribute(Hardfork.HF_Basilisk, 0, "1", "a1", ContractParameterType.String); + + Assert.AreEqual(Hardfork.HF_Basilisk, arg.ActiveIn); + Assert.AreEqual(0, arg.Order); + Assert.AreEqual("1", arg.Descriptor.Name); + Assert.AreEqual(1, arg.Descriptor.Parameters.Length); + Assert.AreEqual("a1", arg.Descriptor.Parameters[0].Name); + Assert.AreEqual(ContractParameterType.String, arg.Descriptor.Parameters[0].Type); + + arg = new ContractEventAttribute(1, "1", "a1", ContractParameterType.String); + + Assert.IsNull(arg.ActiveIn); + Assert.AreEqual(1, arg.Order); + Assert.AreEqual("1", arg.Descriptor.Name); + Assert.AreEqual(1, arg.Descriptor.Parameters.Length); + Assert.AreEqual("a1", arg.Descriptor.Parameters[0].Name); + Assert.AreEqual(ContractParameterType.String, arg.Descriptor.Parameters[0].Type); + } + + [TestMethod] + public void TestConstructorTwoArg() + { + var arg = new ContractEventAttribute(Hardfork.HF_Basilisk, 0, "2", + "a1", ContractParameterType.String, + "a2", ContractParameterType.Integer); + + Assert.AreEqual(Hardfork.HF_Basilisk, arg.ActiveIn); + Assert.AreEqual(0, arg.Order); + Assert.AreEqual("2", arg.Descriptor.Name); + Assert.AreEqual(2, arg.Descriptor.Parameters.Length); + Assert.AreEqual("a1", arg.Descriptor.Parameters[0].Name); + Assert.AreEqual(ContractParameterType.String, arg.Descriptor.Parameters[0].Type); + Assert.AreEqual("a2", arg.Descriptor.Parameters[1].Name); + Assert.AreEqual(ContractParameterType.Integer, arg.Descriptor.Parameters[1].Type); + + arg = new ContractEventAttribute(1, "2", + "a1", ContractParameterType.String, + "a2", ContractParameterType.Integer); + + Assert.IsNull(arg.ActiveIn); + Assert.AreEqual(1, arg.Order); + Assert.AreEqual("2", arg.Descriptor.Name); + Assert.AreEqual(2, arg.Descriptor.Parameters.Length); + Assert.AreEqual("a1", arg.Descriptor.Parameters[0].Name); + Assert.AreEqual(ContractParameterType.String, arg.Descriptor.Parameters[0].Type); + Assert.AreEqual("a2", arg.Descriptor.Parameters[1].Name); + Assert.AreEqual(ContractParameterType.Integer, arg.Descriptor.Parameters[1].Type); + } + + [TestMethod] + public void TestConstructorThreeArg() + { + var arg = new ContractEventAttribute(Hardfork.HF_Basilisk, 0, "3", + "a1", ContractParameterType.String, + "a2", ContractParameterType.Integer, + "a3", ContractParameterType.Boolean); + + Assert.AreEqual(Hardfork.HF_Basilisk, arg.ActiveIn); + Assert.AreEqual(0, arg.Order); + Assert.AreEqual("3", arg.Descriptor.Name); + Assert.AreEqual(3, arg.Descriptor.Parameters.Length); + Assert.AreEqual("a1", arg.Descriptor.Parameters[0].Name); + Assert.AreEqual(ContractParameterType.String, arg.Descriptor.Parameters[0].Type); + Assert.AreEqual("a2", arg.Descriptor.Parameters[1].Name); + Assert.AreEqual(ContractParameterType.Integer, arg.Descriptor.Parameters[1].Type); + Assert.AreEqual("a3", arg.Descriptor.Parameters[2].Name); + Assert.AreEqual(ContractParameterType.Boolean, arg.Descriptor.Parameters[2].Type); + + arg = new ContractEventAttribute(1, "3", + "a1", ContractParameterType.String, + "a2", ContractParameterType.Integer, + "a3", ContractParameterType.Boolean); + + Assert.IsNull(arg.ActiveIn); + Assert.AreEqual(1, arg.Order); + Assert.AreEqual("3", arg.Descriptor.Name); + Assert.AreEqual(3, arg.Descriptor.Parameters.Length); + Assert.AreEqual("a1", arg.Descriptor.Parameters[0].Name); + Assert.AreEqual(ContractParameterType.String, arg.Descriptor.Parameters[0].Type); + Assert.AreEqual("a2", arg.Descriptor.Parameters[1].Name); + Assert.AreEqual(ContractParameterType.Integer, arg.Descriptor.Parameters[1].Type); + Assert.AreEqual("a3", arg.Descriptor.Parameters[2].Name); + Assert.AreEqual(ContractParameterType.Boolean, arg.Descriptor.Parameters[2].Type); + } + + [TestMethod] + public void TestConstructorFourArg() + { + var arg = new ContractEventAttribute(Hardfork.HF_Basilisk, 0, "4", + "a1", ContractParameterType.String, + "a2", ContractParameterType.Integer, + "a3", ContractParameterType.Boolean, + "a4", ContractParameterType.Array); + + Assert.AreEqual(Hardfork.HF_Basilisk, arg.ActiveIn); + Assert.AreEqual(0, arg.Order); + Assert.AreEqual("4", arg.Descriptor.Name); + Assert.AreEqual(4, arg.Descriptor.Parameters.Length); + Assert.AreEqual("a1", arg.Descriptor.Parameters[0].Name); + Assert.AreEqual(ContractParameterType.String, arg.Descriptor.Parameters[0].Type); + Assert.AreEqual("a2", arg.Descriptor.Parameters[1].Name); + Assert.AreEqual(ContractParameterType.Integer, arg.Descriptor.Parameters[1].Type); + Assert.AreEqual("a3", arg.Descriptor.Parameters[2].Name); + Assert.AreEqual(ContractParameterType.Boolean, arg.Descriptor.Parameters[2].Type); + Assert.AreEqual("a4", arg.Descriptor.Parameters[3].Name); + Assert.AreEqual(ContractParameterType.Array, arg.Descriptor.Parameters[3].Type); + + arg = new ContractEventAttribute(1, "4", + "a1", ContractParameterType.String, + "a2", ContractParameterType.Integer, + "a3", ContractParameterType.Boolean, + "a4", ContractParameterType.Array); + + Assert.IsNull(arg.ActiveIn); + Assert.AreEqual(1, arg.Order); + Assert.AreEqual("4", arg.Descriptor.Name); + Assert.AreEqual(4, arg.Descriptor.Parameters.Length); + Assert.AreEqual("a1", arg.Descriptor.Parameters[0].Name); + Assert.AreEqual(ContractParameterType.String, arg.Descriptor.Parameters[0].Type); + Assert.AreEqual("a2", arg.Descriptor.Parameters[1].Name); + Assert.AreEqual(ContractParameterType.Integer, arg.Descriptor.Parameters[1].Type); + Assert.AreEqual("a3", arg.Descriptor.Parameters[2].Name); + Assert.AreEqual(ContractParameterType.Boolean, arg.Descriptor.Parameters[2].Type); + Assert.AreEqual("a4", arg.Descriptor.Parameters[3].Name); + Assert.AreEqual(ContractParameterType.Array, arg.Descriptor.Parameters[3].Type); + } + } +} diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_ContractMethodAttribute.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_ContractMethodAttribute.cs new file mode 100644 index 0000000000..a2b746d7c8 --- /dev/null +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_ContractMethodAttribute.cs @@ -0,0 +1,32 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ContractMethodAttribute.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.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract.Native; + +namespace Neo.UnitTests.SmartContract.Native +{ + [TestClass] + public class UT_ContractMethodAttribute + { + [TestMethod] + public void TestConstructorOneArg() + { + var arg = new ContractMethodAttribute(); + + Assert.IsNull(arg.ActiveIn); + + arg = new ContractMethodAttribute(Hardfork.HF_Aspidochelone); + + Assert.AreEqual(Hardfork.HF_Aspidochelone, arg.ActiveIn); + } + } +} diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs new file mode 100644 index 0000000000..6f150cca12 --- /dev/null +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs @@ -0,0 +1,431 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_CryptoLib.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 FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.BLS12_381; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Neo.UnitTests.SmartContract.Native +{ + [TestClass] + public class UT_CryptoLib + { + private readonly byte[] g1 = "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb".ToLower().HexToBytes(); + private readonly byte[] g2 = "93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8".ToLower().HexToBytes(); + private readonly byte[] gt = "0f41e58663bf08cf068672cbd01a7ec73baca4d72ca93544deff686bfd6df543d48eaa24afe47e1efde449383b67663104c581234d086a9902249b64728ffd21a189e87935a954051c7cdba7b3872629a4fafc05066245cb9108f0242d0fe3ef03350f55a7aefcd3c31b4fcb6ce5771cc6a0e9786ab5973320c806ad360829107ba810c5a09ffdd9be2291a0c25a99a211b8b424cd48bf38fcef68083b0b0ec5c81a93b330ee1a677d0d15ff7b984e8978ef48881e32fac91b93b47333e2ba5706fba23eb7c5af0d9f80940ca771b6ffd5857baaf222eb95a7d2809d61bfe02e1bfd1b68ff02f0b8102ae1c2d5d5ab1a19f26337d205fb469cd6bd15c3d5a04dc88784fbb3d0b2dbdea54d43b2b73f2cbb12d58386a8703e0f948226e47ee89d018107154f25a764bd3c79937a45b84546da634b8f6be14a8061e55cceba478b23f7dacaa35c8ca78beae9624045b4b601b2f522473d171391125ba84dc4007cfbf2f8da752f7c74185203fcca589ac719c34dffbbaad8431dad1c1fb597aaa5193502b86edb8857c273fa075a50512937e0794e1e65a7617c90d8bd66065b1fffe51d7a579973b1315021ec3c19934f1368bb445c7c2d209703f239689ce34c0378a68e72a6b3b216da0e22a5031b54ddff57309396b38c881c4c849ec23e87089a1c5b46e5110b86750ec6a532348868a84045483c92b7af5af689452eafabf1a8943e50439f1d59882a98eaa0170f1250ebd871fc0a92a7b2d83168d0d727272d441befa15c503dd8e90ce98db3e7b6d194f60839c508a84305aaca1789b6".ToLower().HexToBytes(); + + + private readonly byte[] not_g1 = + "8123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".ToLower().HexToBytes(); + private readonly byte[] not_g2 = + "8123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" + .ToLower().HexToBytes(); + + [TestMethod] + public void TestG1() + { + var snapshot = TestBlockchain.GetTestSnapshot(); + using ScriptBuilder script = new(); + script.EmitDynamicCall(NativeContract.CryptoLib.Hash, "bls12381Deserialize", g1); + + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); + engine.LoadScript(script.ToArray()); + Assert.AreEqual(VMState.HALT, engine.Execute()); + var result = engine.ResultStack.Pop(); + result.GetInterface().ToCompressed().ToHexString().Should().Be("97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb"); + } + + [TestMethod] + public void TestG2() + { + var snapshot = TestBlockchain.GetTestSnapshot(); + using ScriptBuilder script = new(); + script.EmitDynamicCall(NativeContract.CryptoLib.Hash, "bls12381Deserialize", g2); + + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); + engine.LoadScript(script.ToArray()); + Assert.AreEqual(VMState.HALT, engine.Execute()); + var result = engine.ResultStack.Pop(); + result.GetInterface().ToCompressed().ToHexString().Should().Be("93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8"); + } + + [TestMethod] + public void TestNotG1() + { + var snapshot = TestBlockchain.GetTestSnapshot(); + using ScriptBuilder script = new(); + script.EmitDynamicCall(NativeContract.CryptoLib.Hash, "bls12381Deserialize", not_g1); + + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); + engine.LoadScript(script.ToArray()); + Assert.AreEqual(VMState.FAULT, engine.Execute()); + } + + [TestMethod] + public void TestNotG2() + { + var snapshot = TestBlockchain.GetTestSnapshot(); + using ScriptBuilder script = new(); + script.EmitDynamicCall(NativeContract.CryptoLib.Hash, "bls12381Deserialize", not_g2); + + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); + engine.LoadScript(script.ToArray()); + Assert.AreEqual(VMState.FAULT, engine.Execute()); + } + [TestMethod] + public void TestBls12381Add() + { + var snapshot = TestBlockchain.GetTestSnapshot(); + using ScriptBuilder script = new(); + script.EmitDynamicCall(NativeContract.CryptoLib.Hash, "bls12381Deserialize", gt); + script.EmitDynamicCall(NativeContract.CryptoLib.Hash, "bls12381Deserialize", gt); + script.EmitPush(2); + script.Emit(OpCode.PACK); + script.EmitPush(CallFlags.All); + script.EmitPush("bls12381Add"); + script.EmitPush(NativeContract.CryptoLib.Hash); + script.EmitSysCall(ApplicationEngine.System_Contract_Call); + + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); + engine.LoadScript(script.ToArray()); + Assert.AreEqual(VMState.HALT, engine.Execute()); + var result = engine.ResultStack.Pop(); + result.GetInterface().ToArray().ToHexString().Should().Be("079AB7B345EB23C944C957A36A6B74C37537163D4CBF73BAD9751DE1DD9C68EF72CB21447E259880F72A871C3EDA1B0C017F1C95CF79B22B459599EA57E613E00CB75E35DE1F837814A93B443C54241015AC9761F8FB20A44512FF5CFC04AC7F0F6B8B52B2B5D0661CBF232820A257B8C5594309C01C2A45E64C6A7142301E4FB36E6E16B5A85BD2E437599D103C3ACE06D8046C6B3424C4CD2D72CE98D279F2290A28A87E8664CB0040580D0C485F34DF45267F8C215DCBCD862787AB555C7E113286DEE21C9C63A458898BEB35914DC8DAAAC453441E7114B21AF7B5F47D559879D477CF2A9CBD5B40C86BECD071280900410BB2751D0A6AF0FE175DCF9D864ECAAC463C6218745B543F9E06289922434EE446030923A3E4C4473B4E3B1914081ABD33A78D31EB8D4C1BB3BAAB0529BB7BAF1103D848B4CEAD1A8E0AA7A7B260FBE79C67DBE41CA4D65BA8A54A72B61692A61CE5F4D7A093B2C46AA4BCA6C4A66CF873D405EBC9C35D8AA639763720177B23BEFFAF522D5E41D3C5310EA3331409CEBEF9EF393AA00F2AC64673675521E8FC8FDDAF90976E607E62A740AC59C3DDDF95A6DE4FBA15BEB30C43D4E3F803A3734DBEB064BF4BC4A03F945A4921E49D04AB8D45FD753A28B8FA082616B4B17BBCB685E455FF3BF8F60C3BD32A0C185EF728CF41A1B7B700B7E445F0B372BC29E370BC227D443C70AE9DBCF73FEE8ACEDBD317A286A53266562D817269C004FB0F149DD925D2C590A960936763E519C2B62E14C7759F96672CD852194325904197B0B19C6B528AB33566946AF39B".ToLower()); + } + + [TestMethod] + public void TestBls12381Mul() + { + var data = new byte[32]; + data[0] = 0x03; + var snapshot = TestBlockchain.GetTestSnapshot(); + using (ScriptBuilder script = new()) + { + script.EmitPush(false); + script.EmitPush(data); + script.EmitDynamicCall(NativeContract.CryptoLib.Hash, "bls12381Deserialize", gt); + script.EmitPush(3); + script.Emit(OpCode.PACK); + script.EmitPush(CallFlags.All); + script.EmitPush("bls12381Mul"); + script.EmitPush(NativeContract.CryptoLib.Hash); + script.EmitSysCall(ApplicationEngine.System_Contract_Call); + + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); + engine.LoadScript(script.ToArray()); + Assert.AreEqual(VMState.HALT, engine.Execute()); + var result = engine.ResultStack.Pop(); + result.GetInterface().ToArray().ToHexString().Should().Be("18B2DB6B3286BAEA116CCAD8F5554D170A69B329A6DE5B24C50B8834965242001A1C58089FD872B211ACD3263897FA660B117248D69D8AC745283A3E6A4CCEC607F6CF7CEDEE919575D4B7C8AE14C36001F76BE5FCA50ADC296EF8DF4926FA7F0B55A75F255FE61FC2DA7CFFE56ADC8775AAAB54C50D0C4952AD919D90FB0EB221C41ABB9F2352A11BE2D7F176ABE41E0E30AFB34FC2CE16136DE66900D92068F30011E9882C0A56E7E7B30F08442BE9E58D093E1888151136259D059FB539210D635BC491D5244A16CA28FDCF10546EC0F7104D3A419DDC081BA30ECB0CD2289010C2D385946229B7A9735ADC82736914FE61AD26C6C38B787775DE3B939105DE055F8D7004358272A0823F6F1787A7ABB6C3C59C8C9CBD1674AC900512632818CDD273F0D38833C07467EAF77743B70C924D43975D3821D47110A358757F926FCF970660FBDD74EF15D93B81E3AA290C78F59CBC6ED0C1E0DCBADFD11A73EB7137850D29EFEB6FA321330D0CF70F5C7F6B004BCF86AC99125F8FECF83157930BEC2AF89F8B378C6D7F63B0A07B3651F5207A84F62CEE929D574DA154EBE795D519B661086F069C9F061BA3B53DC4910EA1614C87B114E2F9EF328AC94E93D00440B412D5AE5A3C396D52D26C0CDF2156EBD3D3F60EA500C42120A7CE1F7EF80F15323118956B17C09E80E96ED4E1572461D604CDE2533330C684F86680406B1D3EE830CBAFE6D29C9A0A2F41E03E26095B713EB7E782144DB1EC6B53047FCB606B7B665B3DD1F52E95FCF2AE59C4AB159C3F98468C0A43C36C022B548189B6".ToLower()); + } + using (ScriptBuilder script = new()) + { + script.EmitPush(true); + script.EmitPush(data); + script.EmitDynamicCall(NativeContract.CryptoLib.Hash, "bls12381Deserialize", gt); + script.EmitPush(3); + script.Emit(OpCode.PACK); + script.EmitPush(CallFlags.All); + script.EmitPush("bls12381Mul"); + script.EmitPush(NativeContract.CryptoLib.Hash); + script.EmitSysCall(ApplicationEngine.System_Contract_Call); + + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); + engine.LoadScript(script.ToArray()); + Assert.AreEqual(VMState.HALT, engine.Execute()); + var result = engine.ResultStack.Pop(); + result.GetInterface().ToArray().ToHexString().Should().Be("014E367F06F92BB039AEDCDD4DF65FC05A0D985B4CA6B79AA2254A6C605EB424048FA7F6117B8D4DA8522CD9C767B0450EEF9FA162E25BD305F36D77D8FEDE115C807C0805968129F15C1AD8489C32C41CB49418B4AEF52390900720B6D8B02C0EAB6A8B1420007A88412AB65DE0D04FEECCA0302E7806761483410365B5E771FCE7E5431230AD5E9E1C280E8953C68D0BD06236E9BD188437ADC14D42728C6E7177399B6B5908687F491F91EE6CCA3A391EF6C098CBEAEE83D962FA604A718A0C9DB625A7AAC25034517EB8743B5868A3803B37B94374E35F152F922BA423FB8E9B3D2B2BBF9DD602558CA5237D37420502B03D12B9230ED2A431D807B81BD18671EBF78380DD3CF490506187996E7C72F53C3914C76342A38A536FFAED478318CDD273F0D38833C07467EAF77743B70C924D43975D3821D47110A358757F926FCF970660FBDD74EF15D93B81E3AA290C78F59CBC6ED0C1E0DCBADFD11A73EB7137850D29EFEB6FA321330D0CF70F5C7F6B004BCF86AC99125F8FECF83157930BEC2AF89F8B378C6D7F63B0A07B3651F5207A84F62CEE929D574DA154EBE795D519B661086F069C9F061BA3B53DC4910EA1614C87B114E2F9EF328AC94E93D00440B412D5AE5A3C396D52D26C0CDF2156EBD3D3F60EA500C42120A7CE1F7EF80F15323118956B17C09E80E96ED4E1572461D604CDE2533330C684F86680406B1D3EE830CBAFE6D29C9A0A2F41E03E26095B713EB7E782144DB1EC6B53047FCB606B7B665B3DD1F52E95FCF2AE59C4AB159C3F98468C0A43C36C022B548189B6".ToLower()); + } + } + + [TestMethod] + public void TestBls12381Pairing() + { + var snapshot = TestBlockchain.GetTestSnapshot(); + using ScriptBuilder script = new(); + script.EmitDynamicCall(NativeContract.CryptoLib.Hash, "bls12381Deserialize", g2); + script.EmitDynamicCall(NativeContract.CryptoLib.Hash, "bls12381Deserialize", g1); + script.EmitPush(2); + script.Emit(OpCode.PACK); + script.EmitPush(CallFlags.All); + script.EmitPush("bls12381Pairing"); + script.EmitPush(NativeContract.CryptoLib.Hash); + script.EmitSysCall(ApplicationEngine.System_Contract_Call); + + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); + engine.LoadScript(script.ToArray()); + Assert.AreEqual(VMState.HALT, engine.Execute()); + var result = engine.ResultStack.Pop(); + result.GetInterface().ToArray().ToHexString().Should().Be("0F41E58663BF08CF068672CBD01A7EC73BACA4D72CA93544DEFF686BFD6DF543D48EAA24AFE47E1EFDE449383B67663104C581234D086A9902249B64728FFD21A189E87935A954051C7CDBA7B3872629A4FAFC05066245CB9108F0242D0FE3EF03350F55A7AEFCD3C31B4FCB6CE5771CC6A0E9786AB5973320C806AD360829107BA810C5A09FFDD9BE2291A0C25A99A211B8B424CD48BF38FCEF68083B0B0EC5C81A93B330EE1A677D0D15FF7B984E8978EF48881E32FAC91B93B47333E2BA5706FBA23EB7C5AF0D9F80940CA771B6FFD5857BAAF222EB95A7D2809D61BFE02E1BFD1B68FF02F0B8102AE1C2D5D5AB1A19F26337D205FB469CD6BD15C3D5A04DC88784FBB3D0B2DBDEA54D43B2B73F2CBB12D58386A8703E0F948226E47EE89D018107154F25A764BD3C79937A45B84546DA634B8F6BE14A8061E55CCEBA478B23F7DACAA35C8CA78BEAE9624045B4B601B2F522473D171391125BA84DC4007CFBF2F8DA752F7C74185203FCCA589AC719C34DFFBBAAD8431DAD1C1FB597AAA5193502B86EDB8857C273FA075A50512937E0794E1E65A7617C90D8BD66065B1FFFE51D7A579973B1315021EC3C19934F1368BB445C7C2D209703F239689CE34C0378A68E72A6B3B216DA0E22A5031B54DDFF57309396B38C881C4C849EC23E87089A1C5B46E5110B86750EC6A532348868A84045483C92B7AF5AF689452EAFABF1A8943E50439F1D59882A98EAA0170F1250EBD871FC0A92A7B2D83168D0D727272D441BEFA15C503DD8E90CE98DB3E7B6D194F60839C508A84305AACA1789B6".ToLower()); + } + + [TestMethod] + public void Bls12381Equal() + { + var snapshot = TestBlockchain.GetTestSnapshot(); + using ScriptBuilder script = new(); + script.EmitDynamicCall(NativeContract.CryptoLib.Hash, "bls12381Deserialize", g1); + script.EmitDynamicCall(NativeContract.CryptoLib.Hash, "bls12381Deserialize", g1); + script.EmitPush(2); + script.Emit(OpCode.PACK); + script.EmitPush(CallFlags.All); + script.EmitPush("bls12381Equal"); + script.EmitPush(NativeContract.CryptoLib.Hash); + script.EmitSysCall(ApplicationEngine.System_Contract_Call); + + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); + engine.LoadScript(script.ToArray()); + Assert.AreEqual(VMState.HALT, engine.Execute()); + var result = engine.ResultStack.Pop(); + result.GetBoolean().Should().BeTrue(); + } + + private enum BLS12381PointType : byte + { + G1Proj, + G2Proj, + GT + } + + private void CheckBls12381ScalarMul_Compat(string point, string mul, bool negative, string expected, BLS12381PointType expectedType) + { + var data = new byte[32]; + data[0] = 0x03; + var snapshot = TestBlockchain.GetTestSnapshot(); + using (ScriptBuilder script = new()) + { + script.EmitPush(negative); + script.EmitPush(mul.ToLower().HexToBytes()); + script.EmitDynamicCall(NativeContract.CryptoLib.Hash, "bls12381Deserialize", point.ToLower().HexToBytes()); + script.EmitPush(3); + script.Emit(OpCode.PACK); + script.EmitPush(CallFlags.All); + script.EmitPush("bls12381Mul"); + script.EmitPush(NativeContract.CryptoLib.Hash); + script.EmitSysCall(ApplicationEngine.System_Contract_Call); + + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); + engine.LoadScript(script.ToArray()); + Assert.AreEqual(VMState.HALT, engine.Execute()); + var result = engine.ResultStack.Pop(); + switch (expectedType) + { + case BLS12381PointType.G1Proj: + { + new G1Affine(result.GetInterface()).ToCompressed().ToHexString().Should().Be(expected); + break; + } + case BLS12381PointType.G2Proj: + { + new G2Affine(result.GetInterface()).ToCompressed().ToHexString().Should().Be(expected); + break; + } + case BLS12381PointType.GT: + { + result.GetInterface().ToArray().ToHexString().Should().Be(expected); + break; + } + default: + Assert.Fail("Unknown result point type."); + break; + } + } + } + + [TestMethod] + public void TestBls12381ScalarMul_Compat() + { + // GT mul by positive scalar. + CheckBls12381ScalarMul_Compat( + "14fd52fe9bfd08bbe23fcdf1d3bc5390c62e75a8786a72f8a343123a30a7c5f8d18508a21a2bf902f4db2c068913bc1c130e7ce13260d601c89ee717acfd3d4e1d80f409dd2a5c38b176f0b64d3d0a224c502717270dfecf2b825ac24608215c0d7fcfdf3c1552ada42b7e0521bc2e7389436660c352ecbf2eedf30b77b6b501df302399e6240473af47abe56fc974780c214542fcc0cf10e3001fa5e82d398f6ba1ddd1ccdf133bfd75e033eae50aec66bd5e884b8c74d4c1c6ac7c01278ac5164a54600cb2e24fec168f82542fbf98234dbb9ddf06503dc3c497da88b73db584ba19e685b1b398b51f40160e6c8f0917b4a68dedcc04674e5f5739cf0d845ba801263f712ed4ddda59c1d9909148e3f28124ae770682c9b19233bf0bcfa00d05bfe708d381b066b83a883ba8251ce2ea6772cbde51e1322d82b2c8a026a2153f4822e20cb69b8b05003ee74e09cb481728d688caa8a671f90b55488e272f48c7c5ae32526d3635a5343eb02640358d9ac445c76a5d8f52f653bbaee04ba5ce03c68b88c25be6fd3611cc21c9968e4f87e541beeccc5170b8696a439bb666ad8a6608ab30ebc7dfe56eaf0dd9ab8439171a6e4e0d608e6e6c8ac5ddcf8d6d2a950d06051e6b6c4d3feb6dc8dac2acadd345cadfb890454a2101a112f7471f0e001701f60f3d4352c4d388c0f198854908c0e939719709c1b3f82d2a25cc7156a3838bc141e041c259849326fbd0839f15cea6a78b89349dcd1c03695a74e72d3657af4ee2cf267337bc96363ef4a1c5d5d7a673cc3a3c1a1350043f99537d62", + "8463159bd9a1d1e1fd815172177ec24c0c291353ed88b3d1838fd9d63b1efd0b", + false, + "03dc980ce0c037634816f9fc1edb2e1807e38a51f838e3a684f195d6c52c41d6a8a5b64d57d3fda507bebe3bd4b661af0e4f7c46754b373c955982b4d64a24838cbc010d04b6ceb499bf411d114dab77eaf70f96ab66c2868dcd63706b602b07010c487fc16c90b61e1c2ad33c31c8f3fc86d114a59b127ac584640f149f3597102c55dd1ed8a305a10c052c0a724e570fc079e410123735a6144ccd88d9e4e91d7b889f80b18a1741eacd6f244fce3cf57795e619b6648b9238053b4b8e4ed6115c905fbcb61525370667ff43144e12b700662a7344ac1af97f11d09779ca6865973f95ff318b42ff00df7c6eb958160947a0ab6cb25534af51ce1f0b076907c6eb5ce0760bd7670cab8814cc3308766eb6e52b5427dbf85d6424990fd3354515ab880358bc55075a08f36b855694c02ee0bd63adefe235ba4ee41dc600a1cae950c1dc760bf7b1edd8712e9e90eebb19de705e29f4feb870129441bd4b9e91c3d37e60c12fa79a5b1e4132ba9498044e6fbf2de37e4dd88b4e9095b46f122019e73a561ba3967b32813c3ec74b8e1b6ab619eeab698e6638114cb29ca9c3d353192db3d392fee2b4dfdfd36b13db440534dd754417cffcd470f4d4cfdcb6d7896181c27b8b30622d7a4ca0a05a7ea67ca011cab07738235b115bbd330239691487d2de5d679a8cad2fe5c7fff16b0b0f3f929619c8005289c3d7ffe5bcd5ea19651bfc9366682a2790cab45ee9a98815bb7e58dc666e2209cd9d700546cf181ceb43fe719243930984b696b0d18d4cd1f5d960e149a2b753b1396e4f8f3b16", + BLS12381PointType.GT + ); + // GT mul by positive scalar. + CheckBls12381ScalarMul_Compat( + "0e0c651ff4a57adebab1fa41aa8d1e53d1cf6a6cc554282a24bb460ea0dc169d3ede8b5a93a331698f3926d273a729aa18788543413f43ada55a6a7505e3514f0db7e14d58311c3211962a350bcf908b3af90fbae31ff536fe542328ad25cd3e044a796200c8a8ead7edbc3a8a37209c5d37433ca7d8b0e644d7aac9726b524c41fef1cf0d546c252d795dffc445ddee07041f57c4c9a673bd314294e280ab61390731c09ad904bdd7b8c087d0ce857ea86e78f2d98e75d9b5e377e5751d67cf1717cbce31bc7ea6df95132549bf6d284a68005c53228127671afa54ecfd4c5c4debc437c4c6d9b9aeeee8b4159a5691128c6dc68b309fd822b14f3ce8ff390bd6834d30147e8ab2edc59d0d7b14cc13c79e6eed5fd6cae1795ba3760345d59c0c585f79c900902515e3e95938d9929ad8310e71fc7fd54be9c7529f244af40dadaca0b3bd8afd911f24b261079de48b161dd8f340d42bd84e717275193a0375d9e10fbe048bbea30abd64d3fe085c15b9be192f7baaa0b3a9658bcbb4292a0c0149beb30e54b065a75df45e5da77583f4471e3454cea90a00b5a9a224c15e2ebe01f0ab8aa86591c1012c618d41fdce07ecfcaddc8dc408b7176b79d8711a4161a56f41a5be6714cbcaa70e53387ab049826ac9e636640bc6da919e52f86f3209572b62d9bfd48bd2b5ef217932237b90a70d40167623d0f25a73b753e3214310bc5b6e017aebc1a9ca0c8067a97da6162c70cc754f1b2ac3b05ba834712758c8de4641ef09237edf588989182ab3047ee42da2b840fd3633fa0f34d46ad961", + "06c93a0ebbc8b5cd3af798b8f72442a67aa885b395452a08e48ec80b4e9f1b3f", + false, + "0d6d91f120ab61e14a3163601ce584f053f1de9dc0a548b6fbf37a776ec7b6ce6b866e8c8b0fc0ac8d32a9a9747c98bf0e6aee5bddd058313958bfc3ac1ed75284628f92bb9b99fee101e1bee9d74bad7812287ea76bdbe07f20ff9998d6e9f016689be1cfc4337433644a679945d5c34a6d4dd984c56d6c28428438268b385cb1d86f69b0377b18f9b084e1d0b6596213233d559a1b5caaba38be853f667fc3b1f9f2c4c9020584502ff5f370b0aba7768a1a4ca4328bc3c7be2bc9c3949f5e16fd3bfc16b11da41b7393e56e777640b000db15b6e6192e5c59dfece90c6fc0b6071fdeef7061974b5e967c5b88b1db09f7c92077c16f56aff9e9627f5e09928e965daee17d05ef3fdc0c502b649db473b5b2bba867d829b04d32cfeab7387614190b265382378f75e4e085a5537d4f200fe56b74b7c52c5546b30d51862e1ac1f60eba157880090a42ea9b0295529f134c1fc90f19a4c20dc0be105b07e0c67218b2f5619a66d8d770d539658eb74c255743e5847bc437fef3077d0a6c4f17198d63cf17e6957f2ad9449269af009635697e92254a3f67be9b8760fd9f974826a1829fedb4cf66968b7c63b0c88c510da12e6d52255256757afa03ad29b5c1624292ef7eb463eb4bc81ac7426f36db3fe1513bdd31bc138bfe903bbb0c5207001335f708c16cea15ef6b77c3215326a779e927b8c2081b15adffe71ba75164e376665533c5bb59373b27dbe93a0a0e1796d821a1b9ff01846446c5ad53064cb9b941f97aa870285395e1a44c9f6e5144ea5a0cf57b9fdd962a5ec3ff1f72fe", + BLS12381PointType.GT + ); + // GT mul by positive scalar. + CheckBls12381ScalarMul_Compat( + "0e0c651ff4a57adebab1fa41aa8d1e53d1cf6a6cc554282a24bb460ea0dc169d3ede8b5a93a331698f3926d273a729aa18788543413f43ada55a6a7505e3514f0db7e14d58311c3211962a350bcf908b3af90fbae31ff536fe542328ad25cd3e044a796200c8a8ead7edbc3a8a37209c5d37433ca7d8b0e644d7aac9726b524c41fef1cf0d546c252d795dffc445ddee07041f57c4c9a673bd314294e280ab61390731c09ad904bdd7b8c087d0ce857ea86e78f2d98e75d9b5e377e5751d67cf1717cbce31bc7ea6df95132549bf6d284a68005c53228127671afa54ecfd4c5c4debc437c4c6d9b9aeeee8b4159a5691128c6dc68b309fd822b14f3ce8ff390bd6834d30147e8ab2edc59d0d7b14cc13c79e6eed5fd6cae1795ba3760345d59c0c585f79c900902515e3e95938d9929ad8310e71fc7fd54be9c7529f244af40dadaca0b3bd8afd911f24b261079de48b161dd8f340d42bd84e717275193a0375d9e10fbe048bbea30abd64d3fe085c15b9be192f7baaa0b3a9658bcbb4292a0c0149beb30e54b065a75df45e5da77583f4471e3454cea90a00b5a9a224c15e2ebe01f0ab8aa86591c1012c618d41fdce07ecfcaddc8dc408b7176b79d8711a4161a56f41a5be6714cbcaa70e53387ab049826ac9e636640bc6da919e52f86f3209572b62d9bfd48bd2b5ef217932237b90a70d40167623d0f25a73b753e3214310bc5b6e017aebc1a9ca0c8067a97da6162c70cc754f1b2ac3b05ba834712758c8de4641ef09237edf588989182ab3047ee42da2b840fd3633fa0f34d46ad961", + "b0010000000000005e0000000000000071f30400000000006d9189c813000000", + false, + "0919ad29cdbe0b6bbd636fbe3c8930a1b959e5aa37294a6cc7d018e2776580768bb98bf91ce1bc97f2e6fa647e7dad7b15db564645d2e4868129ed414b7e369e831b8ff93997a22b6ca0e2ba288783f535aed4b44cf3e952897db1536da18a120a70da2b9dd901bd12a5a7047d3b6346ba1aea53b642b7355a91f957687fccd840ef24af100d0ada6b49e35183456ec30b505098526b975477b6ca0273d3a841c85e4a8319b950e76ec217a4f939844baa6b875a4046a30c618636fe9b25c620030f31044f883789945c2bcb75d7d4099b2bc97665e75c1bee27bc3864e7e5e2ccb57a9da0b57be1a6aca217a6cfda090c4fd222f7b8cfdc32969da4fe8828a59ee1314546efdf99ef7ede1a42df6e7a126fe83b4c41b5e70a56bd9ab499f7e80e27a08884be05f1d2a527417fc6e30448333c0724463bf92d722ef5fd6f06949e294e6f941976d24c856038b55a2ec200d14d958a688f23b572993bd0f18cbbc20defe88e423b262c552dcc4d9f63ad78e85efbcea9449f81f39e1a887eb79b07056bb5a672444e240660617ba7a40985a622c687c1d05c12cee7b086abfc5f39a83a5ad7638ee559f710013b772d4207924687cb30100bcd4e8c83c9fa19dce7785bf3ae7681a0968fd9661c990e2dace05902dceeed65aacf51a04e72f0fd04858ea70fb72f2a3807dc1839a385d85b536abfd3ec76d4931b3bc5ec4d90e2ebc0342567c9507abdfafa602fc6983f13f20eb26b4169dc3908109fe3c1887db4be8f30edad989dc8caa234f9818ac488b110ad30a30f769277168650b6910e", + BLS12381PointType.GT + ); + // GT mul by negative scalar. + CheckBls12381ScalarMul_Compat( + "0bdbfc3b68e7067630a1908de2ce15e1890d57b855ffc2ee0fe765293581c304d0507254fd9921d8ff4bff3185b1e8ae017091a6b9e243c3108b4302f30e2f4cb452c4574d23d06942cf915fb0b64c3546aa0bfbba5182dc42b63ebd09cd950f06ebf85ff360032e63d5422fed5969b80ed4abaf58d29317d9cf8e5a55744993ffc0ccc586a187c63f9c47d4b41870aa0fd73e13a4f7d3b072407a3bfa6539f8d56856542b17326ab77833df274e61a41c237a6dbf20a333698a675fded6ab1a114891795eabbedcb81590ff9bfb4b23b66c8b8376a69cf58511c80f3ac83d52c0c950be8c30d01108479f232d8e4e8919d869dc85db0b9d6ccf40eb8f8ab08e43a910c341737a55e751fa4a097ee82c5ac83d38c543d957bd9850af16039d1a00c96575d2ee24e9990b3401153446aa6593d3afb6ce7ca57d6432b8dda31aaa1a08834ad38deae5a807d11663adc5c20ae7227a2cbb7917d1489175b89ed1ba415e4fc55b7d0a286caf2f5f40b0dd39cdd8fc8c271d8a7ae952fe6ece5f7c1019bfab0167af86314a73bfa37fd16bc6edff6d9ee75610a4eec1818c668ef9f509b1cdd54542e73dc0e343a4fd6e3bb618540c1d060b60b63b645a895105425eb813b08b6ac91be3145da04040f2a45ffcf06e96b685519fca93b0f15238dc0e030c2199127ba82fa8a193f5f01ae24270e9669923653db38cae711d68169aa25df51a8915f3f8219892f4f5e67d550b00910011685017dcc1777a9d48689ce590d57c1fc942d49cfad0ed7efc0169a95d7e7378af26bafb90d1619bcdab64cd", + "688e58217305c1fd2fe0637cbd8e7414d4d0a2113314eb05592f97930d23b34d", + true, + "056fdc84f044148950c0b7c4c0613f5710fcaeb1b023b9d8f814dc39d48702db70ce41aa276566960e37237f22b086b017b9ed0e264e2b7872c8a7affb8b9f847a528d092a038dab4ac58d3a33d30e2e5078b5e39ebb7441c56ae7556b63ecd6139ed9be1c5eb9f987cc704c913c1e23d44d2e04377347f6c471edc40cdb2cd4e32c396194363cd21ceff9bedbd164a41050e701012f0456383210f8054e76c0906e3f37e10d4a3d6342e79e39d566ea785b385bb692cddbd6c16456dfabf19f0f84c27ec4bce096af0369ac070747cd89d97bc287afe5ed5e495ed2d743adbd8eec47df6c3a69628e803e23d824845800e44a8d874756a7541128892e55e9df1d1fe0583ef967db6740617a9ff50766866c0fa631aed8639cd0c13d3d6f6f210b340ee315caec4cc31c916d651db5e002e259fca081fb605258ccf692d786bd5bb45a054c4d8498ac2a7fa241870df60ba0fd8a2b063740af11e7530db1e758a8e2858a443104b8337e18c083035768a0e93126f116bb9c50c8cebe30e0ceaa0c0b53eb2b6a1f96b34b6cc36f3417edda184e19ae1790d255337f14315323e1d2d7382b344bdc0b6b2cfab5837c24c916640ca351539d5459389a9c7f9b0d79e04e4a8392e0c2495dcecf7d48b10c7043825b7c6709108d81856ebf98385f0d099e6521714c48b8eb5d2e97665375175f47c57d427d35a9dc44064a99d1c079028e36d34540baba947333ab3c8976b801ea48578159f041e740ea5bf73c1de3c1043a6e03311d0f2463b72694249ccc5d603e4a93cfd8a6713fb0470383c23f", + BLS12381PointType.GT + ); + // GT mul by zero scalar. + CheckBls12381ScalarMul_Compat( + "176ec726aa447f1791e69fc70a71103c84b17385094ef06a9a0235ac7241f6635377f55ad486c216c8701d61ea2ace3e05ca1605f238dc8f29f868b795e45645c6f7ff8d9d8ffd77b5e149b0325c2a8f24dde40e80a3381ae72a9a1104ef02d70af7cf8f2fe6ff38961b352b0fde6f8536424fc9aa5805b8e12313bdfc01d5c1db1c0a37654c307fbd252c265dcbfc040ee5605ffd6ac20aab15b0343e47831f4157a20ecedd7350d2cf070c0c7d423786fd97aa7236b99f4462fb23e173528815bf2cf3ccbfc38303fa8154d70ee5e1e3158cbb14d5c87a773cbe948a5cfec2763c5e7129940906920aed344453b0f801760fd3eac8e254ce8e0ae4edd30c914bea9e2935acd4a6a9d42d185a9a6e786c8e462b769b2112423f6591b093347718897438ba918b9e4525888194b20ee17709f7dea319cfd053bb1c222783340326953fd3763eb6feaaa4d1458ee6ca001818ad88222a97e43a71dca8d2abaef70657b9ff7b94ca422d0c50ddb4265fa35514ed534217ce2f0219c6985ec2827a0ee1dc17940926551072d693d89e36e6d14162f414b52587e5612ed4a562c9ac15df9d5fa68ccf61d52fea64b2f5d7a600e0a8fa735105bc9a2ecb69b6d9161e55a4ccdc2285164c6846fa5bdc106d1e0693ebd5fe86432e5e88c55f0159ec3217332c8492332dfbd93970f002a6a05f23484e081f38815785e766779c843765d58b2444295a87939ad7f8fa4c11e8530a62426063c9a57cf3481a00372e443dc014fd6ef4723dd4636105d7ce7b96c4b2b3b641c3a2b6e0fa9be6187e5bfaf9", + "0000000000000000000000000000000000000000000000000000000000000000", + false, + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + BLS12381PointType.GT + ); + // G1Affine mul by positive scalar. + CheckBls12381ScalarMul_Compat( + "a1f9855f7670a63e4c80d64dfe6ddedc2ed2bfaebae27e4da82d71ba474987a39808e8921d3df97df6e5d4b979234de8", + "8463159bd9a1d1e1fd815172177ec24c0c291353ed88b3d1838fd9d63b1efd0b", + false, + "ae85e3e2d677c9e3424ed79b5a7554262c3d6849202b84d2e7024e4b1f2e9dd3f7cf20b807a9f2a67d87e47e9e94d361", + BLS12381PointType.G1Proj + ); + // G1Affine mul by negative scalar. + CheckBls12381ScalarMul_Compat( + "a1f9855f7670a63e4c80d64dfe6ddedc2ed2bfaebae27e4da82d71ba474987a39808e8921d3df97df6e5d4b979234de8", + "8463159bd9a1d1e1fd815172177ec24c0c291353ed88b3d1838fd9d63b1efd0b", + true, + "8e85e3e2d677c9e3424ed79b5a7554262c3d6849202b84d2e7024e4b1f2e9dd3f7cf20b807a9f2a67d87e47e9e94d361", + BLS12381PointType.G1Proj + ); + // G1Affine mul by zero scalar. + CheckBls12381ScalarMul_Compat( + "a1f9855f7670a63e4c80d64dfe6ddedc2ed2bfaebae27e4da82d71ba474987a39808e8921d3df97df6e5d4b979234de8", + "0000000000000000000000000000000000000000000000000000000000000000", + false, + "c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + BLS12381PointType.G1Proj + ); + // G2Affine mul by positive scalar. + CheckBls12381ScalarMul_Compat( + "a41e586fdd58d39616fea921a855e65417a5732809afc35e28466e3acaeed3d53dd4b97ca398b2f29bf6bbcaca026a6609a42bdeaaeef42813ae225e35c23c61c293e6ecb6759048fb76ac648ba3bc49f0fcf62f73fca38cdc5e7fa5bf511365", + "cbfffe3e37e53e31306addde1a1725641fbe88cd047ee7477966c44a3f764b47", + false, + "88ae9bba988e854877c66dfb7ff84aa5e107861aa51d1a2a8dac2414d716a7e219bc4b0239e4b12d2182f57b5eea82830639f2e6713098ae8d4b4c3942f366614bac35c91c83ecb57fa90fe03094aca1ecd3555a7a6fdfa2417b5bb06917732e", + BLS12381PointType.G2Proj + ); + // G2Affine mul by negative scalar. + CheckBls12381ScalarMul_Compat( + "a41e586fdd58d39616fea921a855e65417a5732809afc35e28466e3acaeed3d53dd4b97ca398b2f29bf6bbcaca026a6609a42bdeaaeef42813ae225e35c23c61c293e6ecb6759048fb76ac648ba3bc49f0fcf62f73fca38cdc5e7fa5bf511365", + "cbfffe3e37e53e31306addde1a1725641fbe88cd047ee7477966c44a3f764b47", + true, + "a8ae9bba988e854877c66dfb7ff84aa5e107861aa51d1a2a8dac2414d716a7e219bc4b0239e4b12d2182f57b5eea82830639f2e6713098ae8d4b4c3942f366614bac35c91c83ecb57fa90fe03094aca1ecd3555a7a6fdfa2417b5bb06917732e", + BLS12381PointType.G2Proj + ); + // G2Affine mul by negative scalar. + CheckBls12381ScalarMul_Compat( + "a41e586fdd58d39616fea921a855e65417a5732809afc35e28466e3acaeed3d53dd4b97ca398b2f29bf6bbcaca026a6609a42bdeaaeef42813ae225e35c23c61c293e6ecb6759048fb76ac648ba3bc49f0fcf62f73fca38cdc5e7fa5bf511365", + "0000000000000000000000000000000000000000000000000000000000000000", + false, + "c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + BLS12381PointType.G2Proj + ); + } + + /// + /// Keccak256 cases are verified in https://emn178.github.io/online-tools/keccak_256.html + /// + [TestMethod] + public void TestKeccak256_HelloWorld() + { + // Arrange + byte[] inputData = "Hello, World!"u8.ToArray(); + string expectedHashHex = "acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f"; + + // Act + byte[] outputData = CryptoLib.Keccak256(inputData); + string outputHashHex = Hex.ToHexString(outputData); + + // Assert + Assert.AreEqual(expectedHashHex, outputHashHex, "Keccak256 hash did not match expected value for 'Hello, World!'."); + } + [TestMethod] + public void TestKeccak256_Keccak() + { + // Arrange + byte[] inputData = "Keccak"u8.ToArray(); + string expectedHashHex = "868c016b666c7d3698636ee1bd023f3f065621514ab61bf26f062c175fdbe7f2"; + + // Act + byte[] outputData = CryptoLib.Keccak256(inputData); + string outputHashHex = Hex.ToHexString(outputData); + + // Assert + Assert.AreEqual(expectedHashHex, outputHashHex, "Keccak256 hash did not match expected value for 'Keccak'."); + } + + [TestMethod] + public void TestKeccak256_Cryptography() + { + // Arrange + byte[] inputData = "Cryptography"u8.ToArray(); + string expectedHashHex = "53d49d225dd2cfe77d8c5e2112bcc9efe77bea1c7aa5e5ede5798a36e99e2d29"; + + // Act + byte[] outputData = CryptoLib.Keccak256(inputData); + string outputHashHex = Hex.ToHexString(outputData); + + // Assert + Assert.AreEqual(expectedHashHex, outputHashHex, "Keccak256 hash did not match expected value for 'Cryptography'."); + } + + [TestMethod] + public void TestKeccak256_Testing123() + { + // Arrange + byte[] inputData = "Testing123"u8.ToArray(); + string expectedHashHex = "3f82db7b16b0818a1c6b2c6152e265f682d5ebcf497c9aad776ad38bc39cb6ca"; + + // Act + byte[] outputData = CryptoLib.Keccak256(inputData); + string outputHashHex = Hex.ToHexString(outputData); + + // Assert + Assert.AreEqual(expectedHashHex, outputHashHex, "Keccak256 hash did not match expected value for 'Testing123'."); + } + + [TestMethod] + public void TestKeccak256_LongString() + { + // Arrange + byte[] inputData = "This is a longer string for Keccak256 testing purposes."u8.ToArray(); + string expectedHashHex = "24115e5c2359f85f6840b42acd2f7ea47bc239583e576d766fa173bf711bdd2f"; + + // Act + byte[] outputData = CryptoLib.Keccak256(inputData); + string outputHashHex = Hex.ToHexString(outputData); + + // Assert + Assert.AreEqual(expectedHashHex, outputHashHex, "Keccak256 hash did not match expected value for the longer string."); + } + + [TestMethod] + public void TestKeccak256_BlankString() + { + // Arrange + byte[] inputData = ""u8.ToArray(); + string expectedHashHex = "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"; + + // Act + byte[] outputData = CryptoLib.Keccak256(inputData); + string outputHashHex = Hex.ToHexString(outputData); + + // Assert + Assert.AreEqual(expectedHashHex, outputHashHex, "Keccak256 hash did not match expected value for blank string."); + } + } +} diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs similarity index 52% rename from tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs rename to tests/Neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs index 966f0b460c..5d6d00986e 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_FungibleToken.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 Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_GasToken.cs similarity index 76% rename from tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs rename to tests/Neo.UnitTests/SmartContract/Native/UT_GasToken.cs index dc65567377..51617b6744 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_GasToken.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_GasToken.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; @@ -40,12 +51,14 @@ public async Task Check_BalanceOfTransferAndBurn() { var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block { Header = new Header { Index = 1000 } }; - byte[] from = Contract.GetBFTAddress(ProtocolSettings.Default.StandbyValidators).ToArray(); + byte[] from = Contract.GetBFTAddress(TestProtocolSettings.Default.StandbyValidators).ToArray(); byte[] to = new byte[20]; - var keyCount = snapshot.GetChangeSet().Count(); var supply = NativeContract.GAS.TotalSupply(snapshot); supply.Should().Be(5200000050000000); // 3000000000000000 + 50000000 (neo holder reward) + var storageKey = new KeyBuilder(NativeContract.Ledger.Id, 12); + snapshot.Add(storageKey, new StorageItem(new HashIndexState { Hash = UInt256.Zero, Index = persistingBlock.Index - 1 })); + var keyCount = snapshot.GetChangeSet().Count(); // Check unclaim var unclaim = UT_NeoToken.Check_UnclaimedGas(snapshot, from, persistingBlock); @@ -92,7 +105,7 @@ public async Task Check_BalanceOfTransferAndBurn() // Burn using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings, gas: 0); - keyCount = snapshot.GetChangeSet().Count(); + engine.LoadScript(Array.Empty()); await Assert.ThrowsExceptionAsync(async () => await NativeContract.GAS.Burn(engine, new UInt160(to), BigInteger.MinusOne)); @@ -106,21 +119,20 @@ await Assert.ThrowsExceptionAsync(async () => await NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(1)); - NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(5200049999999999); + NativeContract.GAS.BalanceOf(engine.Snapshot, to).Should().Be(5200049999999999); - keyCount.Should().Be(snapshot.GetChangeSet().Count()); + engine.Snapshot.GetChangeSet().Count().Should().Be(2); // Burn all - await NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(5200049999999999)); - (keyCount - 1).Should().Be(snapshot.GetChangeSet().Count()); + (keyCount - 2).Should().Be(engine.Snapshot.GetChangeSet().Count()); // Bad inputs - Assert.ThrowsException(() => NativeContract.GAS.Transfer(snapshot, from, to, BigInteger.MinusOne, true, persistingBlock)); - Assert.ThrowsException(() => NativeContract.GAS.Transfer(snapshot, new byte[19], to, BigInteger.One, false, persistingBlock)); - Assert.ThrowsException(() => NativeContract.GAS.Transfer(snapshot, from, new byte[19], BigInteger.One, false, persistingBlock)); + Assert.ThrowsException(() => NativeContract.GAS.Transfer(engine.Snapshot, from, to, BigInteger.MinusOne, true, persistingBlock)); + Assert.ThrowsException(() => NativeContract.GAS.Transfer(engine.Snapshot, new byte[19], to, BigInteger.One, false, persistingBlock)); + Assert.ThrowsException(() => NativeContract.GAS.Transfer(engine.Snapshot, from, new byte[19], BigInteger.One, false, persistingBlock)); } internal static StorageKey CreateStorageKey(byte prefix, uint key) @@ -130,14 +142,14 @@ internal static StorageKey CreateStorageKey(byte prefix, uint key) internal static StorageKey CreateStorageKey(byte prefix, byte[] key = null) { - StorageKey storageKey = new() + byte[] buffer = GC.AllocateUninitializedArray(sizeof(byte) + (key?.Length ?? 0)); + buffer[0] = prefix; + key?.CopyTo(buffer.AsSpan(1)); + return new() { - Id = NativeContract.NEO.Id, - Key = new byte[sizeof(byte) + (key?.Length ?? 0)] + Id = NativeContract.GAS.Id, + Key = buffer }; - storageKey.Key[0] = prefix; - key?.CopyTo(storageKey.Key.AsSpan(1)); - return storageKey; } } } diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs new file mode 100644 index 0000000000..7f23519e9f --- /dev/null +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -0,0 +1,47 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_NativeContract.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.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract.Native; +using System.IO; + +namespace Neo.UnitTests.SmartContract.Native +{ + [TestClass] + public class UT_NativeContract + { + [TestMethod] + public void TestGetContract() + { + Assert.IsTrue(NativeContract.NEO == NativeContract.GetContract(NativeContract.NEO.Hash)); + } + + [TestMethod] + public void TestIsInitializeBlock() + { + string json = UT_ProtocolSettings.CreateHKSettings("\"HF_Cockatrice\": 20"); + + var file = Path.GetTempFileName(); + File.WriteAllText(file, json); + ProtocolSettings settings = ProtocolSettings.Load(file, false); + File.Delete(file); + + Assert.IsTrue(NativeContract.CryptoLib.IsInitializeBlock(settings, 0, out var hf)); + Assert.IsNull(hf); + + Assert.IsFalse(NativeContract.CryptoLib.IsInitializeBlock(settings, 1, out hf)); + Assert.IsNull(hf); + + Assert.IsTrue(NativeContract.CryptoLib.IsInitializeBlock(settings, 20, out hf)); + Assert.AreEqual(Hardfork.HF_Cockatrice, hf); + } + } +} diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_NeoToken.cs similarity index 84% rename from tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs rename to tests/Neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index a627288978..73cf286652 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -1,6 +1,16 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_NeoToken.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Cryptography; using Neo.Cryptography.ECC; using Neo.IO; using Neo.Network.P2P.Payloads; @@ -49,7 +59,10 @@ public void Check_Vote() var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block { Header = new Header { Index = 1000 } }; - byte[] from = Contract.GetBFTAddress(ProtocolSettings.Default.StandbyValidators).ToArray(); + var storageKey = new KeyBuilder(NativeContract.Ledger.Id, 12); + snapshot.Add(storageKey, new StorageItem(new HashIndexState { Hash = UInt256.Zero, Index = persistingBlock.Index - 1 })); + + byte[] from = Contract.GetBFTAddress(TestProtocolSettings.Default.StandbyValidators).ToArray(); // No signature @@ -90,10 +103,11 @@ public void Check_Vote() // normal case - snapshot.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState())); + snapshot.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState() { Registered = true })); ret = Check_Vote(snapshot, from, ECCurve.Secp256r1.G.ToArray(), true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); + accountState = snapshot.TryGet(CreateStorageKey(20, from)).GetInteroperable(); accountState.VoteTo.Should().Be(ECCurve.Secp256r1.G); } @@ -103,13 +117,17 @@ public void Check_Vote_Sameaccounts() var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block { Header = new Header { Index = 1000 } }; - byte[] from = Contract.GetBFTAddress(ProtocolSettings.Default.StandbyValidators).ToArray(); + var storageKey = new KeyBuilder(NativeContract.Ledger.Id, 12); + snapshot.Add(storageKey, new StorageItem(new HashIndexState { Hash = UInt256.Zero, Index = persistingBlock.Index - 1 })); + + byte[] from = Contract.GetBFTAddress(TestProtocolSettings.Default.StandbyValidators).ToArray(); var accountState = snapshot.TryGet(CreateStorageKey(20, from)).GetInteroperable(); accountState.Balance = 100; - snapshot.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState())); + snapshot.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState() { Registered = true })); var ret = Check_Vote(snapshot, from, ECCurve.Secp256r1.G.ToArray(), true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); + accountState = snapshot.TryGet(CreateStorageKey(20, from)).GetInteroperable(); accountState.VoteTo.Should().Be(ECCurve.Secp256r1.G); //two account vote for the same account @@ -122,6 +140,7 @@ public void Check_Vote_Sameaccounts() ret = Check_Vote(snapshot, G_Account, ECCurve.Secp256r1.G.ToArray(), true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); + stateValidator = snapshot.GetAndChange(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray())).GetInteroperable(); stateValidator.Votes.Should().Be(300); } @@ -130,16 +149,19 @@ public void Check_Vote_ChangeVote() { var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block { Header = new Header { Index = 1000 } }; + var storageKey = new KeyBuilder(NativeContract.Ledger.Id, 12); + snapshot.Add(storageKey, new StorageItem(new HashIndexState { Hash = UInt256.Zero, Index = persistingBlock.Index - 1 })); //from vote to G - byte[] from = ProtocolSettings.Default.StandbyValidators[0].ToArray(); - var from_Account = Contract.CreateSignatureContract(ProtocolSettings.Default.StandbyValidators[0]).ScriptHash.ToArray(); + byte[] from = TestProtocolSettings.Default.StandbyValidators[0].ToArray(); + var from_Account = Contract.CreateSignatureContract(TestProtocolSettings.Default.StandbyValidators[0]).ScriptHash.ToArray(); snapshot.Add(CreateStorageKey(20, from_Account), new StorageItem(new NeoAccountState())); var accountState = snapshot.TryGet(CreateStorageKey(20, from_Account)).GetInteroperable(); accountState.Balance = 100; - snapshot.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState())); + snapshot.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState() { Registered = true })); var ret = Check_Vote(snapshot, from_Account, ECCurve.Secp256r1.G.ToArray(), true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); + accountState = snapshot.TryGet(CreateStorageKey(20, from_Account)).GetInteroperable(); accountState.VoteTo.Should().Be(ECCurve.Secp256r1.G); //from change vote to itself @@ -147,10 +169,11 @@ public void Check_Vote_ChangeVote() G_stateValidator.Votes.Should().Be(100); var G_Account = Contract.CreateSignatureContract(ECCurve.Secp256r1.G).ScriptHash.ToArray(); snapshot.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState { Balance = 200 })); - snapshot.Add(CreateStorageKey(33, from), new StorageItem(new CandidateState())); + snapshot.Add(CreateStorageKey(33, from), new StorageItem(new CandidateState() { Registered = true })); ret = Check_Vote(snapshot, from_Account, from, true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); + G_stateValidator = snapshot.GetAndChange(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray())).GetInteroperable(); G_stateValidator.Votes.Should().Be(0); var from_stateValidator = snapshot.GetAndChange(CreateStorageKey(33, from)).GetInteroperable(); from_stateValidator.Votes.Should().Be(100); @@ -161,16 +184,18 @@ public void Check_Vote_VoteToNull() { var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block { Header = new Header { Index = 1000 } }; - - byte[] from = ProtocolSettings.Default.StandbyValidators[0].ToArray(); - var from_Account = Contract.CreateSignatureContract(ProtocolSettings.Default.StandbyValidators[0]).ScriptHash.ToArray(); + var storageKey = new KeyBuilder(NativeContract.Ledger.Id, 12); + snapshot.Add(storageKey, new StorageItem(new HashIndexState { Hash = UInt256.Zero, Index = persistingBlock.Index - 1 })); + byte[] from = TestProtocolSettings.Default.StandbyValidators[0].ToArray(); + var from_Account = Contract.CreateSignatureContract(TestProtocolSettings.Default.StandbyValidators[0]).ScriptHash.ToArray(); snapshot.Add(CreateStorageKey(20, from_Account), new StorageItem(new NeoAccountState())); var accountState = snapshot.TryGet(CreateStorageKey(20, from_Account)).GetInteroperable(); accountState.Balance = 100; - snapshot.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState())); + snapshot.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState() { Registered = true })); var ret = Check_Vote(snapshot, from_Account, ECCurve.Secp256r1.G.ToArray(), true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); + accountState = snapshot.TryGet(CreateStorageKey(20, from_Account)).GetInteroperable(); accountState.VoteTo.Should().Be(ECCurve.Secp256r1.G); //from vote to null account G votes becomes 0 @@ -178,11 +203,13 @@ public void Check_Vote_VoteToNull() G_stateValidator.Votes.Should().Be(100); var G_Account = Contract.CreateSignatureContract(ECCurve.Secp256r1.G).ScriptHash.ToArray(); snapshot.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState { Balance = 200 })); - snapshot.Add(CreateStorageKey(33, from), new StorageItem(new CandidateState())); + snapshot.Add(CreateStorageKey(33, from), new StorageItem(new CandidateState() { Registered = true })); ret = Check_Vote(snapshot, from_Account, null, true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); + G_stateValidator = snapshot.GetAndChange(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray())).GetInteroperable(); G_stateValidator.Votes.Should().Be(0); + accountState = snapshot.TryGet(CreateStorageKey(20, from_Account)).GetInteroperable(); accountState.VoteTo.Should().Be(null); } @@ -192,7 +219,10 @@ public void Check_UnclaimedGas() var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block { Header = new Header { Index = 1000 } }; - byte[] from = Contract.GetBFTAddress(ProtocolSettings.Default.StandbyValidators).ToArray(); + var storageKey = new KeyBuilder(NativeContract.Ledger.Id, 12); + snapshot.Add(storageKey, new StorageItem(new HashIndexState { Hash = UInt256.Zero, Index = persistingBlock.Index - 1 })); + + byte[] from = Contract.GetBFTAddress(TestProtocolSettings.Default.StandbyValidators).ToArray(); var unclaim = Check_UnclaimedGas(snapshot, from, persistingBlock); unclaim.Value.Should().Be(new BigInteger(0.5 * 1000 * 100000000L)); @@ -209,7 +239,7 @@ public void Check_RegisterValidator() var snapshot = _snapshot.CreateSnapshot(); var keyCount = snapshot.GetChangeSet().Count(); - var point = ProtocolSettings.Default.StandbyValidators[0].EncodePoint(true).Clone() as byte[]; + var point = TestProtocolSettings.Default.StandbyValidators[0].EncodePoint(true).Clone() as byte[]; var ret = Check_RegisterValidator(snapshot, point, _persistingBlock); // Exists ret.State.Should().BeTrue(); @@ -227,17 +257,17 @@ public void Check_RegisterValidator() // Check GetRegisteredValidators - var members = NativeContract.NEO.GetCandidates(snapshot); - Assert.AreEqual(2, members.Length); + var members = NativeContract.NEO.GetCandidatesInternal(snapshot); + Assert.AreEqual(2, members.Count()); } [TestMethod] public void Check_UnregisterCandidate() { var snapshot = _snapshot.CreateSnapshot(); - + _persistingBlock.Header.Index = 1; var keyCount = snapshot.GetChangeSet().Count(); - var point = ProtocolSettings.Default.StandbyValidators[0].EncodePoint(true); + var point = TestProtocolSettings.Default.StandbyValidators[0].EncodePoint(true); //without register var ret = Check_UnregisterCandidate(snapshot, point, _persistingBlock); @@ -252,8 +282,8 @@ public void Check_UnregisterCandidate() ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); - var members = NativeContract.NEO.GetCandidates(snapshot); - Assert.AreEqual(1, members.Length); + var members = NativeContract.NEO.GetCandidatesInternal(snapshot); + Assert.AreEqual(1, members.Count()); snapshot.GetChangeSet().Count().Should().Be(keyCount + 1); StorageKey key = CreateStorageKey(33, point); snapshot.TryGet(key).Should().NotBeNull(); @@ -263,8 +293,8 @@ public void Check_UnregisterCandidate() ret.Result.Should().BeTrue(); snapshot.GetChangeSet().Count().Should().Be(keyCount); - members = NativeContract.NEO.GetCandidates(snapshot); - Assert.AreEqual(0, members.Length); + members = NativeContract.NEO.GetCandidatesInternal(snapshot); + Assert.AreEqual(0, members.Count()); snapshot.TryGet(key).Should().BeNull(); //register with votes, then unregister @@ -274,7 +304,7 @@ public void Check_UnregisterCandidate() snapshot.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState())); var accountState = snapshot.TryGet(CreateStorageKey(20, G_Account)).GetInteroperable(); accountState.Balance = 100; - Check_Vote(snapshot, G_Account, ProtocolSettings.Default.StandbyValidators[0].ToArray(), true, _persistingBlock); + Check_Vote(snapshot, G_Account, TestProtocolSettings.Default.StandbyValidators[0].ToArray(), true, _persistingBlock); ret = Check_UnregisterCandidate(snapshot, point, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); @@ -285,10 +315,11 @@ public void Check_UnregisterCandidate() pointState.Votes.Should().Be(100); //vote fail - ret = Check_Vote(snapshot, G_Account, ProtocolSettings.Default.StandbyValidators[0].ToArray(), true, _persistingBlock); + ret = Check_Vote(snapshot, G_Account, TestProtocolSettings.Default.StandbyValidators[0].ToArray(), true, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeFalse(); - accountState.VoteTo.Should().Be(ProtocolSettings.Default.StandbyValidators[0]); + accountState = snapshot.TryGet(CreateStorageKey(20, G_Account)).GetInteroperable(); + accountState.VoteTo.Should().Be(TestProtocolSettings.Default.StandbyValidators[0]); } [TestMethod] @@ -296,9 +327,9 @@ public void Check_GetCommittee() { var snapshot = _snapshot.CreateSnapshot(); var keyCount = snapshot.GetChangeSet().Count(); - var point = ProtocolSettings.Default.StandbyValidators[0].EncodePoint(true); + var point = TestProtocolSettings.Default.StandbyValidators[0].EncodePoint(true); var persistingBlock = _persistingBlock; - + persistingBlock.Header.Index = 1; //register with votes with 20000000 var G_Account = Contract.CreateSignatureContract(ECCurve.Secp256r1.G).ScriptHash.ToArray(); snapshot.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState())); @@ -312,9 +343,9 @@ public void Check_GetCommittee() ret.Result.Should().BeTrue(); var committeemembers = NativeContract.NEO.GetCommittee(snapshot); - var defaultCommittee = ProtocolSettings.Default.StandbyCommittee.OrderBy(p => p).ToArray(); + var defaultCommittee = TestProtocolSettings.Default.StandbyCommittee.OrderBy(p => p).ToArray(); committeemembers.GetType().Should().Be(typeof(ECPoint[])); - for (int i = 0; i < ProtocolSettings.Default.CommitteeMembersCount; i++) + for (int i = 0; i < TestProtocolSettings.Default.CommitteeMembersCount; i++) { committeemembers[i].Should().Be(defaultCommittee[i]); } @@ -324,7 +355,7 @@ public void Check_GetCommittee() { Header = new Header { - Index = (uint)ProtocolSettings.Default.CommitteeMembersCount, + Index = (uint)TestProtocolSettings.Default.CommitteeMembersCount, MerkleRoot = UInt256.Zero, NextConsensus = UInt160.Zero, PrevHash = UInt256.Zero, @@ -332,9 +363,9 @@ public void Check_GetCommittee() }, Transactions = Array.Empty() }; - for (int i = 0; i < ProtocolSettings.Default.CommitteeMembersCount - 1; i++) + for (int i = 0; i < TestProtocolSettings.Default.CommitteeMembersCount - 1; i++) { - ret = Check_RegisterValidator(snapshot, ProtocolSettings.Default.StandbyCommittee[i].ToArray(), persistingBlock); + ret = Check_RegisterValidator(snapshot, TestProtocolSettings.Default.StandbyCommittee[i].ToArray(), persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); } @@ -342,13 +373,13 @@ public void Check_GetCommittee() Check_OnPersist(snapshot, persistingBlock).Should().BeTrue(); committeemembers = NativeContract.NEO.GetCommittee(snapshot); - committeemembers.Length.Should().Be(ProtocolSettings.Default.CommitteeMembersCount); + committeemembers.Length.Should().Be(TestProtocolSettings.Default.CommitteeMembersCount); committeemembers.Contains(ECCurve.Secp256r1.G).Should().BeTrue(); - for (int i = 0; i < ProtocolSettings.Default.CommitteeMembersCount - 1; i++) + for (int i = 0; i < TestProtocolSettings.Default.CommitteeMembersCount - 1; i++) { - committeemembers.Contains(ProtocolSettings.Default.StandbyCommittee[i]).Should().BeTrue(); + committeemembers.Contains(TestProtocolSettings.Default.StandbyCommittee[i]).Should().BeTrue(); } - committeemembers.Contains(ProtocolSettings.Default.StandbyCommittee[ProtocolSettings.Default.CommitteeMembersCount - 1]).Should().BeFalse(); + committeemembers.Contains(TestProtocolSettings.Default.StandbyCommittee[TestProtocolSettings.Default.CommitteeMembersCount - 1]).Should().BeFalse(); } [TestMethod] @@ -357,9 +388,11 @@ public void Check_Transfer() var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block { Header = new Header { Index = 1000 } }; - byte[] from = Contract.GetBFTAddress(ProtocolSettings.Default.StandbyValidators).ToArray(); + byte[] from = Contract.GetBFTAddress(TestProtocolSettings.Default.StandbyValidators).ToArray(); byte[] to = new byte[20]; + var storageKey = new KeyBuilder(NativeContract.Ledger.Id, 12); + snapshot.Add(storageKey, new StorageItem(new HashIndexState { Hash = UInt256.Zero, Index = persistingBlock.Index - 1 })); var keyCount = snapshot.GetChangeSet().Count(); // Check unclaim @@ -412,7 +445,7 @@ public void Check_Transfer() public void Check_BalanceOf() { var snapshot = _snapshot.CreateSnapshot(); - byte[] account = Contract.GetBFTAddress(ProtocolSettings.Default.StandbyValidators).ToArray(); + byte[] account = Contract.GetBFTAddress(TestProtocolSettings.Default.StandbyValidators).ToArray(); NativeContract.NEO.BalanceOf(snapshot, account).Should().Be(100_000_000); @@ -440,7 +473,7 @@ public void Check_CommitteeBonus() Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); - var committee = ProtocolSettings.Default.StandbyCommittee; + var committee = TestProtocolSettings.Default.StandbyCommittee; NativeContract.GAS.BalanceOf(snapshot, Contract.CreateSignatureContract(committee[0]).ScriptHash.ToArray()).Should().Be(50000000); NativeContract.GAS.BalanceOf(snapshot, Contract.CreateSignatureContract(committee[1]).ScriptHash.ToArray()).Should().Be(50000000); NativeContract.GAS.BalanceOf(snapshot, Contract.CreateSignatureContract(committee[2]).ScriptHash.ToArray()).Should().Be(0); @@ -500,6 +533,11 @@ public void TestCalculateBonus() { Balance = 100 })); + + var storageKey = new KeyBuilder(NativeContract.Ledger.Id, 12); + var item = snapshot.GetAndChange(storageKey).GetInteroperable(); + item.Index = 99; + NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 100).Should().Be(new BigInteger(0.5 * 100 * 100)); snapshot.Delete(key); @@ -518,9 +556,9 @@ public void TestCalculateBonus() snapshot.GetAndChange(key, () => new StorageItem(new NeoAccountState { Balance = 100, - VoteTo = ProtocolSettings.Default.StandbyCommittee[0] + VoteTo = TestProtocolSettings.Default.StandbyCommittee[0] })); - snapshot.Add(new KeyBuilder(NativeContract.NEO.Id, 23).Add(ProtocolSettings.Default.StandbyCommittee[0]).AddBigEndian(uint.MaxValue - 50), new StorageItem() { Value = new BigInteger(50 * 10000L).ToByteArray() }); + snapshot.Add(new KeyBuilder(NativeContract.NEO.Id, 23).Add(TestProtocolSettings.Default.StandbyCommittee[0]).AddBigEndian(uint.MaxValue - 50), new StorageItem() { Value = new BigInteger(50 * 10000L).ToByteArray() }); NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 100).Should().Be(new BigInteger(50 * 100)); snapshot.Delete(key); } @@ -567,12 +605,12 @@ public void TestGetCandidates1() public void TestGetCandidates2() { var snapshot = _snapshot.CreateSnapshot(); - var result = NativeContract.NEO.GetCandidates(snapshot); - result.Length.Should().Be(0); + var result = NativeContract.NEO.GetCandidatesInternal(snapshot); + result.Count().Should().Be(0); StorageKey key = NativeContract.NEO.CreateStorageKey(33, ECCurve.Secp256r1.G); - snapshot.Add(key, new StorageItem(new CandidateState())); - NativeContract.NEO.GetCandidates(snapshot).Length.Should().Be(1); + snapshot.Add(key, new StorageItem(new CandidateState() { Registered = true })); + NativeContract.NEO.GetCandidatesInternal(snapshot).Count().Should().Be(1); } [TestMethod] @@ -583,7 +621,7 @@ public void TestCheckCandidate() var point = committee[0].EncodePoint(true); // Prepare Prefix_VoterRewardPerCommittee - var storageKey = new KeyBuilder(NativeContract.NEO.Id, 23).Add(committee[0]).AddBigEndian(20); + var storageKey = new KeyBuilder(NativeContract.NEO.Id, 23).Add(committee[0]); snapshot.Add(storageKey, new StorageItem(new BigInteger(1000))); // Prepare Candidate @@ -660,7 +698,7 @@ public void TestGetCommittee() public void TestGetValidators() { var snapshot = _snapshot.CreateSnapshot(); - var result = NativeContract.NEO.ComputeNextBlockValidators(snapshot, ProtocolSettings.Default); + var result = NativeContract.NEO.ComputeNextBlockValidators(snapshot, TestProtocolSettings.Default); result[0].ToArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); result[1].ToArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); result[2].ToArray().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); @@ -720,7 +758,7 @@ public void TestEconomicParameter() NeoAccountState state = storage.GetInteroperable(); state.Balance = 1000; state.BalanceHeight = 0; - height.Index = 0; // Fake Height=0 + height.Index = persistingBlock.Index + 1; NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, persistingBlock.Index + 2).Should().Be(6500); } @@ -732,9 +770,9 @@ public void TestClaimGas() // Initialize block snapshot.Add(CreateStorageKey(1), new StorageItem(new BigInteger(30000000))); - ECPoint[] standbyCommittee = ProtocolSettings.Default.StandbyCommittee.OrderBy(p => p).ToArray(); + ECPoint[] standbyCommittee = TestProtocolSettings.Default.StandbyCommittee.OrderBy(p => p).ToArray(); CachedCommittee cachedCommittee = new(); - for (var i = 0; i < ProtocolSettings.Default.CommitteeMembersCount; i++) + for (var i = 0; i < TestProtocolSettings.Default.CommitteeMembersCount; i++) { ECPoint member = standbyCommittee[i]; snapshot.Add(new KeyBuilder(NativeContract.NEO.Id, 33).Add(member), new StorageItem(new CandidateState() @@ -744,7 +782,7 @@ public void TestClaimGas() })); cachedCommittee.Add((member, 200 * 10000)); } - snapshot.GetOrAdd(new KeyBuilder(NativeContract.NEO.Id, 14), () => new StorageItem()).Value = BinarySerializer.Serialize(cachedCommittee.ToStackItem(null), 4096); + snapshot.GetOrAdd(new KeyBuilder(NativeContract.NEO.Id, 14), () => new StorageItem()).Value = BinarySerializer.Serialize(cachedCommittee.ToStackItem(null), ExecutionEngineLimits.Default); var item = snapshot.GetAndChange(new KeyBuilder(NativeContract.NEO.Id, 1), () => new StorageItem()); item.Value = ((BigInteger)2100 * 10000L).ToByteArray(); @@ -763,13 +801,13 @@ public void TestClaimGas() }; Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); - var committee = ProtocolSettings.Default.StandbyCommittee.OrderBy(p => p).ToArray(); + var committee = TestProtocolSettings.Default.StandbyCommittee.OrderBy(p => p).ToArray(); var accountA = committee[0]; - var accountB = committee[ProtocolSettings.Default.CommitteeMembersCount - 1]; + var accountB = committee[TestProtocolSettings.Default.CommitteeMembersCount - 1]; NativeContract.NEO.BalanceOf(snapshot, Contract.CreateSignatureContract(accountA).ScriptHash).Should().Be(0); - StorageItem storageItem = snapshot.TryGet(new KeyBuilder(NativeContract.NEO.Id, 23).Add(accountA).AddBigEndian(1)); - new BigInteger(storageItem.Value).Should().Be(30000000000); + StorageItem storageItem = snapshot.TryGet(new KeyBuilder(NativeContract.NEO.Id, 23).Add(accountA)); + ((BigInteger)storageItem).Should().Be(30000000000); snapshot.TryGet(new KeyBuilder(NativeContract.NEO.Id, 23).Add(accountB).AddBigEndian(uint.MaxValue - 1)).Should().BeNull(); @@ -791,8 +829,8 @@ public void TestClaimGas() NativeContract.NEO.BalanceOf(snapshot, Contract.CreateSignatureContract(committee[1]).ScriptHash).Should().Be(0); - storageItem = snapshot.TryGet(new KeyBuilder(NativeContract.NEO.Id, 23).Add(committee[1]).AddBigEndian(1)); - new BigInteger(storageItem.Value).Should().Be(30000000000); + storageItem = snapshot.TryGet(new KeyBuilder(NativeContract.NEO.Id, 23).Add(committee[1])); + ((BigInteger)storageItem).Should().Be(30000000000); // Next block @@ -810,11 +848,11 @@ public void TestClaimGas() }; Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); - accountA = ProtocolSettings.Default.StandbyCommittee.OrderBy(p => p).ToArray()[2]; + accountA = TestProtocolSettings.Default.StandbyCommittee.OrderBy(p => p).ToArray()[2]; NativeContract.NEO.BalanceOf(snapshot, Contract.CreateSignatureContract(committee[2]).ScriptHash).Should().Be(0); - storageItem = snapshot.TryGet(new KeyBuilder(NativeContract.NEO.Id, 23).Add(committee[2]).AddBigEndian(22)); - new BigInteger(storageItem.Value).Should().Be(30000000000 * 2); + storageItem = snapshot.TryGet(new KeyBuilder(NativeContract.NEO.Id, 23).Add(committee[2])); + ((BigInteger)storageItem).Should().Be(30000000000 * 2); // Claim GAS @@ -823,9 +861,12 @@ public void TestClaimGas() { BalanceHeight = 3, Balance = 200 * 10000 - 2 * 100, - VoteTo = committee[2] + VoteTo = committee[2], + LastGasPerVote = 30000000000, })); NativeContract.NEO.BalanceOf(snapshot, account).Should().Be(1999800); + var storageKey = new KeyBuilder(NativeContract.Ledger.Id, 12); + snapshot.GetAndChange(storageKey).GetInteroperable().Index = 29 + 2; BigInteger value = NativeContract.NEO.UnclaimedGas(snapshot, account, 29 + 3); value.Should().Be(1999800 * 30000000000 / 100000000L + (1999800L * 10 * 5 * 29 / 100)); } @@ -846,6 +887,7 @@ public void TestVote() UInt160 account = UInt160.Parse("01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4"); StorageKey keyAccount = CreateStorageKey(20, account.ToArray()); StorageKey keyValidator = CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()); + _persistingBlock.Header.Index = 1; var ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), false, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeFalse(); @@ -865,9 +907,10 @@ public void TestVote() snapshot.Delete(keyAccount); snapshot.GetAndChange(keyAccount, () => new StorageItem(new NeoAccountState { + Balance = 1, VoteTo = ECCurve.Secp256r1.G })); - snapshot.Add(keyValidator, new StorageItem(new CandidateState())); + snapshot.Add(keyValidator, new StorageItem(new CandidateState() { Registered = true })); ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), true, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); @@ -879,7 +922,8 @@ public void TestVote() internal (bool State, bool Result) Transfer4TesingOnBalanceChanging(BigInteger amount, bool addVotes) { var snapshot = _snapshot.CreateSnapshot(); - var engine = ApplicationEngine.Create(TriggerType.Application, TestBlockchain.TheNeoSystem.GenesisBlock, snapshot, TestBlockchain.TheNeoSystem.GenesisBlock, settings: TestBlockchain.TheNeoSystem.Settings); + _persistingBlock.Header.Index = 1; + var engine = ApplicationEngine.Create(TriggerType.Application, TestBlockchain.TheNeoSystem.GenesisBlock, snapshot, _persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); ScriptBuilder sb = new(); var tmp = engine.ScriptContainer.GetScriptHashesForVerifying(engine.Snapshot); UInt160 from = engine.ScriptContainer.GetScriptHashesForVerifying(engine.Snapshot)[0]; @@ -902,7 +946,8 @@ public void TestVote() sb.EmitDynamicCall(NativeContract.NEO.Hash, "transfer", from, UInt160.Zero, amount, null); engine.LoadScript(sb.ToArray()); - engine.Execute(); + var state = engine.Execute(); + Console.WriteLine($"{state} {engine.FaultException}"); var result = engine.ResultStack.Peek(); result.GetType().Should().Be(typeof(VM.Types.Boolean)); return (true, result.GetBoolean()); @@ -975,6 +1020,7 @@ internal static (bool State, bool Result) Check_Vote(DataCache snapshot, byte[] if (engine.Execute() == VMState.FAULT) { + Console.WriteLine(engine.FaultException); return (false, false); } @@ -1017,7 +1063,7 @@ internal static ECPoint[] Check_GetCommittee(DataCache snapshot, Block persistin var result = engine.ResultStack.Pop(); result.Should().BeOfType(typeof(VM.Types.Array)); - return (result as VM.Types.Array).Select(u => u.GetSpan().AsSerializable()).ToArray(); + return (result as VM.Types.Array).Select(u => ECPoint.DecodePoint(u.GetSpan(), ECCurve.Secp256r1)).ToArray(); } internal static (BigInteger Value, bool State) Check_UnclaimedGas(DataCache snapshot, byte[] address, Block persistingBlock) @@ -1030,6 +1076,7 @@ internal static (BigInteger Value, bool State) Check_UnclaimedGas(DataCache snap if (engine.Execute() == VMState.FAULT) { + Console.WriteLine(engine.FaultException); return (BigInteger.Zero, false); } @@ -1041,7 +1088,7 @@ internal static (BigInteger Value, bool State) Check_UnclaimedGas(DataCache snap internal static void CheckValidator(ECPoint eCPoint, DataCache.Trackable trackable) { - var st = new BigInteger(trackable.Item.Value); + BigInteger st = trackable.Item; st.Should().Be(0); trackable.Key.Key.Should().BeEquivalentTo(new byte[] { 33 }.Concat(eCPoint.EncodePoint(true))); @@ -1056,21 +1103,21 @@ internal static void CheckBalance(byte[] account, DataCache.Trackable trackable, st[0].GetInteger().Should().Be(balance); // Balance st[1].GetInteger().Should().Be(height); // BalanceHeight - st[2].GetSpan().AsSerializable().Should().BeEquivalentTo(voteTo); // Votes + ECPoint.DecodePoint(st[2].GetSpan(), ECCurve.Secp256r1).Should().BeEquivalentTo(voteTo); // Votes trackable.Key.Key.Should().BeEquivalentTo(new byte[] { 20 }.Concat(account)); } internal static StorageKey CreateStorageKey(byte prefix, byte[] key = null) { - StorageKey storageKey = new() + byte[] buffer = GC.AllocateUninitializedArray(sizeof(byte) + (key?.Length ?? 0)); + buffer[0] = prefix; + key?.CopyTo(buffer.AsSpan(1)); + return new() { Id = NativeContract.NEO.Id, - Key = new byte[sizeof(byte) + (key?.Length ?? 0)] + Key = buffer }; - storageKey.Key[0] = prefix; - key?.CopyTo(storageKey.Key.AsSpan(1)); - return storageKey; } internal static (bool State, bool Result) Check_UnregisterCandidate(DataCache snapshot, byte[] pubkey, Block persistingBlock) diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs similarity index 75% rename from tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs rename to tests/Neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index 6730dd8825..32da3b1602 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_PolicyContract.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; @@ -8,6 +19,7 @@ using Neo.UnitTests.Extensions; using System; using System.Linq; +using System.Numerics; namespace Neo.UnitTests.SmartContract.Native { @@ -33,6 +45,71 @@ public void Check_Default() var ret = NativeContract.Policy.Call(snapshot, "getFeePerByte"); ret.Should().BeOfType(); ret.GetInteger().Should().Be(1000); + + ret = NativeContract.Policy.Call(snapshot, "getAttributeFee", new ContractParameter(ContractParameterType.Integer) { Value = (BigInteger)(byte)TransactionAttributeType.Conflicts }); + ret.Should().BeOfType(); + ret.GetInteger().Should().Be(PolicyContract.DefaultAttributeFee); + + Assert.ThrowsException(() => NativeContract.Policy.Call(snapshot, "getAttributeFee", new ContractParameter(ContractParameterType.Integer) { Value = (BigInteger)byte.MaxValue })); + } + + [TestMethod] + public void Check_SetAttributeFee() + { + var snapshot = _snapshot.CreateSnapshot(); + + // Fake blockchain + Block block = new() + { + Header = new Header + { + Index = 1000, + PrevHash = UInt256.Zero + } + }; + + var attr = new ContractParameter(ContractParameterType.Integer) { Value = (BigInteger)(byte)TransactionAttributeType.Conflicts }; + + // Without signature + Assert.ThrowsException(() => + { + NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, + "setAttributeFee", attr, new ContractParameter(ContractParameterType.Integer) { Value = 100500 }); + }); + + var ret = NativeContract.Policy.Call(snapshot, "getAttributeFee", attr); + ret.Should().BeOfType(); + ret.GetInteger().Should().Be(0); + + // With signature, wrong value + UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); + Assert.ThrowsException(() => + { + NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, + "setAttributeFee", attr, new ContractParameter(ContractParameterType.Integer) { Value = 11_0000_0000 }); + }); + + ret = NativeContract.Policy.Call(snapshot, "getAttributeFee", attr); + ret.Should().BeOfType(); + ret.GetInteger().Should().Be(0); + + // Proper set + ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, + "setAttributeFee", attr, new ContractParameter(ContractParameterType.Integer) { Value = 300300 }); + ret.IsNull.Should().BeTrue(); + + ret = NativeContract.Policy.Call(snapshot, "getAttributeFee", attr); + ret.Should().BeOfType(); + ret.GetInteger().Should().Be(300300); + + // Set to zero + ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, + "setAttributeFee", attr, new ContractParameter(ContractParameterType.Integer) { Value = 0 }); + ret.IsNull.Should().BeTrue(); + + ret = NativeContract.Policy.Call(snapshot, "getAttributeFee", attr); + ret.Should().BeOfType(); + ret.GetInteger().Should().Be(0); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs similarity index 86% rename from tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs rename to tests/Neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs index 45a0a0ddb6..a87c630194 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_RoleManagement.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; @@ -25,12 +36,18 @@ public void TestSetup() _snapshot = TestBlockchain.GetTestSnapshot(); } + [TestCleanup] + public void Clean() + { + TestBlockchain.ResetStore(); + } + [TestMethod] public void TestSetAndGet() { var snapshot1 = _snapshot.CreateSnapshot(); UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot1); - ECPoint[] validators = NativeContract.NEO.ComputeNextBlockValidators(snapshot1, ProtocolSettings.Default); + ECPoint[] validators = NativeContract.NEO.ComputeNextBlockValidators(snapshot1, TestProtocolSettings.Default); List notifications = new List(); EventHandler ev = (o, e) => notifications.Add(e); ApplicationEngine.Notify += ev; diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs similarity index 87% rename from tests/neo.UnitTests/SmartContract/Native/UT_StdLib.cs rename to tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs index 8879c828b5..f36fa2cfa4 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_StdLib.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.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; using Neo.SmartContract.Native; @@ -209,6 +220,47 @@ public void StringSplit() Assert.AreEqual("b", arr[1].GetString()); } + [TestMethod] + public void StringElementLength() + { + var snapshot = TestBlockchain.GetTestSnapshot(); + + using var script = new ScriptBuilder(); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "strLen", "🦆"); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "strLen", "ã"); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "strLen", "a"); + + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(3, engine.ResultStack.Count); + Assert.AreEqual(1, engine.ResultStack.Pop().GetInteger()); + Assert.AreEqual(1, engine.ResultStack.Pop().GetInteger()); + Assert.AreEqual(1, engine.ResultStack.Pop().GetInteger()); + } + + [TestMethod] + public void TestInvalidUtf8Sequence() + { + // Simulating invalid UTF-8 byte (0xff) decoded as a UTF-16 char + const char badChar = (char)0xff; + var badStr = badChar.ToString(); + var snapshot = TestBlockchain.GetTestSnapshot(); + + using var script = new ScriptBuilder(); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "strLen", badStr); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "strLen", badStr + "ab"); + + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(2, engine.ResultStack.Count); + Assert.AreEqual(3, engine.ResultStack.Pop().GetInteger()); + Assert.AreEqual(1, engine.ResultStack.Pop().GetInteger()); + } + [TestMethod] public void Json_Deserialize() { @@ -290,7 +342,7 @@ public void Json_Serialize() Assert.IsTrue(engine.ResultStack.Pop().GetString() == "{\"key\":\"value\"}"); Assert.IsTrue(engine.ResultStack.Pop().GetString() == "null"); Assert.IsTrue(engine.ResultStack.Pop().GetString() == "\"test\""); - Assert.IsTrue(engine.ResultStack.Pop().GetString() == "1"); + Assert.IsTrue(engine.ResultStack.Pop().GetString() == "true"); Assert.IsTrue(engine.ResultStack.Pop().GetString() == "5"); } diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.Contract.cs b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Contract.cs similarity index 74% rename from tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.Contract.cs rename to tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Contract.cs index 939cc282a2..f9dc5e7b46 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.Contract.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Contract.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ApplicationEngine.Contract.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; @@ -11,8 +22,8 @@ public partial class UT_ApplicationEngine [TestMethod] public void TestCreateStandardAccount() { - var settings = ProtocolSettings.Default; - using var engine = ApplicationEngine.Create(TriggerType.Application, null, null, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1100_00000000); + var settings = TestProtocolSettings.Default; + using var engine = ApplicationEngine.Create(TriggerType.Application, null, null, settings: TestProtocolSettings.Default, gas: 1100_00000000); using var script = new ScriptBuilder(); script.EmitSysCall(ApplicationEngine.System_Contract_CreateStandardAccount, settings.StandbyCommittee[0].EncodePoint(true)); @@ -27,7 +38,7 @@ public void TestCreateStandardAccount() [TestMethod] public void TestCreateStandardMultisigAccount() { - var settings = ProtocolSettings.Default; + var settings = TestProtocolSettings.Default; using var engine = ApplicationEngine.Create(TriggerType.Application, null, null, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1100_00000000); using var script = new ScriptBuilder(); diff --git a/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs new file mode 100644 index 0000000000..75e6e7b6da --- /dev/null +++ b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs @@ -0,0 +1,191 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ApplicationEngine.Runtime.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 FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +using Neo.SmartContract.Manifest; +using System; +using System.Numerics; +using System.Text; + +namespace Neo.UnitTests.SmartContract +{ + public partial class UT_ApplicationEngine + { + [TestMethod] + public void TestGetNetworkAndAddressVersion() + { + var tx = TestUtils.GetTransaction(UInt160.Zero); + using var engine = ApplicationEngine.Create(TriggerType.Application, tx, null, TestBlockchain.TheNeoSystem.GenesisBlock, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1100_00000000); + + engine.GetNetwork().Should().Be(TestBlockchain.TheNeoSystem.Settings.Network); + engine.GetAddressVersion().Should().Be(TestBlockchain.TheNeoSystem.Settings.AddressVersion); + } + + [TestMethod] + public void TestNotSupportedNotification() + { + using var engine = ApplicationEngine.Create(TriggerType.Application, null, null, TestBlockchain.TheNeoSystem.GenesisBlock, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1100_00000000); + engine.LoadScript(Array.Empty()); + engine.CurrentContext.GetState().Contract = new() + { + Manifest = new() + { + Abi = new() + { + Events = new[] + { + new ContractEventDescriptor + { + Name = "e1", + Parameters = new[] + { + new ContractParameterDefinition + { + Type = ContractParameterType.Array + } + } + } + } + } + } + }; + + // circular + + VM.Types.Array array = new(); + array.Add(array); + + Assert.ThrowsException(() => engine.RuntimeNotify(Encoding.ASCII.GetBytes("e1"), array)); + + // Buffer + + array.Clear(); + array.Add(new VM.Types.Buffer(1)); + engine.CurrentContext.GetState().Contract.Manifest.Abi.Events[0].Parameters[0].Type = ContractParameterType.ByteArray; + + engine.RuntimeNotify(Encoding.ASCII.GetBytes("e1"), array); + engine.Notifications[0].State[0].Type.Should().Be(VM.Types.StackItemType.ByteString); + + // Pointer + + array.Clear(); + array.Add(new VM.Types.Pointer(Array.Empty(), 1)); + + Assert.ThrowsException(() => engine.RuntimeNotify(Encoding.ASCII.GetBytes("e1"), array)); + + // InteropInterface + + array.Clear(); + array.Add(new VM.Types.InteropInterface(new object())); + engine.CurrentContext.GetState().Contract.Manifest.Abi.Events[0].Parameters[0].Type = ContractParameterType.InteropInterface; + + Assert.ThrowsException(() => engine.RuntimeNotify(Encoding.ASCII.GetBytes("e1"), array)); + } + + [TestMethod] + public void TestGetRandomSameBlock() + { + var tx = TestUtils.GetTransaction(UInt160.Zero); + // Even if persisting the same block, in different ApplicationEngine instance, the random number should be different + using var engine_1 = ApplicationEngine.Create(TriggerType.Application, tx, null, TestBlockchain.TheNeoSystem.GenesisBlock, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1100_00000000); + using var engine_2 = ApplicationEngine.Create(TriggerType.Application, tx, null, TestBlockchain.TheNeoSystem.GenesisBlock, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1100_00000000); + + engine_1.LoadScript(new byte[] { 0x01 }); + engine_2.LoadScript(new byte[] { 0x01 }); + + var rand_1 = engine_1.GetRandom(); + var rand_2 = engine_1.GetRandom(); + var rand_3 = engine_1.GetRandom(); + var rand_4 = engine_1.GetRandom(); + var rand_5 = engine_1.GetRandom(); + + var rand_6 = engine_2.GetRandom(); + var rand_7 = engine_2.GetRandom(); + var rand_8 = engine_2.GetRandom(); + var rand_9 = engine_2.GetRandom(); + var rand_10 = engine_2.GetRandom(); + + rand_1.Should().Be(BigInteger.Parse("271339657438512451304577787170704246350")); + rand_2.Should().Be(BigInteger.Parse("98548189559099075644778613728143131367")); + rand_3.Should().Be(BigInteger.Parse("247654688993873392544380234598471205121")); + rand_4.Should().Be(BigInteger.Parse("291082758879475329976578097236212073607")); + rand_5.Should().Be(BigInteger.Parse("247152297361212656635216876565962360375")); + + rand_1.Should().Be(rand_6); + rand_2.Should().Be(rand_7); + rand_3.Should().Be(rand_8); + rand_4.Should().Be(rand_9); + rand_5.Should().Be(rand_10); + } + + [TestMethod] + public void TestGetRandomDifferentBlock() + { + var tx_1 = TestUtils.GetTransaction(UInt160.Zero); + + var tx_2 = new Transaction + { + Version = 0, + Nonce = 2083236893, + ValidUntilBlock = 0, + Signers = Array.Empty(), + Attributes = Array.Empty(), + Script = Array.Empty(), + SystemFee = 0, + NetworkFee = 0, + Witnesses = Array.Empty() + }; + + using var engine_1 = ApplicationEngine.Create(TriggerType.Application, tx_1, null, TestBlockchain.TheNeoSystem.GenesisBlock, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1100_00000000); + // The next_nonce shuld be reinitialized when a new block is persisting + using var engine_2 = ApplicationEngine.Create(TriggerType.Application, tx_2, null, TestBlockchain.TheNeoSystem.GenesisBlock, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1100_00000000); + + var rand_1 = engine_1.GetRandom(); + var rand_2 = engine_1.GetRandom(); + var rand_3 = engine_1.GetRandom(); + var rand_4 = engine_1.GetRandom(); + var rand_5 = engine_1.GetRandom(); + + var rand_6 = engine_2.GetRandom(); + var rand_7 = engine_2.GetRandom(); + var rand_8 = engine_2.GetRandom(); + var rand_9 = engine_2.GetRandom(); + var rand_10 = engine_2.GetRandom(); + + rand_1.Should().Be(BigInteger.Parse("271339657438512451304577787170704246350")); + rand_2.Should().Be(BigInteger.Parse("98548189559099075644778613728143131367")); + rand_3.Should().Be(BigInteger.Parse("247654688993873392544380234598471205121")); + rand_4.Should().Be(BigInteger.Parse("291082758879475329976578097236212073607")); + rand_5.Should().Be(BigInteger.Parse("247152297361212656635216876565962360375")); + + rand_1.Should().NotBe(rand_6); + rand_2.Should().NotBe(rand_7); + rand_3.Should().NotBe(rand_8); + rand_4.Should().NotBe(rand_9); + rand_5.Should().NotBe(rand_10); + } + + [TestMethod] + public void TestInvalidUtf8LogMessage() + { + var tx_1 = TestUtils.GetTransaction(UInt160.Zero); + using var engine = ApplicationEngine.Create(TriggerType.Application, tx_1, null, TestBlockchain.TheNeoSystem.GenesisBlock, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1100_00000000); + var msg = new byte[] + { + 68, 216, 160, 6, 89, 102, 86, 72, 37, 15, 132, 45, 76, 221, 170, 21, 128, 51, 34, 168, 205, 56, 10, 228, 51, 114, 4, 218, 245, 155, 172, 132 + }; + Assert.ThrowsException(() => engine.RuntimeLog(msg)); + } + } +} diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.cs similarity index 51% rename from tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs rename to tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.cs index c0c1199627..7639377fe1 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.cs @@ -1,7 +1,21 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ApplicationEngine.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; -using Neo.VM.Types; +using System; +using System.Collections.Immutable; +using System.Linq; +using Array = Neo.VM.Types.Array; namespace Neo.UnitTests.SmartContract { @@ -15,6 +29,7 @@ public void TestNotify() { var snapshot = TestBlockchain.GetTestSnapshot(); using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); + engine.LoadScript(System.Array.Empty()); ApplicationEngine.Notify += Test_Notify1; const string notifyEvent = "TestEvent"; @@ -50,11 +65,43 @@ public void TestCreateDummyBlock() { var snapshot = TestBlockchain.GetTestSnapshot(); byte[] SyscallSystemRuntimeCheckWitnessHash = new byte[] { 0x68, 0xf8, 0x27, 0xec, 0x8c }; - ApplicationEngine engine = ApplicationEngine.Run(SyscallSystemRuntimeCheckWitnessHash, snapshot, diagnostic: new Diagnostic()); + ApplicationEngine engine = ApplicationEngine.Run(SyscallSystemRuntimeCheckWitnessHash, snapshot, settings: TestProtocolSettings.Default); engine.PersistingBlock.Version.Should().Be(0); engine.PersistingBlock.PrevHash.Should().Be(TestBlockchain.TheNeoSystem.GenesisBlock.Hash); engine.PersistingBlock.MerkleRoot.Should().Be(new UInt256()); - string.Join(",", engine.Diagnostic.InvocationTree.GetItems()).Should().Be("0x67bd6dffcf36468c9ffbe6cc4cd0cb7108304d24"); + } + + [TestMethod] + public void TestCheckingHardfork() + { + var allHardforks = Enum.GetValues(typeof(Hardfork)).Cast().ToList(); + + var builder = ImmutableDictionary.CreateBuilder(); + builder.Add(Hardfork.HF_Aspidochelone, 0); + builder.Add(Hardfork.HF_Basilisk, 1); + + var setting = builder.ToImmutable(); + + // Check for continuity in configured hardforks + var sortedHardforks = setting.Keys + .OrderBy(h => allHardforks.IndexOf(h)) + .ToList(); + + for (int i = 0; i < sortedHardforks.Count - 1; i++) + { + int currentIndex = allHardforks.IndexOf(sortedHardforks[i]); + int nextIndex = allHardforks.IndexOf(sortedHardforks[i + 1]); + + // If they aren't consecutive, return false. + var inc = nextIndex - currentIndex; + inc.Should().Be(1); + } + + // Check that block numbers are not higher in earlier hardforks than in later ones + for (int i = 0; i < sortedHardforks.Count - 1; i++) + { + (setting[sortedHardforks[i]] > setting[sortedHardforks[i + 1]]).Should().Be(false); + } } } } diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs similarity index 55% rename from tests/neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs rename to tests/Neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs index aed76fc89e..75fc558669 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs @@ -1,9 +1,20 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ApplicationEngineProvider.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Network.P2P.Payloads; using Neo.Persistence; -using Neo.Plugins; using Neo.SmartContract; +using Neo.VM; namespace Neo.UnitTests.SmartContract { @@ -13,20 +24,19 @@ public class UT_ApplicationEngineProvider [TestInitialize] public void TestInitialize() { - ApplicationEngine.ResetApplicationEngineProvider(); + ApplicationEngine.Provider = null; } [TestCleanup] public void TestCleanup() { - ApplicationEngine.ResetApplicationEngineProvider(); + ApplicationEngine.Provider = null; } [TestMethod] public void TestSetAppEngineProvider() { - var provider = new TestProvider(); - ApplicationEngine.SetApplicationEngineProvider(provider).Should().BeTrue(); + ApplicationEngine.Provider = new TestProvider(); using var appEngine = ApplicationEngine.Create(TriggerType.Application, null, null, gas: 0, settings: TestBlockchain.TheNeoSystem.Settings); (appEngine is TestEngine).Should().BeTrue(); @@ -39,40 +49,18 @@ public void TestDefaultAppEngineProvider() (appEngine is ApplicationEngine).Should().BeTrue(); } - [TestMethod] - public void TestCantSetAppEngineProviderTwice() - { - var provider = new TestProvider(); - ApplicationEngine.SetApplicationEngineProvider(provider).Should().BeTrue(); - - var provider2 = new TestProvider(); - ApplicationEngine.SetApplicationEngineProvider(provider2).Should().BeFalse(); - } - - [TestMethod] - public void TestCanResetAppEngineProviderTwice() - { - var provider = new TestProvider(); - ApplicationEngine.SetApplicationEngineProvider(provider).Should().BeTrue(); - - ApplicationEngine.ResetApplicationEngineProvider(); - - var provider2 = new TestProvider(); - ApplicationEngine.SetApplicationEngineProvider(provider2).Should().BeTrue(); - } - class TestProvider : IApplicationEngineProvider { - public ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, ProtocolSettings settings, long gas, Diagnostic diagnostic) + public ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, ProtocolSettings settings, long gas, IDiagnostic diagnostic, JumpTable jumpTable) { - return new TestEngine(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic); + return new TestEngine(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic, jumpTable); } } class TestEngine : ApplicationEngine { - public TestEngine(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, ProtocolSettings settings, long gas, Diagnostic diagnostic) - : base(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic) + public TestEngine(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, ProtocolSettings settings, long gas, IDiagnostic diagnostic, JumpTable jumpTable) + : base(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic, jumpTable) { } } diff --git a/tests/neo.UnitTests/SmartContract/UT_BinarySerializer.cs b/tests/Neo.UnitTests/SmartContract/UT_BinarySerializer.cs similarity index 81% rename from tests/neo.UnitTests/SmartContract/UT_BinarySerializer.cs rename to tests/Neo.UnitTests/SmartContract/UT_BinarySerializer.cs index 7bae06b68b..6202c8336b 100644 --- a/tests/neo.UnitTests/SmartContract/UT_BinarySerializer.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_BinarySerializer.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_BinarySerializer.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; @@ -13,36 +24,34 @@ namespace Neo.UnitTests.SmartContract [TestClass] public class UT_BinarySerializer { - private const int MaxItemSize = 1024 * 1024; - [TestMethod] public void TestSerialize() { - byte[] result1 = BinarySerializer.Serialize(new byte[5], MaxItemSize); + byte[] result1 = BinarySerializer.Serialize(new byte[5], ExecutionEngineLimits.Default); byte[] expectedArray1 = new byte[] { 0x28, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 }; Assert.AreEqual(Encoding.Default.GetString(expectedArray1), Encoding.Default.GetString(result1)); - byte[] result2 = BinarySerializer.Serialize(true, MaxItemSize); + byte[] result2 = BinarySerializer.Serialize(true, ExecutionEngineLimits.Default); byte[] expectedArray2 = new byte[] { 0x20, 0x01 }; Assert.AreEqual(Encoding.Default.GetString(expectedArray2), Encoding.Default.GetString(result2)); - byte[] result3 = BinarySerializer.Serialize(1, MaxItemSize); + byte[] result3 = BinarySerializer.Serialize(1, ExecutionEngineLimits.Default); byte[] expectedArray3 = new byte[] { 0x21, 0x01, 0x01 }; Assert.AreEqual(Encoding.Default.GetString(expectedArray3), Encoding.Default.GetString(result3)); StackItem stackItem4 = new InteropInterface(new object()); - Action action4 = () => BinarySerializer.Serialize(stackItem4, MaxItemSize); + Action action4 = () => BinarySerializer.Serialize(stackItem4, ExecutionEngineLimits.Default); action4.Should().Throw(); List list6 = new List { 1 }; StackItem stackItem62 = new VM.Types.Array(list6); - byte[] result6 = BinarySerializer.Serialize(stackItem62, MaxItemSize); + byte[] result6 = BinarySerializer.Serialize(stackItem62, ExecutionEngineLimits.Default); byte[] expectedArray6 = new byte[] { 0x40,0x01,0x21,0x01,0x01 }; @@ -50,14 +59,14 @@ public void TestSerialize() List list7 = new List { 1 }; StackItem stackItem72 = new Struct(list7); - byte[] result7 = BinarySerializer.Serialize(stackItem72, MaxItemSize); + byte[] result7 = BinarySerializer.Serialize(stackItem72, ExecutionEngineLimits.Default); byte[] expectedArray7 = new byte[] { 0x41,0x01,0x21,0x01,0x01 }; Assert.AreEqual(Encoding.Default.GetString(expectedArray7), Encoding.Default.GetString(result7)); StackItem stackItem82 = new Map { [2] = 1 }; - byte[] result8 = BinarySerializer.Serialize(stackItem82, MaxItemSize); + byte[] result8 = BinarySerializer.Serialize(stackItem82, ExecutionEngineLimits.Default); byte[] expectedArray8 = new byte[] { 0x48,0x01,0x21,0x01,0x02,0x21,0x01,0x01 }; @@ -65,12 +74,12 @@ public void TestSerialize() Map stackItem91 = new Map(); stackItem91[1] = stackItem91; - Action action9 = () => BinarySerializer.Serialize(stackItem91, MaxItemSize); + Action action9 = () => BinarySerializer.Serialize(stackItem91, ExecutionEngineLimits.Default); action9.Should().Throw(); VM.Types.Array stackItem10 = new VM.Types.Array(); stackItem10.Add(stackItem10); - Action action10 = () => BinarySerializer.Serialize(stackItem10, MaxItemSize); + Action action10 = () => BinarySerializer.Serialize(stackItem10, ExecutionEngineLimits.Default); action10.Should().Throw(); } @@ -78,41 +87,41 @@ public void TestSerialize() public void TestDeserializeStackItem() { StackItem stackItem1 = new ByteString(new byte[5]); - byte[] byteArray1 = BinarySerializer.Serialize(stackItem1, MaxItemSize); + byte[] byteArray1 = BinarySerializer.Serialize(stackItem1, ExecutionEngineLimits.Default); StackItem result1 = BinarySerializer.Deserialize(byteArray1, ExecutionEngineLimits.Default); Assert.AreEqual(stackItem1, result1); - StackItem stackItem2 = new VM.Types.Boolean(true); - byte[] byteArray2 = BinarySerializer.Serialize(stackItem2, MaxItemSize); + StackItem stackItem2 = StackItem.True; + byte[] byteArray2 = BinarySerializer.Serialize(stackItem2, ExecutionEngineLimits.Default); StackItem result2 = BinarySerializer.Deserialize(byteArray2, ExecutionEngineLimits.Default); Assert.AreEqual(stackItem2, result2); StackItem stackItem3 = new Integer(1); - byte[] byteArray3 = BinarySerializer.Serialize(stackItem3, MaxItemSize); + byte[] byteArray3 = BinarySerializer.Serialize(stackItem3, ExecutionEngineLimits.Default); StackItem result3 = BinarySerializer.Deserialize(byteArray3, ExecutionEngineLimits.Default); Assert.AreEqual(stackItem3, result3); - byte[] byteArray4 = BinarySerializer.Serialize(1, MaxItemSize); + byte[] byteArray4 = BinarySerializer.Serialize(1, ExecutionEngineLimits.Default); byteArray4[0] = 0x40; Action action4 = () => BinarySerializer.Deserialize(byteArray4, ExecutionEngineLimits.Default); action4.Should().Throw(); List list5 = new List { 1 }; StackItem stackItem52 = new VM.Types.Array(list5); - byte[] byteArray5 = BinarySerializer.Serialize(stackItem52, MaxItemSize); + byte[] byteArray5 = BinarySerializer.Serialize(stackItem52, ExecutionEngineLimits.Default); StackItem result5 = BinarySerializer.Deserialize(byteArray5, ExecutionEngineLimits.Default); Assert.AreEqual(((VM.Types.Array)stackItem52).Count, ((VM.Types.Array)result5).Count); Assert.AreEqual(((VM.Types.Array)stackItem52).GetEnumerator().Current, ((VM.Types.Array)result5).GetEnumerator().Current); List list6 = new List { 1 }; StackItem stackItem62 = new Struct(list6); - byte[] byteArray6 = BinarySerializer.Serialize(stackItem62, MaxItemSize); + byte[] byteArray6 = BinarySerializer.Serialize(stackItem62, ExecutionEngineLimits.Default); StackItem result6 = BinarySerializer.Deserialize(byteArray6, ExecutionEngineLimits.Default); Assert.AreEqual(((Struct)stackItem62).Count, ((Struct)result6).Count); Assert.AreEqual(((Struct)stackItem62).GetEnumerator().Current, ((Struct)result6).GetEnumerator().Current); StackItem stackItem72 = new Map { [2] = 1 }; - byte[] byteArray7 = BinarySerializer.Serialize(stackItem72, MaxItemSize); + byte[] byteArray7 = BinarySerializer.Serialize(stackItem72, ExecutionEngineLimits.Default); StackItem result7 = BinarySerializer.Deserialize(byteArray7, ExecutionEngineLimits.Default); Assert.AreEqual(((Map)stackItem72).Count, ((Map)result7).Count); CollectionAssert.AreEqual(((Map)stackItem72).Keys.ToArray(), ((Map)result7).Keys.ToArray()); diff --git a/tests/neo.UnitTests/SmartContract/UT_Contract.cs b/tests/Neo.UnitTests/SmartContract/UT_Contract.cs similarity index 92% rename from tests/neo.UnitTests/SmartContract/UT_Contract.cs rename to tests/Neo.UnitTests/SmartContract/UT_Contract.cs index 1661203d82..a263779282 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Contract.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_Contract.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Contract.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Network.P2P.Payloads; @@ -153,7 +164,7 @@ public void TestSignatureRedeemScriptFee() byte[] verification = Contract.CreateSignatureRedeemScript(key.PublicKey); byte[] invocation = new ScriptBuilder().EmitPush(UInt160.Zero).ToArray(); - var fee = PolicyContract.DefaultExecFeeFactor * (ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * 2 + ApplicationEngine.OpCodePrices[OpCode.SYSCALL] + ApplicationEngine.CheckSigPrice); + var fee = PolicyContract.DefaultExecFeeFactor * (ApplicationEngine.OpCodePriceTable[(byte)OpCode.PUSHDATA1] * 2 + ApplicationEngine.OpCodePriceTable[(byte)OpCode.SYSCALL] + ApplicationEngine.CheckSigPrice); using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, new Transaction { Signers = Array.Empty(), Attributes = Array.Empty() }, null, settings: TestBlockchain.TheNeoSystem.Settings)) { @@ -181,7 +192,7 @@ public void TestCreateMultiSigRedeemScriptFee() byte[] verification = Contract.CreateMultiSigRedeemScript(2, publicKeys); byte[] invocation = new ScriptBuilder().EmitPush(UInt160.Zero).EmitPush(UInt160.Zero).ToArray(); - long fee = PolicyContract.DefaultExecFeeFactor * (ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * (2 + 2) + ApplicationEngine.OpCodePrices[OpCode.PUSHINT8] * 2 + ApplicationEngine.OpCodePrices[OpCode.SYSCALL] + ApplicationEngine.CheckSigPrice * 2); + long fee = PolicyContract.DefaultExecFeeFactor * (ApplicationEngine.OpCodePriceTable[(byte)OpCode.PUSHDATA1] * (2 + 2) + ApplicationEngine.OpCodePriceTable[(byte)OpCode.PUSHINT8] * 2 + ApplicationEngine.OpCodePriceTable[(byte)OpCode.SYSCALL] + ApplicationEngine.CheckSigPrice * 2); using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, new Transaction { Signers = Array.Empty(), Attributes = Array.Empty() }, null, settings: TestBlockchain.TheNeoSystem.Settings)) { diff --git a/tests/neo.UnitTests/SmartContract/UT_ContractParameter.cs b/tests/Neo.UnitTests/SmartContract/UT_ContractParameter.cs similarity index 95% rename from tests/neo.UnitTests/SmartContract/UT_ContractParameter.cs rename to tests/Neo.UnitTests/SmartContract/UT_ContractParameter.cs index 8b8dd8d22d..3c567ca5bc 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ContractParameter.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_ContractParameter.cs @@ -1,7 +1,18 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ContractParameter.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using System; using System.Collections.Generic; diff --git a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs b/tests/Neo.UnitTests/SmartContract/UT_ContractParameterContext.cs similarity index 85% rename from tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs rename to tests/Neo.UnitTests/SmartContract/UT_ContractParameterContext.cs index 2a16547542..aba90cc6f0 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_ContractParameterContext.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ContractParameterContext.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; @@ -34,7 +45,7 @@ public void TestGetComplete() { var snapshot = TestBlockchain.GetTestSnapshot(); Transaction tx = TestUtils.GetTransaction(UInt160.Parse("0x1bd5c777ec35768892bd3daab60fb7a1cb905066")); - var context = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + var context = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); context.Completed.Should().BeFalse(); } @@ -43,19 +54,19 @@ public void TestToString() { var snapshot = TestBlockchain.GetTestSnapshot(); Transaction tx = TestUtils.GetTransaction(UInt160.Parse("0x1bd5c777ec35768892bd3daab60fb7a1cb905066")); - var context = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + var context = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); context.Add(contract, 0, new byte[] { 0x01 }); string str = context.ToString(); - str.Should().Be(@"{""type"":""Neo.Network.P2P.Payloads.Transaction"",""hash"":""0x602c1fa1c08b041e4e6b87aa9a9f9c643166cd34bdd5215a3dd85778c59cce88"",""data"":""AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFmUJDLobcPtqo9vZKIdjXsd8fVGwEAARI="",""items"":{},""network"":" + ProtocolSettings.Default.Network + "}"); + str.Should().Be(@"{""type"":""Neo.Network.P2P.Payloads.Transaction"",""hash"":""0x602c1fa1c08b041e4e6b87aa9a9f9c643166cd34bdd5215a3dd85778c59cce88"",""data"":""AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFmUJDLobcPtqo9vZKIdjXsd8fVGwEAARI="",""items"":{},""network"":" + TestProtocolSettings.Default.Network + "}"); } [TestMethod] public void TestParse() { var snapshot = TestBlockchain.GetTestSnapshot(); - var ret = ContractParametersContext.Parse("{\"type\":\"Neo.Network.P2P.Payloads.Transaction\",\"data\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFmUJDLobcPtqo9vZKIdjXsd8fVGwEAARI=\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"IQJv8DuUkkHOHa3UNRnmlg4KhbQaaaBcMoEDqivOFZTKFmh0dHaq\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"AQ==\"}],\"signatures\":{\"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c\":\"AQ==\"}}},\"network\":" + ProtocolSettings.Default.Network + "}", snapshot); + var ret = ContractParametersContext.Parse("{\"type\":\"Neo.Network.P2P.Payloads.Transaction\",\"data\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFmUJDLobcPtqo9vZKIdjXsd8fVGwEAARI=\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"IQJv8DuUkkHOHa3UNRnmlg4KhbQaaaBcMoEDqivOFZTKFmh0dHaq\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"AQ==\"}],\"signatures\":{\"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c\":\"AQ==\"}}},\"network\":" + TestProtocolSettings.Default.Network + "}", snapshot); ret.ScriptHashes[0].ToString().Should().Be("0x1bd5c777ec35768892bd3daab60fb7a1cb905066"); - ((Transaction)ret.Verifiable).Script.ToHexString().Should().Be(new byte[] { 18 }.ToHexString()); + ((Transaction)ret.Verifiable).Script.Span.ToHexString().Should().Be(new byte[] { 18 }.ToHexString()); } [TestMethod] @@ -71,11 +82,11 @@ public void TestAdd() { var snapshot = TestBlockchain.GetTestSnapshot(); Transaction tx = TestUtils.GetTransaction(UInt160.Zero); - var context1 = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + var context1 = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); context1.Add(contract, 0, new byte[] { 0x01 }).Should().BeFalse(); tx = TestUtils.GetTransaction(UInt160.Parse("0x902e0d38da5e513b6d07c1c55b85e77d3dce8063")); - var context2 = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + var context2 = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); context2.Add(contract, 0, new byte[] { 0x01 }).Should().BeTrue(); //test repeatlly createItem context2.Add(contract, 0, new byte[] { 0x01 }).Should().BeTrue(); @@ -86,7 +97,7 @@ public void TestGetParameter() { var snapshot = TestBlockchain.GetTestSnapshot(); Transaction tx = TestUtils.GetTransaction(UInt160.Parse("0x902e0d38da5e513b6d07c1c55b85e77d3dce8063")); - var context = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + var context = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); context.GetParameter(tx.Sender, 0).Should().BeNull(); context.Add(contract, 0, new byte[] { 0x01 }); @@ -99,12 +110,12 @@ public void TestGetWitnesses() { var snapshot = TestBlockchain.GetTestSnapshot(); Transaction tx = TestUtils.GetTransaction(UInt160.Parse("0x902e0d38da5e513b6d07c1c55b85e77d3dce8063")); - var context = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + var context = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); context.Add(contract, 0, new byte[] { 0x01 }); Witness[] witnesses = context.GetWitnesses(); witnesses.Length.Should().Be(1); - witnesses[0].InvocationScript.ToHexString().Should().Be(new byte[] { (byte)OpCode.PUSHDATA1, 0x01, 0x01 }.ToHexString()); - witnesses[0].VerificationScript.ToHexString().Should().Be(contract.Script.ToHexString()); + witnesses[0].InvocationScript.Span.ToHexString().Should().Be(new byte[] { (byte)OpCode.PUSHDATA1, 0x01, 0x01 }.ToHexString()); + witnesses[0].VerificationScript.Span.ToHexString().Should().Be(contract.Script.ToHexString()); } [TestMethod] @@ -116,12 +127,12 @@ public void TestAddSignature() //singleSign - var context = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + var context = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); context.AddSignature(contract, key.PublicKey, new byte[] { 0x01 }).Should().BeTrue(); var contract1 = Contract.CreateSignatureContract(key.PublicKey); contract1.ParameterList = Array.Empty(); - context = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + context = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); context.AddSignature(contract1, key.PublicKey, new byte[] { 0x01 }).Should().BeFalse(); contract1.ParameterList = new[] { ContractParameterType.Signature, ContractParameterType.Signature }; @@ -143,16 +154,16 @@ public void TestAddSignature() }); var multiSender = UInt160.Parse("0xf76b51bc6605ac3cfcd188173af0930507f51210"); tx = TestUtils.GetTransaction(multiSender); - context = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + context = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); context.AddSignature(multiSignContract, key.PublicKey, new byte[] { 0x01 }).Should().BeTrue(); context.AddSignature(multiSignContract, key2.PublicKey, new byte[] { 0x01 }).Should().BeTrue(); tx = TestUtils.GetTransaction(singleSender); - context = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + context = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); context.AddSignature(multiSignContract, key.PublicKey, new byte[] { 0x01 }).Should().BeFalse(); tx = TestUtils.GetTransaction(multiSender); - context = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); + context = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); byte[] privateKey3 = new byte[] { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, diff --git a/tests/neo.UnitTests/SmartContract/UT_ContractState.cs b/tests/Neo.UnitTests/SmartContract/UT_ContractState.cs similarity index 79% rename from tests/neo.UnitTests/SmartContract/UT_ContractState.cs rename to tests/Neo.UnitTests/SmartContract/UT_ContractState.cs index f24f5b350b..d0e52ae234 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ContractState.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_ContractState.cs @@ -1,6 +1,17 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ContractState.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.SmartContract.Manifest; using Neo.VM; @@ -12,7 +23,7 @@ namespace Neo.UnitTests.SmartContract public class UT_ContractState { ContractState contract; - byte[] script = { 0x01 }; + readonly byte[] script = { 0x01 }; ContractManifest manifest; [TestInitialize] @@ -49,7 +60,7 @@ public void TestIInteroperable() IInteroperable newContract = new ContractState(); newContract.FromStackItem(contract.ToStackItem(null)); ((ContractState)newContract).Manifest.ToJson().ToString().Should().Be(contract.Manifest.ToJson().ToString()); - ((ContractState)newContract).Script.Should().BeEquivalentTo(contract.Script); + ((ContractState)newContract).Script.Span.SequenceEqual(contract.Script.Span).Should().BeTrue(); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_DeployedContract.cs b/tests/Neo.UnitTests/SmartContract/UT_DeployedContract.cs similarity index 84% rename from tests/neo.UnitTests/SmartContract/UT_DeployedContract.cs rename to tests/Neo.UnitTests/SmartContract/UT_DeployedContract.cs index b1571868bc..b5f8bbde0e 100644 --- a/tests/neo.UnitTests/SmartContract/UT_DeployedContract.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_DeployedContract.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_DeployedContract.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.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; using System; diff --git a/tests/neo.UnitTests/SmartContract/UT_Helper.cs b/tests/Neo.UnitTests/SmartContract/UT_Helper.cs similarity index 84% rename from tests/neo.UnitTests/SmartContract/UT_Helper.cs rename to tests/Neo.UnitTests/SmartContract/UT_Helper.cs index 343724bd4d..3d682a4ee9 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Helper.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_Helper.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_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 Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; using Neo.Network.P2P.Payloads; @@ -6,6 +17,7 @@ using Neo.VM; using Neo.Wallets; using System; +using static Neo.SmartContract.Helper; namespace Neo.UnitTests.SmartContract { @@ -48,7 +60,7 @@ public void TestIsMultiSigContract() 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, }; - Assert.IsFalse(case1.IsMultiSigContract()); + Assert.IsFalse(IsMultiSigContract(case1)); var case2 = new byte[] { @@ -57,7 +69,7 @@ public void TestIsMultiSigContract() 29, 173, 212, 53, 25, 230, 150, 14, 10, 133, 180, 26, 105, 160, 92, 50, 129, 3, 170, 43, 206, 21, 148, 202, 22, 18 }; - Assert.IsFalse(case2.IsMultiSigContract()); + Assert.IsFalse(IsMultiSigContract(case2)); } [TestMethod] @@ -69,10 +81,10 @@ public void TestSignatureContractCost() tx.Signers[0].Account = contract.ScriptHash; using ScriptBuilder invocationScript = new(); - invocationScript.EmitPush(Neo.Wallets.Helper.Sign(tx, _key, ProtocolSettings.Default.Network)); + invocationScript.EmitPush(Neo.Wallets.Helper.Sign(tx, _key, TestProtocolSettings.Default.Network)); tx.Witnesses = new Witness[] { new Witness() { InvocationScript = invocationScript.ToArray(), VerificationScript = contract.Script } }; - using var engine = ApplicationEngine.Create(TriggerType.Verification, tx, null, null, ProtocolSettings.Default); + using var engine = ApplicationEngine.Create(TriggerType.Verification, tx, null, null, TestProtocolSettings.Default); engine.LoadScript(contract.Script); engine.LoadScript(new Script(invocationScript.ToArray(), true), configureState: p => p.CallFlags = CallFlags.None); Assert.AreEqual(VMState.HALT, engine.Execute()); @@ -90,9 +102,9 @@ public void TestMultiSignatureContractCost() tx.Signers[0].Account = contract.ScriptHash; using ScriptBuilder invocationScript = new(); - invocationScript.EmitPush(Neo.Wallets.Helper.Sign(tx, _key, ProtocolSettings.Default.Network)); + invocationScript.EmitPush(Neo.Wallets.Helper.Sign(tx, _key, TestProtocolSettings.Default.Network)); - using var engine = ApplicationEngine.Create(TriggerType.Verification, tx, null, null, ProtocolSettings.Default); + using var engine = ApplicationEngine.Create(TriggerType.Verification, tx, null, null, TestProtocolSettings.Default); engine.LoadScript(contract.Script); engine.LoadScript(new Script(invocationScript.ToArray(), true), configureState: p => p.CallFlags = CallFlags.None); Assert.AreEqual(VMState.HALT, engine.Execute()); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/Neo.UnitTests/SmartContract/UT_InteropPrices.cs similarity index 95% rename from tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs rename to tests/Neo.UnitTests/SmartContract/UT_InteropPrices.cs index 96b20779e9..a55db382df 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_InteropPrices.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/Neo.UnitTests/SmartContract/UT_InteropService.NEO.cs similarity index 91% rename from tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs rename to tests/Neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 6dd34672a9..49d7690188 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_InteropService.NEO.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; @@ -23,7 +34,7 @@ public void TestCheckSig() { var engine = GetEngine(true); IVerifiable iv = engine.ScriptContainer; - byte[] message = iv.GetSignData(ProtocolSettings.Default.Network); + byte[] message = iv.GetSignData(TestProtocolSettings.Default.Network); byte[] privateKey = { 0x01,0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; KeyPair keyPair = new KeyPair(privateKey); @@ -39,7 +50,7 @@ public void TestCrypto_CheckMultiSig() { var engine = GetEngine(true); IVerifiable iv = engine.ScriptContainer; - byte[] message = iv.GetSignData(ProtocolSettings.Default.Network); + byte[] message = iv.GetSignData(TestProtocolSettings.Default.Network); byte[] privkey1 = { 0x01,0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; @@ -121,12 +132,14 @@ public void TestContract_Create() var script_exceedMaxLength = new NefFile() { - Script = new byte[NefFile.MaxScriptLength - 1], + Script = new byte[ExecutionEngineLimits.Default.MaxItemSize - 50], Source = string.Empty, Compiler = "", - Tokens = System.Array.Empty() + Tokens = Array.Empty() }; - script_exceedMaxLength.CheckSum = NefFile.ComputeChecksum(nef); + script_exceedMaxLength.CheckSum = NefFile.ComputeChecksum(script_exceedMaxLength); + + Assert.ThrowsException(() => script_exceedMaxLength.ToArray().AsSerializable()); Assert.ThrowsException(() => snapshot.DeployContract(UInt160.Zero, script_exceedMaxLength.ToArray(), manifest.ToJson().ToByteArray(true))); var script_zeroLength = System.Array.Empty(); @@ -195,7 +208,7 @@ public void TestContract_Update() ret.UpdateCounter.Should().Be(1); ret.Id.Should().Be(state.Id); ret.Manifest.ToJson().ToString().Should().Be(manifest.ToJson().ToString()); - ret.Script.ToHexString().Should().Be(nef.Script.ToHexString().ToString()); + ret.Script.Span.ToHexString().Should().Be(nef.Script.Span.ToHexString().ToString()); } [TestMethod] @@ -255,8 +268,8 @@ public void TestStorage_Find() IsReadOnly = false }, new byte[] { 0x01 }, FindOptions.ValuesOnly); iterator.Next(); - var ele = iterator.Value(); - ele.GetSpan().ToHexString().Should().Be(storageItem.Value.ToHexString()); + var ele = iterator.Value(null); + ele.GetSpan().ToHexString().Should().Be(storageItem.Value.Span.ToHexString()); } } } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/Neo.UnitTests/SmartContract/UT_InteropService.cs similarity index 75% rename from tests/neo.UnitTests/SmartContract/UT_InteropService.cs rename to tests/Neo.UnitTests/SmartContract/UT_InteropService.cs index 0e49b41918..a0ab12fdc3 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_InteropService.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_InteropService.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 Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -45,17 +56,27 @@ public void Runtime_GetNotifications_Test() scriptHash2 = script.ToArray().ToScriptHash(); snapshot.DeleteContract(scriptHash2); - snapshot.AddContract(scriptHash2, new ContractState() + var contract = TestUtils.GetContract(script.ToArray(), TestUtils.CreateManifest("test", ContractParameterType.Any, ContractParameterType.Integer, ContractParameterType.Integer)); + contract.Manifest.Abi.Events = new[] { - Nef = new NefFile { Script = script.ToArray() }, - Hash = script.ToArray().ToScriptHash(), - Manifest = TestUtils.CreateManifest("test", ContractParameterType.Any, ContractParameterType.Integer, ContractParameterType.Integer), - }); + new ContractEventDescriptor + { + Name = "testEvent2", + Parameters = new[] + { + new ContractParameterDefinition + { + Type = ContractParameterType.Any + } + } + } + }; + snapshot.AddContract(scriptHash2, contract); } // Wrong length - using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot)) + using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, ProtocolSettings.Default)) using (var script = new ScriptBuilder()) { // Retrive @@ -72,7 +93,7 @@ public void Runtime_GetNotifications_Test() // All test - using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot)) + using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, ProtocolSettings.Default)) using (var script = new ScriptBuilder()) { // Notification @@ -98,6 +119,23 @@ public void Runtime_GetNotifications_Test() // Execute engine.LoadScript(script.ToArray()); + engine.CurrentContext.GetState().Contract = new() + { + Manifest = new() + { + Abi = new() + { + Events = new[] + { + new ContractEventDescriptor + { + Name = "testEvent1", + Parameters = System.Array.Empty() + } + } + } + } + }; var currentScriptHash = engine.EntryScriptHash; Assert.AreEqual(VMState.HALT, engine.Execute()); @@ -124,7 +162,7 @@ public void Runtime_GetNotifications_Test() // Script notifications - using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot)) + using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, ProtocolSettings.Default)) using (var script = new ScriptBuilder()) { // Notification @@ -150,6 +188,23 @@ public void Runtime_GetNotifications_Test() // Execute engine.LoadScript(script.ToArray()); + engine.CurrentContext.GetState().Contract = new() + { + Manifest = new() + { + Abi = new() + { + Events = new[] + { + new ContractEventDescriptor + { + Name = "testEvent1", + Parameters = System.Array.Empty() + } + } + } + } + }; var currentScriptHash = engine.EntryScriptHash; Assert.AreEqual(VMState.HALT, engine.Execute()); @@ -209,12 +264,7 @@ public void TestExecutionEngine_GetCallingScriptHash() scriptA.Emit(OpCode.DROP); // Drop method scriptA.EmitSysCall(ApplicationEngine.System_Runtime_GetCallingScriptHash); - var contract = new ContractState() - { - Manifest = TestUtils.CreateManifest("test", ContractParameterType.Any, ContractParameterType.String, ContractParameterType.Integer), - Nef = new NefFile { Script = scriptA.ToArray() }, - Hash = scriptA.ToArray().ToScriptHash() - }; + var contract = TestUtils.GetContract(scriptA.ToArray(), TestUtils.CreateManifest("test", ContractParameterType.Any, ContractParameterType.String, ContractParameterType.Integer)); engine = GetEngine(true, true, addScript: false); engine.Snapshot.AddContract(contract.Hash, contract); @@ -244,8 +294,8 @@ public void TestRuntime_CheckWitness() { byte[] privateKey = { 0x01,0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; - KeyPair keyPair = new(privateKey); - ECPoint pubkey = keyPair.PublicKey; + var keyPair = new KeyPair(privateKey); + var pubkey = keyPair.PublicKey; var engine = GetEngine(true); ((Transaction)engine.ScriptContainer).Signers[0].Account = Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash(); @@ -261,14 +311,27 @@ public void TestRuntime_CheckWitness() action.Should().Throw(); } + [TestMethod] + public void TestRuntime_CheckWitness_Null_ScriptContainer() + { + byte[] privateKey = { 0x01,0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; + var keyPair = new KeyPair(privateKey); + var pubkey = keyPair.PublicKey; + + var engine = GetEngine(); + + engine.CheckWitness(pubkey.EncodePoint(true)).Should().BeFalse(); + } + [TestMethod] public void TestRuntime_Log() { var engine = GetEngine(true); - string message = "hello"; + var message = "hello"; ApplicationEngine.Log += LogEvent; engine.RuntimeLog(Encoding.UTF8.GetBytes(message)); - ((Transaction)engine.ScriptContainer).Script.ToHexString().Should().Be(new byte[] { 0x01, 0x02, 0x03 }.ToHexString()); + ((Transaction)engine.ScriptContainer).Script.Span.ToHexString().Should().Be(new byte[] { 0x01, 0x02, 0x03 }.ToHexString()); ApplicationEngine.Log -= LogEvent; } @@ -287,20 +350,62 @@ public void TestRuntime_GetInvocationCounter() Assert.AreEqual(1, engine.GetInvocationCounter()); } + [TestMethod] + public void TestRuntime_GetCurrentSigners() + { + using var engine = GetEngine(hasContainer: true); + Assert.AreEqual(UInt160.Zero, engine.GetCurrentSigners()[0].Account); + } + + [TestMethod] + public void TestRuntime_GetCurrentSigners_SysCall() + { + using ScriptBuilder script = new(); + script.EmitSysCall(ApplicationEngine.System_Runtime_CurrentSigners.Hash); + + // Null + + using var engineA = GetEngine(hasSnapshot: true, addScript: false, hasContainer: false); + + engineA.LoadScript(script.ToArray()); + engineA.Execute(); + Assert.AreEqual(engineA.State, VMState.HALT); + + var result = engineA.ResultStack.Pop(); + result.Should().BeOfType(typeof(VM.Types.Null)); + + // Not null + + using var engineB = GetEngine(hasSnapshot: true, addScript: false, hasContainer: true); + + engineB.LoadScript(script.ToArray()); + engineB.Execute(); + Assert.AreEqual(engineB.State, VMState.HALT); + + result = engineB.ResultStack.Pop(); + result.Should().BeOfType(typeof(VM.Types.Array)); + (result as VM.Types.Array).Count.Should().Be(1); + result = (result as VM.Types.Array)[0]; + result.Should().BeOfType(typeof(VM.Types.Array)); + (result as VM.Types.Array).Count.Should().Be(5); + result = (result as VM.Types.Array)[0]; // Address + Assert.AreEqual(UInt160.Zero, new UInt160(result.GetSpan())); + } + [TestMethod] public void TestCrypto_Verify() { var engine = GetEngine(true); - IVerifiable iv = engine.ScriptContainer; - byte[] message = iv.GetSignData(ProtocolSettings.Default.Network); + var iv = engine.ScriptContainer; + var message = iv.GetSignData(TestProtocolSettings.Default.Network); byte[] privateKey = { 0x01,0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; KeyPair keyPair = new(privateKey); - ECPoint pubkey = keyPair.PublicKey; - byte[] signature = Crypto.Sign(message, privateKey, pubkey.EncodePoint(false).Skip(1).ToArray()); + var pubkey = keyPair.PublicKey; + var signature = Crypto.Sign(message, privateKey, pubkey.EncodePoint(false).Skip(1).ToArray()); engine.CheckSig(pubkey.EncodePoint(false), signature).Should().BeTrue(); - byte[] wrongkey = pubkey.EncodePoint(false); + var wrongkey = pubkey.EncodePoint(false); wrongkey[0] = 5; Assert.ThrowsException(() => engine.CheckSig(wrongkey, signature)); } @@ -319,7 +424,7 @@ public void TestBlockchain_GetBlock() NativeContract.Ledger.GetBlock(engine.Snapshot, UInt256.Zero).Should().BeNull(); - byte[] data1 = new byte[] { 0x01, 0x01, 0x01 ,0x01, 0x01, 0x01, 0x01, 0x01, + var data1 = new byte[] { 0x01, 0x01, 0x01 ,0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; @@ -331,7 +436,7 @@ public void TestBlockchain_GetBlock() public void TestBlockchain_GetTransaction() { var engine = GetEngine(true, true); - byte[] data1 = new byte[] { 0x01, 0x01, 0x01 ,0x01, 0x01, 0x01, 0x01, 0x01, + var data1 = new byte[] { 0x01, 0x01, 0x01 ,0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; @@ -364,7 +469,7 @@ public void TestBlockchain_GetTransactionHeight() public void TestBlockchain_GetContract() { var engine = GetEngine(true, true); - byte[] data1 = new byte[] { 0x01, 0x01, 0x01 ,0x01, 0x01, + var data1 = new byte[] { 0x01, 0x01, 0x01 ,0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; @@ -375,7 +480,7 @@ public void TestBlockchain_GetContract() snapshot.AddContract(state.Hash, state); engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(new byte[] { 0x01 }); - NativeContract.ContractManagement.GetContract(engine.Snapshot, state.Hash).Should().BeSameAs(state); + NativeContract.ContractManagement.GetContract(engine.Snapshot, state.Hash).Hash.Should().Be(state.Hash); } [TestMethod] @@ -423,7 +528,7 @@ public void TestStorage_Get() { Id = state.Id, IsReadOnly = false - }, new byte[] { 0x01 }).ToHexString().Should().Be(storageItem.Value.ToHexString()); + }, new byte[] { 0x01 }).Value.Span.ToHexString().Should().Be(storageItem.Value.Span.ToHexString()); } [TestMethod] @@ -533,24 +638,30 @@ public void TestStorageContext_AsReadOnly() public void TestContract_Call() { var snapshot = TestBlockchain.GetTestSnapshot(); - string method = "method"; + var method = "method"; var args = new VM.Types.Array { 0, 1 }; var state = TestUtils.GetContract(method, args.Count); - snapshot.AddContract(state.Hash, state); var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(new byte[] { 0x01 }); + engine.Snapshot.AddContract(state.Hash, state); engine.CallContract(state.Hash, method, CallFlags.All, args); engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[0]); engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[1]); state.Manifest.Permissions[0].Methods = WildcardContainer.Create("a"); + engine.Snapshot.DeleteContract(state.Hash); + engine.Snapshot.AddContract(state.Hash, state); Assert.ThrowsException(() => engine.CallContract(state.Hash, method, CallFlags.All, args)); state.Manifest.Permissions[0].Methods = WildcardContainer.CreateWildcard(); + engine.Snapshot.DeleteContract(state.Hash); + engine.Snapshot.AddContract(state.Hash, state); engine.CallContract(state.Hash, method, CallFlags.All, args); + engine.Snapshot.DeleteContract(state.Hash); + engine.Snapshot.AddContract(state.Hash, state); Assert.ThrowsException(() => engine.CallContract(UInt160.Zero, method, CallFlags.All, args)); } @@ -585,13 +696,13 @@ public void TestContract_Destroy() [TestMethod] public void TestContract_CreateStandardAccount() { - ECPoint pubkey = ECPoint.Parse("024b817ef37f2fc3d4a33fe36687e592d9f30fe24b3e28187dc8f12b3b3b2b839e", ECCurve.Secp256r1); - ApplicationEngine.CreateStandardAccount(pubkey).ToArray().ToHexString().Should().Be("c44ea575c5f79638f0e73f39d7bd4b3337c81691"); + var pubkey = ECPoint.Parse("024b817ef37f2fc3d4a33fe36687e592d9f30fe24b3e28187dc8f12b3b3b2b839e", ECCurve.Secp256r1); + GetEngine().CreateStandardAccount(pubkey).ToArray().ToHexString().Should().Be("c44ea575c5f79638f0e73f39d7bd4b3337c81691"); } public static void LogEvent(object sender, LogEventArgs args) { - Transaction tx = (Transaction)args.ScriptContainer; + var tx = (Transaction)args.ScriptContainer; tx.Script = new byte[] { 0x01, 0x02, 0x03 }; } @@ -600,7 +711,7 @@ private static ApplicationEngine GetEngine(bool hasContainer = false, bool hasSn var tx = hasContainer ? TestUtils.GetTransaction(UInt160.Zero) : null; var snapshot = hasSnapshot ? TestBlockchain.GetTestSnapshot() : null; var block = hasBlock ? new Block { Header = new Header() } : null; - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshot, block, TestBlockchain.TheNeoSystem.Settings, gas: gas); + var engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshot, block, TestBlockchain.TheNeoSystem.Settings, gas: gas); if (addScript) engine.LoadScript(new byte[] { 0x01 }); return engine; } diff --git a/tests/neo.UnitTests/SmartContract/UT_JsonSerializer.cs b/tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs similarity index 83% rename from tests/neo.UnitTests/SmartContract/UT_JsonSerializer.cs rename to tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs index 7bea6ffc43..fc29c95c12 100644 --- a/tests/neo.UnitTests/SmartContract/UT_JsonSerializer.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs @@ -1,5 +1,16 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_JsonSerializer.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.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.VM; using Neo.VM.Types; @@ -80,10 +91,10 @@ public void JsonTest_Numbers() Assert.AreEqual("[1,-2,3.5]", parsed.ToString()); - json = "[200.500000E+005,200.500000e+5,-1.1234e-100]"; + json = "[200.500000E+005,200.500000e+5,-1.1234e-100,9.05E+28]"; parsed = JObject.Parse(json); - Assert.AreEqual("[20050000,20050000,-1.1234E-100]", parsed.ToString()); + Assert.AreEqual("[20050000,20050000,-1.1234E-100,9.05E+28]", parsed.ToString()); json = "[-]"; Assert.ThrowsException(() => JObject.Parse(json)); @@ -182,7 +193,8 @@ public void JsonTest_Object() [TestMethod] public void Deserialize_WrongJson() { - Assert.ThrowsException(() => JsonSerializer.Deserialize(JObject.Parse("x"), ExecutionEngineLimits.Default)); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null); + Assert.ThrowsException(() => JsonSerializer.Deserialize(engine, JObject.Parse("x"), ExecutionEngineLimits.Default)); } [TestMethod] @@ -216,7 +228,8 @@ public void Serialize_Null() [TestMethod] public void Deserialize_EmptyObject() { - var items = JsonSerializer.Deserialize(JObject.Parse("{}"), ExecutionEngineLimits.Default); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null); + var items = JsonSerializer.Deserialize(engine, JObject.Parse("{}"), ExecutionEngineLimits.Default); Assert.IsInstanceOfType(items, typeof(Map)); Assert.AreEqual(((Map)items).Count, 0); @@ -234,7 +247,8 @@ public void Serialize_EmptyArray() [TestMethod] public void Deserialize_EmptyArray() { - var items = JsonSerializer.Deserialize(JObject.Parse("[]"), ExecutionEngineLimits.Default); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null); + var items = JsonSerializer.Deserialize(engine, JObject.Parse("[]"), ExecutionEngineLimits.Default); Assert.IsInstanceOfType(items, typeof(VM.Types.Array)); Assert.AreEqual(((VM.Types.Array)items).Count, 0); @@ -258,7 +272,8 @@ public void Serialize_Map_Test() [TestMethod] public void Deserialize_Map_Test() { - var items = JsonSerializer.Deserialize(JObject.Parse("{\"test1\":123,\"test2\":321}"), ExecutionEngineLimits.Default); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null, null, ProtocolSettings.Default); + var items = JsonSerializer.Deserialize(engine, JObject.Parse("{\"test1\":123,\"test2\":321}"), ExecutionEngineLimits.Default); Assert.IsInstanceOfType(items, typeof(Map)); Assert.AreEqual(((Map)items).Count, 2); @@ -287,16 +302,18 @@ public void Serialize_Array_Bool_Str_Num() [TestMethod] public void Deserialize_Array_Bool_Str_Num() { - var items = JsonSerializer.Deserialize(JObject.Parse("[true,\"test\",123]"), ExecutionEngineLimits.Default); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null, null, ProtocolSettings.Default); + var items = JsonSerializer.Deserialize(engine, JObject.Parse("[true,\"test\",123,9.05E+28]"), ExecutionEngineLimits.Default); Assert.IsInstanceOfType(items, typeof(VM.Types.Array)); - Assert.AreEqual(((VM.Types.Array)items).Count, 3); + Assert.AreEqual(((VM.Types.Array)items).Count, 4); var array = (VM.Types.Array)items; Assert.IsTrue(array[0].GetBoolean()); Assert.AreEqual(array[1].GetString(), "test"); Assert.AreEqual(array[2].GetInteger(), 123); + Assert.AreEqual(array[3].GetInteger(), BigInteger.Parse("90500000000000000000000000000")); } [TestMethod] @@ -316,7 +333,8 @@ public void Serialize_Array_OfArray() [TestMethod] public void Deserialize_Array_OfArray() { - var items = JsonSerializer.Deserialize(JObject.Parse("[[true,\"test1\",123],[true,\"test2\",321]]"), ExecutionEngineLimits.Default); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null, null, ProtocolSettings.Default); + var items = JsonSerializer.Deserialize(engine, JObject.Parse("[[true,\"test1\",123],[true,\"test2\",321]]"), ExecutionEngineLimits.Default); Assert.IsInstanceOfType(items, typeof(VM.Types.Array)); Assert.AreEqual(((VM.Types.Array)items).Count, 2); diff --git a/tests/neo.UnitTests/SmartContract/UT_KeyBuilder.cs b/tests/Neo.UnitTests/SmartContract/UT_KeyBuilder.cs similarity index 62% rename from tests/neo.UnitTests/SmartContract/UT_KeyBuilder.cs rename to tests/Neo.UnitTests/SmartContract/UT_KeyBuilder.cs index 39c2018f25..3ff52c1324 100644 --- a/tests/neo.UnitTests/SmartContract/UT_KeyBuilder.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_KeyBuilder.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_KeyBuilder.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.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; @@ -6,11 +17,6 @@ namespace Neo.UnitTests.SmartContract [TestClass] public class UT_KeyBuilder { - private struct a - { - public int x; - } - [TestMethod] public void Test() { @@ -28,11 +34,11 @@ public void Test() Assert.AreEqual("010000000203040000000000000000000000000000000000000000", key.ToArray().ToHexString()); key = new KeyBuilder(1, 2); - key = key.Add(new a() { x = 123 }); - Assert.AreEqual("01000000027b000000", key.ToArray().ToHexString()); + key = key.AddBigEndian(123); + Assert.AreEqual("01000000020000007b", key.ToArray().ToHexString()); key = new KeyBuilder(1, 0); - key = key.AddBigEndian(new a() { x = 1 }); + key = key.AddBigEndian(1); Assert.AreEqual("010000000000000001", key.ToArray().ToHexString()); } } diff --git a/tests/neo.UnitTests/SmartContract/UT_LogEventArgs.cs b/tests/Neo.UnitTests/SmartContract/UT_LogEventArgs.cs similarity index 63% rename from tests/neo.UnitTests/SmartContract/UT_LogEventArgs.cs rename to tests/Neo.UnitTests/SmartContract/UT_LogEventArgs.cs index 8dc3699c82..4e587a2246 100644 --- a/tests/neo.UnitTests/SmartContract/UT_LogEventArgs.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_LogEventArgs.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_LogEventArgs.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.VisualStudio.TestTools.UnitTesting; using Neo.Network.P2P.Payloads; using Neo.SmartContract; diff --git a/tests/neo.UnitTests/SmartContract/UT_MethodToken.cs b/tests/Neo.UnitTests/SmartContract/UT_MethodToken.cs similarity index 80% rename from tests/neo.UnitTests/SmartContract/UT_MethodToken.cs rename to tests/Neo.UnitTests/SmartContract/UT_MethodToken.cs index 8d530eefd9..9d687e1053 100644 --- a/tests/neo.UnitTests/SmartContract/UT_MethodToken.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_MethodToken.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_MethodToken.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.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.SmartContract; diff --git a/tests/neo.UnitTests/SmartContract/UT_NefFile.cs b/tests/Neo.UnitTests/SmartContract/UT_NefFile.cs similarity index 75% rename from tests/neo.UnitTests/SmartContract/UT_NefFile.cs rename to tests/Neo.UnitTests/SmartContract/UT_NefFile.cs index 877d869667..40142e93be 100644 --- a/tests/neo.UnitTests/SmartContract/UT_NefFile.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_NefFile.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_NefFile.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; @@ -30,40 +41,46 @@ public void TestDeserialize() byte[] wrongMagic = { 0x00, 0x00, 0x00, 0x00 }; using (MemoryStream ms = new(1024)) using (BinaryWriter writer = new(ms)) - using (BinaryReader reader = new(ms)) { ((ISerializable)file).Serialize(writer); ms.Seek(0, SeekOrigin.Begin); ms.Write(wrongMagic, 0, 4); - ms.Seek(0, SeekOrigin.Begin); ISerializable newFile = new NefFile(); - Action action = () => newFile.Deserialize(reader); - action.Should().Throw(); + Assert.ThrowsException(() => + { + MemoryReader reader = new(ms.ToArray()); + newFile.Deserialize(ref reader); + Assert.Fail(); + }); } file.CheckSum = 0; using (MemoryStream ms = new(1024)) using (BinaryWriter writer = new(ms)) - using (BinaryReader reader = new(ms)) { ((ISerializable)file).Serialize(writer); - ms.Seek(0, SeekOrigin.Begin); ISerializable newFile = new NefFile(); - Action action = () => newFile.Deserialize(reader); - action.Should().Throw(); + Assert.ThrowsException(() => + { + MemoryReader reader = new(ms.ToArray()); + newFile.Deserialize(ref reader); + Assert.Fail(); + }); } file.Script = Array.Empty(); file.CheckSum = NefFile.ComputeChecksum(file); using (MemoryStream ms = new(1024)) using (BinaryWriter writer = new(ms)) - using (BinaryReader reader = new(ms)) { ((ISerializable)file).Serialize(writer); - ms.Seek(0, SeekOrigin.Begin); ISerializable newFile = new NefFile(); - Action action = () => newFile.Deserialize(reader); - action.Should().Throw(); + Assert.ThrowsException(() => + { + MemoryReader reader = new(ms.ToArray()); + newFile.Deserialize(ref reader); + Assert.Fail(); + }); } file.Script = new byte[] { 0x01, 0x02, 0x03 }; @@ -72,7 +89,7 @@ public void TestDeserialize() var newFile1 = data.AsSerializable(); newFile1.Compiler.Should().Be(file.Compiler); newFile1.CheckSum.Should().Be(file.CheckSum); - newFile1.Script.Should().BeEquivalentTo(file.Script); + newFile1.Script.Span.SequenceEqual(file.Script.Span).Should().BeTrue(); } [TestMethod] @@ -98,7 +115,7 @@ public void ParseTest() file = data.AsSerializable(); Assert.AreEqual("".PadLeft(32, ' '), file.Compiler); - CollectionAssert.AreEqual(new byte[] { 0x01, 0x02, 0x03 }, file.Script); + CollectionAssert.AreEqual(new byte[] { 0x01, 0x02, 0x03 }, file.Script.ToArray()); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs b/tests/Neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs similarity index 58% rename from tests/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs rename to tests/Neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs index e83f8fd7fc..fdaeb9106e 100644 --- a/tests/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_NotifyEventArgs.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Network.P2P.Payloads; diff --git a/tests/Neo.UnitTests/SmartContract/UT_OpCodePrices.cs b/tests/Neo.UnitTests/SmartContract/UT_OpCodePrices.cs new file mode 100644 index 0000000000..c373930c6e --- /dev/null +++ b/tests/Neo.UnitTests/SmartContract/UT_OpCodePrices.cs @@ -0,0 +1,42 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_OpCodePrices.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.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract; +using Neo.VM; +using System; + +namespace Neo.UnitTests.SmartContract +{ + [TestClass] + public class UT_OpCodePrices + { + [TestMethod] + public void AllOpcodePriceAreSet() + { + foreach (OpCode opcode in Enum.GetValues(typeof(OpCode))) + { +#pragma warning disable CS0618 // Type or member is obsolete + Assert.IsTrue(ApplicationEngine.OpCodePrices.ContainsKey(opcode), opcode.ToString(), $"{opcode} without price"); + Assert.AreEqual(ApplicationEngine.OpCodePrices[opcode], ApplicationEngine.OpCodePriceTable[(byte)opcode], $"{opcode} price mismatch"); +#pragma warning restore CS0618 // Type or member is obsolete + + if (opcode == OpCode.RET || + opcode == OpCode.SYSCALL || + opcode == OpCode.ABORT || + opcode == OpCode.ABORTMSG) + continue; + + Assert.AreNotEqual(0, ApplicationEngine.OpCodePriceTable[(byte)opcode], $"{opcode} without price"); + } + } + } +} diff --git a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/tests/Neo.UnitTests/SmartContract/UT_SmartContractHelper.cs similarity index 93% rename from tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs rename to tests/Neo.UnitTests/SmartContract/UT_SmartContractHelper.cs index 773c3ed4e1..c14b15c241 100644 --- a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_SmartContractHelper.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.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.Network.P2P.Payloads; @@ -131,7 +142,7 @@ public void TestVerifyWitnesses() Hashes = new UInt256[1] { UInt256.Zero }, }); BlocksDelete(snapshot1, index1); - Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(new Header() { PrevHash = index1 }, ProtocolSettings.Default, snapshot1, 100)); + Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(new Header() { PrevHash = index1 }, TestProtocolSettings.Default, snapshot1, 100)); var snapshot2 = TestBlockchain.GetTestSnapshot(); UInt256 index2 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); @@ -152,7 +163,7 @@ public void TestVerifyWitnesses() snapshot2.AddContract(UInt160.Zero, new ContractState()); snapshot2.DeleteContract(UInt160.Zero); - Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(header2, ProtocolSettings.Default, snapshot2, 100)); + Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(header2, TestProtocolSettings.Default, snapshot2, 100)); var snapshot3 = TestBlockchain.GetTestSnapshot(); UInt256 index3 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); @@ -184,7 +195,7 @@ public void TestVerifyWitnesses() Hash = Array.Empty().ToScriptHash(), Manifest = TestUtils.CreateManifest("verify", ContractParameterType.Boolean, ContractParameterType.Signature), }); - Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(header3, ProtocolSettings.Default, snapshot3, 100)); + Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(header3, TestProtocolSettings.Default, snapshot3, 100)); // Smart contract verification @@ -200,7 +211,7 @@ public void TestVerifyWitnesses() Witnesses = new Witness[] { new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() } } }; - Assert.AreEqual(true, Neo.SmartContract.Helper.VerifyWitnesses(tx, ProtocolSettings.Default, snapshot3, 1000)); + Assert.AreEqual(true, Neo.SmartContract.Helper.VerifyWitnesses(tx, TestProtocolSettings.Default, snapshot3, 1000)); } private static void BlocksDelete(DataCache snapshot, UInt256 hash) diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/Neo.UnitTests/SmartContract/UT_Syscalls.cs similarity index 84% rename from tests/neo.UnitTests/SmartContract/UT_Syscalls.cs rename to tests/Neo.UnitTests/SmartContract/UT_Syscalls.cs index 89df09da26..0a98b5fd66 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -1,5 +1,17 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Syscalls.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 Akka.TestKit.Xunit2; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.ECC; using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.SmartContract; @@ -70,7 +82,7 @@ public void System_Blockchain_GetBlock() const byte Prefix_CurrentBlock = 12; var height = snapshot[NativeContract.Ledger.CreateStorageKey(Prefix_CurrentBlock)].GetInteroperable(); - height.Index = block.Index + ProtocolSettings.Default.MaxTraceableBlocks; + height.Index = block.Index + TestProtocolSettings.Default.MaxTraceableBlocks; UT_SmartContractHelper.BlocksAdd(snapshot, block.Hash, block); snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_Transaction, tx.Hash), new StorageItem(new TransactionState @@ -88,6 +100,7 @@ public void System_Blockchain_GetBlock() // With block + height = snapshot[NativeContract.Ledger.CreateStorageKey(Prefix_CurrentBlock)].GetInteroperable(); height.Index = block.Index; engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); @@ -120,7 +133,16 @@ public void System_ExecutionEngine_GetScriptContainer() var tx = new Transaction() { Script = new byte[] { 0x01 }, - Signers = new Signer[] { new Signer() { Account = UInt160.Zero, Scopes = WitnessScope.None } }, + Signers = new Signer[] { + new Signer() + { + Account = UInt160.Zero, + Scopes = WitnessScope.None, + AllowedContracts = Array.Empty(), + AllowedGroups = Array.Empty(), + Rules = Array.Empty(), + } + }, Attributes = Array.Empty(), NetworkFee = 0x02, SystemFee = 0x03, @@ -203,12 +225,12 @@ public void System_Runtime_GetInvocationCounter() { script.EmitSysCall(ApplicationEngine.System_Runtime_GetInvocationCounter); - contractA = new ContractState() { Nef = new NefFile { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP }.Concat(script.ToArray()).ToArray() } }; - contractB = new ContractState() { Nef = new NefFile { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() } }; - contractC = new ContractState() { Nef = new NefFile { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() } }; - contractA.Hash = contractA.Script.ToScriptHash(); - contractB.Hash = contractB.Script.ToScriptHash(); - contractC.Hash = contractC.Script.ToScriptHash(); + contractA = TestUtils.GetContract(new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP }.Concat(script.ToArray()).ToArray()); + contractB = TestUtils.GetContract(new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray()); + contractC = TestUtils.GetContract(new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray()); + contractA.Hash = contractA.Script.Span.ToScriptHash(); + contractB.Hash = contractB.Script.Span.ToScriptHash(); + contractC.Hash = contractC.Script.Span.ToScriptHash(); // Init A,B,C contracts // First two drops is for drop method and arguments diff --git a/tests/Neo.UnitTests/TestBlockchain.cs b/tests/Neo.UnitTests/TestBlockchain.cs new file mode 100644 index 0000000000..f7e06d0595 --- /dev/null +++ b/tests/Neo.UnitTests/TestBlockchain.cs @@ -0,0 +1,49 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TestBlockchain.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 Akka.Actor; +using Neo.Ledger; +using Neo.Persistence; +using System; + +namespace Neo.UnitTests +{ + public static class TestBlockchain + { + public static readonly NeoSystem TheNeoSystem; + public static readonly UInt160[] DefaultExtensibleWitnessWhiteList; + private static readonly MemoryStore Store = new(); + + private class StoreProvider : IStoreProvider + { + public string Name => "TestProvider"; + + public IStore GetStore(string path) => Store; + } + + static TestBlockchain() + { + Console.WriteLine("initialize NeoSystem"); + TheNeoSystem = new NeoSystem(TestProtocolSettings.Default, new StoreProvider()); + } + + internal static void ResetStore() + { + Store.Reset(); + TheNeoSystem.Blockchain.Ask(new Blockchain.Initialize()).Wait(); + } + + internal static DataCache GetTestSnapshot() + { + return TheNeoSystem.GetSnapshot().CreateSnapshot(); + } + } +} diff --git a/tests/Neo.UnitTests/TestProtocolSettings.cs b/tests/Neo.UnitTests/TestProtocolSettings.cs new file mode 100644 index 0000000000..b12f5c9a85 --- /dev/null +++ b/tests/Neo.UnitTests/TestProtocolSettings.cs @@ -0,0 +1,65 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TestProtocolSettings.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.Cryptography.ECC; + +namespace Neo.UnitTests +{ + public static class TestProtocolSettings + { + public static ProtocolSettings Default = new() + { + Network = 0x334F454Eu, + AddressVersion = ProtocolSettings.Default.AddressVersion, + StandbyCommittee = new[] + { + //Validators + ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", ECCurve.Secp256r1), + ECPoint.Parse("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", ECCurve.Secp256r1), + ECPoint.Parse("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", ECCurve.Secp256r1), + ECPoint.Parse("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", ECCurve.Secp256r1), + ECPoint.Parse("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", ECCurve.Secp256r1), + ECPoint.Parse("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", ECCurve.Secp256r1), + ECPoint.Parse("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", ECCurve.Secp256r1), + //Other Members + ECPoint.Parse("023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe", ECCurve.Secp256r1), + ECPoint.Parse("03708b860c1de5d87f5b151a12c2a99feebd2e8b315ee8e7cf8aa19692a9e18379", ECCurve.Secp256r1), + ECPoint.Parse("03c6aa6e12638b36e88adc1ccdceac4db9929575c3e03576c617c49cce7114a050", ECCurve.Secp256r1), + ECPoint.Parse("03204223f8c86b8cd5c89ef12e4f0dbb314172e9241e30c9ef2293790793537cf0", ECCurve.Secp256r1), + ECPoint.Parse("02a62c915cf19c7f19a50ec217e79fac2439bbaad658493de0c7d8ffa92ab0aa62", ECCurve.Secp256r1), + ECPoint.Parse("03409f31f0d66bdc2f70a9730b66fe186658f84a8018204db01c106edc36553cd0", ECCurve.Secp256r1), + ECPoint.Parse("0288342b141c30dc8ffcde0204929bb46aed5756b41ef4a56778d15ada8f0c6654", ECCurve.Secp256r1), + ECPoint.Parse("020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639", ECCurve.Secp256r1), + ECPoint.Parse("0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30", ECCurve.Secp256r1), + ECPoint.Parse("03d281b42002647f0113f36c7b8efb30db66078dfaaa9ab3ff76d043a98d512fde", ECCurve.Secp256r1), + ECPoint.Parse("02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad", ECCurve.Secp256r1), + ECPoint.Parse("0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d", ECCurve.Secp256r1), + ECPoint.Parse("03cdcea66032b82f5c30450e381e5295cae85c5e6943af716cc6b646352a6067dc", ECCurve.Secp256r1), + ECPoint.Parse("02cd5a5547119e24feaa7c2a0f37b8c9366216bab7054de0065c9be42084003c8a", ECCurve.Secp256r1) + }, + ValidatorsCount = 7, + SeedList = new[] + { + "seed1.neo.org:10333", + "seed2.neo.org:10333", + "seed3.neo.org:10333", + "seed4.neo.org:10333", + "seed5.neo.org:10333" + }, + MillisecondsPerBlock = ProtocolSettings.Default.MillisecondsPerBlock, + MaxTransactionsPerBlock = ProtocolSettings.Default.MaxTransactionsPerBlock, + MemoryPoolMaxTransactions = ProtocolSettings.Default.MemoryPoolMaxTransactions, + MaxTraceableBlocks = ProtocolSettings.Default.MaxTraceableBlocks, + InitialGasDistribution = ProtocolSettings.Default.InitialGasDistribution, + Hardforks = ProtocolSettings.Default.Hardforks + }; + } +} diff --git a/tests/neo.UnitTests/TestUtils.cs b/tests/Neo.UnitTests/TestUtils.cs similarity index 80% rename from tests/neo.UnitTests/TestUtils.cs rename to tests/Neo.UnitTests/TestUtils.cs index c6f5ad36db..f8f63de53a 100644 --- a/tests/neo.UnitTests/TestUtils.cs +++ b/tests/Neo.UnitTests/TestUtils.cs @@ -1,7 +1,19 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TestUtils.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 FluentAssertions; using Neo.Cryptography; +using Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.SmartContract.Manifest; @@ -9,7 +21,6 @@ using Neo.VM; using Neo.Wallets.NEP6; using System; -using System.IO; using System.Linq; namespace Neo.UnitTests @@ -88,7 +99,7 @@ public static byte[] GetByteArray(int length, byte firstByte) return array; } - public static NEP6Wallet GenerateTestWallet() + public static NEP6Wallet GenerateTestWallet(string password) { JObject wallet = new JObject(); wallet["name"] = "noname"; @@ -97,7 +108,7 @@ public static NEP6Wallet GenerateTestWallet() wallet["accounts"] = new JArray(); wallet["extra"] = null; wallet.ToString().Should().Be("{\"name\":\"noname\",\"version\":\"1.0\",\"scrypt\":{\"n\":2,\"r\":1,\"p\":1},\"accounts\":[],\"extra\":null}"); - return new NEP6Wallet(null, ProtocolSettings.Default, wallet); + return new NEP6Wallet(null, password, TestProtocolSettings.Default, wallet); } public static Transaction GetTransaction(UInt160 sender) @@ -109,7 +120,10 @@ public static Transaction GetTransaction(UInt160 sender) Signers = new[]{ new Signer() { Account = sender, - Scopes = WitnessScope.CalledByEntry + Scopes = WitnessScope.CalledByEntry, + AllowedContracts = Array.Empty(), + AllowedGroups = Array.Empty(), + Rules = Array.Empty(), } }, Witnesses = new Witness[]{ new Witness { @@ -121,22 +135,39 @@ public static Transaction GetTransaction(UInt160 sender) internal static ContractState GetContract(string method = "test", int parametersCount = 0) { + NefFile nef = new() + { + Compiler = "", + Source = "", + Tokens = Array.Empty(), + Script = new byte[] { 0x01, 0x01, 0x01, 0x01 } + }; + nef.CheckSum = NefFile.ComputeChecksum(nef); return new ContractState { Id = 0x43000000, - Nef = new NefFile { Script = new byte[] { 0x01, 0x01, 0x01, 0x01 } }, - Hash = new byte[] { 0x01, 0x01, 0x01, 0x01 }.ToScriptHash(), + Nef = nef, + Hash = nef.Script.Span.ToScriptHash(), Manifest = CreateManifest(method, ContractParameterType.Any, Enumerable.Repeat(ContractParameterType.Any, parametersCount).ToArray()) }; } - internal static ContractState GetContract(byte[] script) + internal static ContractState GetContract(byte[] script, ContractManifest manifest = null) { + NefFile nef = new() + { + Compiler = "", + Source = "", + Tokens = Array.Empty(), + Script = script + }; + nef.CheckSum = NefFile.ComputeChecksum(nef); return new ContractState { Id = 1, - Nef = new NefFile { Script = script }, - Manifest = CreateDefaultManifest() + Hash = script.ToScriptHash(), + Nef = nef, + Manifest = manifest ?? CreateDefaultManifest() }; } @@ -225,13 +256,14 @@ public static Transaction CreateRandomHashTransaction() public static T CopyMsgBySerialization(T serializableObj, T newObj) where T : ISerializable { - using (MemoryStream ms = new MemoryStream(serializableObj.ToArray(), false)) - using (BinaryReader reader = new BinaryReader(ms)) - { - newObj.Deserialize(reader); - } - + MemoryReader reader = new(serializableObj.ToArray()); + newObj.Deserialize(ref reader); return newObj; } + + public static bool EqualsTo(this StorageItem item, StorageItem other) + { + return item.Value.Span.SequenceEqual(other.Value.Span); + } } } diff --git a/tests/neo.UnitTests/TestVerifiable.cs b/tests/Neo.UnitTests/TestVerifiable.cs similarity index 61% rename from tests/neo.UnitTests/TestVerifiable.cs rename to tests/Neo.UnitTests/TestVerifiable.cs index 5f98f07f2a..12dc7992f6 100644 --- a/tests/neo.UnitTests/TestVerifiable.cs +++ b/tests/Neo.UnitTests/TestVerifiable.cs @@ -1,3 +1,15 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TestVerifiable.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.IO; using Neo.Network.P2P.Payloads; using Neo.Persistence; using System; @@ -17,12 +29,12 @@ public Witness[] Witnesses public int Size => throw new NotImplementedException(); - public void Deserialize(BinaryReader reader) + public void Deserialize(ref MemoryReader reader) { throw new NotImplementedException(); } - public void DeserializeUnsigned(BinaryReader reader) + public void DeserializeUnsigned(ref MemoryReader reader) { throw new NotImplementedException(); } @@ -39,7 +51,7 @@ public void Serialize(BinaryWriter writer) public void SerializeUnsigned(BinaryWriter writer) { - writer.Write((string)testStr); + writer.Write(testStr); } } } diff --git a/tests/neo.UnitTests/TestWalletAccount.cs b/tests/Neo.UnitTests/TestWalletAccount.cs similarity index 64% rename from tests/neo.UnitTests/TestWalletAccount.cs rename to tests/Neo.UnitTests/TestWalletAccount.cs index bc6d04825b..08686cf70b 100644 --- a/tests/neo.UnitTests/TestWalletAccount.cs +++ b/tests/Neo.UnitTests/TestWalletAccount.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TestWalletAccount.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 Moq; using Neo.SmartContract; using Neo.Wallets; @@ -13,7 +24,7 @@ class TestWalletAccount : WalletAccount public override KeyPair GetKey() => key; public TestWalletAccount(UInt160 hash) - : base(hash, ProtocolSettings.Default) + : base(hash, TestProtocolSettings.Default) { var mock = new Mock(); mock.SetupGet(p => p.ScriptHash).Returns(hash); diff --git a/tests/neo.UnitTests/UT_BigDecimal.cs b/tests/Neo.UnitTests/UT_BigDecimal.cs similarity index 95% rename from tests/neo.UnitTests/UT_BigDecimal.cs rename to tests/Neo.UnitTests/UT_BigDecimal.cs index 9dc21f5b34..9e3c50a9db 100644 --- a/tests/neo.UnitTests/UT_BigDecimal.cs +++ b/tests/Neo.UnitTests/UT_BigDecimal.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_BigDecimal.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; diff --git a/tests/neo.UnitTests/UT_DataCache.cs b/tests/Neo.UnitTests/UT_DataCache.cs similarity index 84% rename from tests/neo.UnitTests/UT_DataCache.cs rename to tests/Neo.UnitTests/UT_DataCache.cs index 951c2275d1..008f166b6f 100644 --- a/tests/neo.UnitTests/UT_DataCache.cs +++ b/tests/Neo.UnitTests/UT_DataCache.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_DataCache.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.VisualStudio.TestTools.UnitTesting; using Neo.Persistence; using Neo.SmartContract; @@ -42,7 +53,7 @@ public void TestCachedFind_Between() ); CollectionAssert.AreEqual( - cache.Find(new byte[5]).Select(u => u.Key.Key[1]).ToArray(), + cache.Find(new byte[5]).Select(u => u.Key.Key.Span[1]).ToArray(), new byte[] { 0x01, 0x02, 0x03 } ); } @@ -74,7 +85,7 @@ public void TestCachedFind_Last() new StorageKey() { Key = new byte[] { 0x01, 0x02 }, Id = 0 }, new StorageItem() { Value = new byte[] { } } ); - CollectionAssert.AreEqual(cache.Find(new byte[5]).Select(u => u.Key.Key[1]).ToArray(), + CollectionAssert.AreEqual(cache.Find(new byte[5]).Select(u => u.Key.Key.Span[1]).ToArray(), new byte[] { 0x01, 0x02 } ); } @@ -98,7 +109,7 @@ public void TestCachedFind_Empty() ); CollectionAssert.AreEqual( - cache.Find(new byte[5]).Select(u => u.Key.Key[1]).ToArray(), + cache.Find(new byte[5]).Select(u => u.Key.Key.Span[1]).ToArray(), new byte[] { 0x02 } ); } diff --git a/tests/neo.UnitTests/UT_Helper.cs b/tests/Neo.UnitTests/UT_Helper.cs similarity index 91% rename from tests/neo.UnitTests/UT_Helper.cs rename to tests/Neo.UnitTests/UT_Helper.cs index fda1e40ace..b7dc4f1681 100644 --- a/tests/neo.UnitTests/UT_Helper.cs +++ b/tests/Neo.UnitTests/UT_Helper.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Caching; @@ -19,7 +30,7 @@ public class UT_Helper public void GetSignData() { TestVerifiable verifiable = new(); - byte[] res = verifiable.GetSignData(ProtocolSettings.Default.Network); + byte[] res = verifiable.GetSignData(TestProtocolSettings.Default.Network); res.ToHexString().Should().Be("4e454f3350b51da6bb366be3ea50140cda45ba7df575287c0371000b2037ed3898ff8bf5"); } @@ -27,7 +38,7 @@ public void GetSignData() public void Sign() { TestVerifiable verifiable = new(); - byte[] res = verifiable.Sign(new KeyPair(TestUtils.GetByteArray(32, 0x42)), ProtocolSettings.Default.Network); + byte[] res = verifiable.Sign(new KeyPair(TestUtils.GetByteArray(32, 0x42)), TestProtocolSettings.Default.Network); res.Length.Should().Be(64); } diff --git a/tests/neo.UnitTests/UT_NeoSystem.cs b/tests/Neo.UnitTests/UT_NeoSystem.cs similarity index 60% rename from tests/neo.UnitTests/UT_NeoSystem.cs rename to tests/Neo.UnitTests/UT_NeoSystem.cs index 646d214f6c..a88498b504 100644 --- a/tests/neo.UnitTests/UT_NeoSystem.cs +++ b/tests/Neo.UnitTests/UT_NeoSystem.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_NeoSystem.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/tests/Neo.UnitTests/UT_ProtocolSettings.cs b/tests/Neo.UnitTests/UT_ProtocolSettings.cs new file mode 100644 index 0000000000..e5d829ef8b --- /dev/null +++ b/tests/Neo.UnitTests/UT_ProtocolSettings.cs @@ -0,0 +1,338 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ProtocolSettings.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 FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.ECC; +using Neo.Wallets; +using System; +using System.IO; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_ProtocolSettings + { + [TestMethod] + public void CheckFirstLetterOfAddresses() + { + UInt160 min = UInt160.Parse("0x0000000000000000000000000000000000000000"); + min.ToAddress(TestProtocolSettings.Default.AddressVersion)[0].Should().Be('N'); + UInt160 max = UInt160.Parse("0xffffffffffffffffffffffffffffffffffffffff"); + max.ToAddress(TestProtocolSettings.Default.AddressVersion)[0].Should().Be('N'); + } + + [TestMethod] + public void Default_Network_should_be_mainnet_Network_value() + { + var mainNetNetwork = 0x334F454Eu; + TestProtocolSettings.Default.Network.Should().Be(mainNetNetwork); + } + + [TestMethod] + public void TestGetMemoryPoolMaxTransactions() + { + TestProtocolSettings.Default.MemoryPoolMaxTransactions.Should().Be(50000); + } + + [TestMethod] + public void TestGetMillisecondsPerBlock() + { + TestProtocolSettings.Default.MillisecondsPerBlock.Should().Be(15000); + } + + [TestMethod] + public void HardForkTestBAndNotA() + { + string json = CreateHKSettings("\"HF_Basilisk\": 4120000"); + + var file = Path.GetTempFileName(); + File.WriteAllText(file, json); + ProtocolSettings settings = ProtocolSettings.Load(file, false); + File.Delete(file); + + settings.Hardforks[Hardfork.HF_Aspidochelone].Should().Be(0); + settings.Hardforks[Hardfork.HF_Basilisk].Should().Be(4120000); + + // Check IsHardforkEnabled + + settings.IsHardforkEnabled(Hardfork.HF_Aspidochelone, 0).Should().BeTrue(); + settings.IsHardforkEnabled(Hardfork.HF_Aspidochelone, 10).Should().BeTrue(); + settings.IsHardforkEnabled(Hardfork.HF_Basilisk, 0).Should().BeFalse(); + settings.IsHardforkEnabled(Hardfork.HF_Basilisk, 10).Should().BeFalse(); + settings.IsHardforkEnabled(Hardfork.HF_Basilisk, 4120000).Should().BeTrue(); + } + + [TestMethod] + public void HardForkTestAAndNotB() + { + string json = CreateHKSettings("\"HF_Aspidochelone\": 0"); + + var file = Path.GetTempFileName(); + File.WriteAllText(file, json); + ProtocolSettings settings = ProtocolSettings.Load(file, false); + File.Delete(file); + + settings.Hardforks[Hardfork.HF_Aspidochelone].Should().Be(0); + settings.Hardforks.ContainsKey(Hardfork.HF_Basilisk).Should().BeFalse(); + + // Check IsHardforkEnabled + + settings.IsHardforkEnabled(Hardfork.HF_Aspidochelone, 0).Should().BeTrue(); + settings.IsHardforkEnabled(Hardfork.HF_Aspidochelone, 10).Should().BeTrue(); + settings.IsHardforkEnabled(Hardfork.HF_Basilisk, 0).Should().BeFalse(); + settings.IsHardforkEnabled(Hardfork.HF_Basilisk, 10).Should().BeFalse(); + settings.IsHardforkEnabled(Hardfork.HF_Basilisk, 4120000).Should().BeFalse(); + } + + [TestMethod] + public void HardForkTestNone() + { + string json = CreateHKSettings(""); + + var file = Path.GetTempFileName(); + File.WriteAllText(file, json); + ProtocolSettings settings = ProtocolSettings.Load(file, false); + File.Delete(file); + + settings.Hardforks[Hardfork.HF_Aspidochelone].Should().Be(0); + settings.Hardforks[Hardfork.HF_Basilisk].Should().Be(0); + + // Check IsHardforkEnabled + + settings.IsHardforkEnabled(Hardfork.HF_Aspidochelone, 0).Should().BeTrue(); + settings.IsHardforkEnabled(Hardfork.HF_Aspidochelone, 10).Should().BeTrue(); + settings.IsHardforkEnabled(Hardfork.HF_Basilisk, 0).Should().BeTrue(); + settings.IsHardforkEnabled(Hardfork.HF_Basilisk, 10).Should().BeTrue(); + } + + [TestMethod] + public void HardForkTestAMoreThanB() + { + string json = CreateHKSettings("\"HF_Aspidochelone\": 4120001, \"HF_Basilisk\": 4120000"); + var file = Path.GetTempFileName(); + File.WriteAllText(file, json); + Assert.ThrowsException(() => ProtocolSettings.Load(file, false)); + File.Delete(file); + } + + internal static string CreateHKSettings(string hf) + { + return @" +{ + ""ProtocolConfiguration"": { + ""Network"": 860833102, + ""AddressVersion"": 53, + ""MillisecondsPerBlock"": 15000, + ""MaxTransactionsPerBlock"": 512, + ""MemoryPoolMaxTransactions"": 50000, + ""MaxTraceableBlocks"": 2102400, + ""Hardforks"": { + " + hf + @" + }, + ""InitialGasDistribution"": 5200000000000000, + ""ValidatorsCount"": 7, + ""StandbyCommittee"": [ + ""03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"", + ""02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"", + ""03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"", + ""02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"", + ""024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"", + ""02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"", + ""02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"", + ""023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe"", + ""03708b860c1de5d87f5b151a12c2a99feebd2e8b315ee8e7cf8aa19692a9e18379"", + ""03c6aa6e12638b36e88adc1ccdceac4db9929575c3e03576c617c49cce7114a050"", + ""03204223f8c86b8cd5c89ef12e4f0dbb314172e9241e30c9ef2293790793537cf0"", + ""02a62c915cf19c7f19a50ec217e79fac2439bbaad658493de0c7d8ffa92ab0aa62"", + ""03409f31f0d66bdc2f70a9730b66fe186658f84a8018204db01c106edc36553cd0"", + ""0288342b141c30dc8ffcde0204929bb46aed5756b41ef4a56778d15ada8f0c6654"", + ""020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639"", + ""0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30"", + ""03d281b42002647f0113f36c7b8efb30db66078dfaaa9ab3ff76d043a98d512fde"", + ""02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad"", + ""0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d"", + ""03cdcea66032b82f5c30450e381e5295cae85c5e6943af716cc6b646352a6067dc"", + ""02cd5a5547119e24feaa7c2a0f37b8c9366216bab7054de0065c9be42084003c8a"" + ], + ""SeedList"": [ + ""seed1.neo.org:10333"", + ""seed2.neo.org:10333"", + ""seed3.neo.org:10333"", + ""seed4.neo.org:10333"", + ""seed5.neo.org:10333"" + ] + } +} +"; + } + + [TestMethod] + public void TestGetSeedList() + { + TestProtocolSettings.Default.SeedList.Should().BeEquivalentTo(new string[] { "seed1.neo.org:10333", "seed2.neo.org:10333", "seed3.neo.org:10333", "seed4.neo.org:10333", "seed5.neo.org:10333", }); + } + + [TestMethod] + public void TestStandbyCommitteeAddressesFormat() + { + foreach (var point in TestProtocolSettings.Default.StandbyCommittee) + { + point.ToString().Should().MatchRegex("^[0-9A-Fa-f]{66}$"); // ECPoint is 66 hex characters + } + } + + [TestMethod] + public void TestValidatorsCount() + { + TestProtocolSettings.Default.StandbyCommittee.Count.Should().Be(TestProtocolSettings.Default.ValidatorsCount * 3); + } + + [TestMethod] + public void TestMaxTransactionsPerBlock() + { + TestProtocolSettings.Default.MaxTransactionsPerBlock.Should().BePositive().And.BeLessOrEqualTo(50000); // Assuming 50000 as a reasonable upper limit + } + + [TestMethod] + public void TestMaxTraceableBlocks() + { + TestProtocolSettings.Default.MaxTraceableBlocks.Should().BePositive(); + } + + [TestMethod] + public void TestInitialGasDistribution() + { + TestProtocolSettings.Default.InitialGasDistribution.Should().BeGreaterThan(0); + } + + [TestMethod] + public void TestHardforksSettings() + { + TestProtocolSettings.Default.Hardforks.Should().NotBeNull(); + } + + [TestMethod] + public void TestAddressVersion() + { + TestProtocolSettings.Default.AddressVersion.Should().BeInRange(0, 255); // Address version is a byte + } + + [TestMethod] + public void TestNetworkSettingsConsistency() + { + TestProtocolSettings.Default.Network.Should().BePositive(); + TestProtocolSettings.Default.SeedList.Should().NotBeEmpty(); + } + + [TestMethod] + public void TestECPointParsing() + { + foreach (var point in TestProtocolSettings.Default.StandbyCommittee) + { + Action act = () => ECPoint.Parse(point.ToString(), ECCurve.Secp256r1); + act.Should().NotThrow(); + } + } + + [TestMethod] + public void TestSeedListFormatAndReachability() + { + foreach (var seed in TestProtocolSettings.Default.SeedList) + { + seed.Should().MatchRegex(@"^[\w.-]+:\d+$"); // Format: domain:port + } + } + + [TestMethod] + public void TestDefaultNetworkValue() + { + ProtocolSettings.Default.Network.Should().Be(0); + } + + [TestMethod] + public void TestDefaultAddressVersionValue() + { + TestProtocolSettings.Default.AddressVersion.Should().Be(ProtocolSettings.Default.AddressVersion); + } + + [TestMethod] + public void TestDefaultValidatorsCountValue() + { + ProtocolSettings.Default.ValidatorsCount.Should().Be(0); + } + + [TestMethod] + public void TestDefaultMillisecondsPerBlockValue() + { + TestProtocolSettings.Default.MillisecondsPerBlock.Should().Be(ProtocolSettings.Default.MillisecondsPerBlock); + } + + [TestMethod] + public void TestDefaultMaxTransactionsPerBlockValue() + { + TestProtocolSettings.Default.MaxTransactionsPerBlock.Should().Be(ProtocolSettings.Default.MaxTransactionsPerBlock); + } + + [TestMethod] + public void TestDefaultMemoryPoolMaxTransactionsValue() + { + TestProtocolSettings.Default.MemoryPoolMaxTransactions.Should().Be(ProtocolSettings.Default.MemoryPoolMaxTransactions); + } + + [TestMethod] + public void TestDefaultMaxTraceableBlocksValue() + { + TestProtocolSettings.Default.MaxTraceableBlocks.Should().Be(ProtocolSettings.Default.MaxTraceableBlocks); + } + + [TestMethod] + public void TestDefaultInitialGasDistributionValue() + { + TestProtocolSettings.Default.InitialGasDistribution.Should().Be(ProtocolSettings.Default.InitialGasDistribution); + } + + [TestMethod] + public void TestDefaultHardforksValue() + { + TestProtocolSettings.Default.Hardforks.Should().BeEquivalentTo(ProtocolSettings.Default.Hardforks); + } + + [TestMethod] + public void TestTimePerBlockCalculation() + { + var expectedTimeSpan = TimeSpan.FromMilliseconds(TestProtocolSettings.Default.MillisecondsPerBlock); + TestProtocolSettings.Default.TimePerBlock.Should().Be(expectedTimeSpan); + } + + [TestMethod] + public void TestLoad() + { + var loadedSetting = ProtocolSettings.Load("test.config.json", false); + + // Comparing all properties + TestProtocolSettings.Default.Network.Should().Be(loadedSetting.Network); + TestProtocolSettings.Default.AddressVersion.Should().Be(loadedSetting.AddressVersion); + TestProtocolSettings.Default.StandbyCommittee.Should().BeEquivalentTo(loadedSetting.StandbyCommittee); + TestProtocolSettings.Default.ValidatorsCount.Should().Be(loadedSetting.ValidatorsCount); + TestProtocolSettings.Default.SeedList.Should().BeEquivalentTo(loadedSetting.SeedList); + TestProtocolSettings.Default.MillisecondsPerBlock.Should().Be(loadedSetting.MillisecondsPerBlock); + TestProtocolSettings.Default.MaxTransactionsPerBlock.Should().Be(loadedSetting.MaxTransactionsPerBlock); + TestProtocolSettings.Default.MemoryPoolMaxTransactions.Should().Be(loadedSetting.MemoryPoolMaxTransactions); + TestProtocolSettings.Default.MaxTraceableBlocks.Should().Be(loadedSetting.MaxTraceableBlocks); + TestProtocolSettings.Default.InitialGasDistribution.Should().Be(loadedSetting.InitialGasDistribution); + TestProtocolSettings.Default.Hardforks.Should().BeEquivalentTo(loadedSetting.Hardforks); + + // If StandbyValidators is a derived property, comparing it as well + TestProtocolSettings.Default.StandbyValidators.Should().BeEquivalentTo(loadedSetting.StandbyValidators); + } + } +} diff --git a/tests/neo.UnitTests/UT_UInt160.cs b/tests/Neo.UnitTests/UT_UInt160.cs similarity index 89% rename from tests/neo.UnitTests/UT_UInt160.cs rename to tests/Neo.UnitTests/UT_UInt160.cs index 701eec0408..4799a80d19 100644 --- a/tests/neo.UnitTests/UT_UInt160.cs +++ b/tests/Neo.UnitTests/UT_UInt160.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_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. + #pragma warning disable CS1718 using FluentAssertions; diff --git a/tests/neo.UnitTests/UT_UInt256.cs b/tests/Neo.UnitTests/UT_UInt256.cs similarity index 79% rename from tests/neo.UnitTests/UT_UInt256.cs rename to tests/Neo.UnitTests/UT_UInt256.cs index e8b008c7ae..b2bd02dac3 100644 --- a/tests/neo.UnitTests/UT_UInt256.cs +++ b/tests/Neo.UnitTests/UT_UInt256.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_UInt256.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. + #pragma warning disable CS1718 using FluentAssertions; @@ -20,14 +31,14 @@ public void TestFail() [TestMethod] public void TestGernerator1() { - UInt256 uInt256 = new UInt256(); + UInt256 uInt256 = new(); Assert.IsNotNull(uInt256); } [TestMethod] public void TestGernerator2() { - UInt256 uInt256 = new UInt256(new byte[32]); + UInt256 uInt256 = new(new byte[32]); Assert.IsNotNull(uInt256); } @@ -36,7 +47,7 @@ public void TestCompareTo() { byte[] temp = new byte[32]; temp[31] = 0x01; - UInt256 result = new UInt256(temp); + UInt256 result = new(temp); Assert.AreEqual(0, UInt256.Zero.CompareTo(UInt256.Zero)); Assert.AreEqual(-1, UInt256.Zero.CompareTo(result)); Assert.AreEqual(1, result.CompareTo(UInt256.Zero)); @@ -45,14 +56,15 @@ public void TestCompareTo() [TestMethod] public void TestDeserialize() { - using MemoryStream stream = new MemoryStream(); - using BinaryWriter writer = new BinaryWriter(stream); - using BinaryReader reader = new BinaryReader(stream); + using MemoryStream stream = new(); + using BinaryWriter writer = new(stream); writer.Write(new byte[20]); - stream.Seek(0, SeekOrigin.Begin); - UInt256 uInt256 = new UInt256(); - Action action = () => ((ISerializable)uInt256).Deserialize(reader); - action.Should().Throw(); + UInt256 uInt256 = new(); + Assert.ThrowsException(() => + { + MemoryReader reader = new(stream.ToArray()); + ((ISerializable)uInt256).Deserialize(ref reader); + }); } [TestMethod] @@ -60,7 +72,7 @@ public void TestEquals() { byte[] temp = new byte[32]; temp[31] = 0x01; - UInt256 result = new UInt256(temp); + UInt256 result = new(temp); Assert.AreEqual(true, UInt256.Zero.Equals(UInt256.Zero)); Assert.AreEqual(false, UInt256.Zero.Equals(result)); Assert.AreEqual(false, result.Equals(null)); @@ -69,9 +81,9 @@ public void TestEquals() [TestMethod] public void TestEquals1() { - UInt256 temp1 = new UInt256(); - UInt256 temp2 = new UInt256(); - UInt160 temp3 = new UInt160(); + UInt256 temp1 = new(); + UInt256 temp2 = new(); + UInt160 temp3 = new(); Assert.AreEqual(false, temp1.Equals(null)); Assert.AreEqual(true, temp1.Equals(temp1)); Assert.AreEqual(true, temp1.Equals(temp2)); @@ -81,9 +93,9 @@ public void TestEquals1() [TestMethod] public void TestEquals2() { - UInt256 temp1 = new UInt256(); + UInt256 temp1 = new(); object temp2 = null; - object temp3 = new object(); + object temp3 = new(); Assert.AreEqual(false, temp1.Equals(temp2)); Assert.AreEqual(false, temp1.Equals(temp3)); } diff --git a/tests/neo.UnitTests/UT_UIntBenchmarks.cs b/tests/Neo.UnitTests/UT_UIntBenchmarks.cs similarity index 95% rename from tests/neo.UnitTests/UT_UIntBenchmarks.cs rename to tests/Neo.UnitTests/UT_UIntBenchmarks.cs index 882b5647ae..460588e1f1 100644 --- a/tests/neo.UnitTests/UT_UIntBenchmarks.cs +++ b/tests/Neo.UnitTests/UT_UIntBenchmarks.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_UIntBenchmarks.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/Neo.UnitTests/VM/UT_Helper.cs similarity index 97% rename from tests/neo.UnitTests/VM/UT_Helper.cs rename to tests/Neo.UnitTests/VM/UT_Helper.cs index e5a1f61985..fbe12fc787 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/Neo.UnitTests/VM/UT_Helper.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; @@ -137,7 +148,7 @@ public void TestEmitAppCall3() ScriptBuilder sb = new ScriptBuilder(); sb.EmitDynamicCall(UInt160.Zero, "AAAAA", true); byte[] tempArray = new byte[38]; - tempArray[0] = (byte)OpCode.PUSH1; + tempArray[0] = (byte)OpCode.PUSHT; tempArray[1] = (byte)OpCode.PUSH1;//arg.Length tempArray[2] = (byte)OpCode.PACK; tempArray[3] = (byte)OpCode.PUSH15;//(byte)CallFlags.All; @@ -344,7 +355,7 @@ private void TestEmitPush2Boolean() ScriptBuilder sb = new ScriptBuilder(); sb.EmitPush(new ContractParameter(ContractParameterType.Boolean)); byte[] tempArray = new byte[1]; - tempArray[0] = (byte)OpCode.PUSH0; + tempArray[0] = (byte)OpCode.PUSHF; CollectionAssert.AreEqual(tempArray, sb.ToArray()); } @@ -531,7 +542,7 @@ private void TestEmitPush3Bool() ScriptBuilder sb = new ScriptBuilder(); sb.EmitPush(true); byte[] tempArray = new byte[1]; - tempArray[0] = (byte)OpCode.PUSH1; + tempArray[0] = (byte)OpCode.PUSHT; CollectionAssert.AreEqual(tempArray, sb.ToArray()); } @@ -541,7 +552,7 @@ public void TestEmitSysCall() ScriptBuilder sb = new ScriptBuilder(); sb.EmitSysCall(0, true); byte[] tempArray = new byte[6]; - tempArray[0] = (byte)OpCode.PUSH1; + tempArray[0] = (byte)OpCode.PUSHT; tempArray[1] = (byte)OpCode.SYSCALL; tempArray[2] = 0x00; tempArray[3] = 0x00; @@ -593,7 +604,7 @@ private void TestToParameter2ByteArray() private void TestToParameter2VMBoolean() { - StackItem item = new VM.Types.Boolean(true); + StackItem item = StackItem.True; ContractParameter parameter = VM.Helper.ToParameter(item); Assert.AreEqual(ContractParameterType.Boolean, parameter.Type); Assert.AreEqual(true, parameter.Value); diff --git a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs b/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs similarity index 86% rename from tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs rename to tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs index f03f23aeff..65ec90ab7e 100644 --- a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs +++ b/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs @@ -1,7 +1,18 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_NEP6Account.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.Wallets; using Neo.Wallets.NEP6; @@ -23,13 +34,13 @@ public static void ClassSetup(TestContext ctx) byte[] privateKey = { 0x01,0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; _keyPair = new KeyPair(privateKey); - _nep2 = _keyPair.Export("Satoshi", ProtocolSettings.Default.AddressVersion, 2, 1, 1); + _nep2 = _keyPair.Export("Satoshi", TestProtocolSettings.Default.AddressVersion, 2, 1, 1); } [TestInitialize] public void TestSetup() { - _wallet = TestUtils.GenerateTestWallet(); + _wallet = TestUtils.GenerateTestWallet("Satoshi"); byte[] array1 = { 0x01 }; _hash = new UInt160(Crypto.Hash160(array1)); _account = new NEP6Account(_wallet, _hash); @@ -42,7 +53,6 @@ public void TestChangePassword() _account.ChangePasswordPrepare("b", "Satoshi").Should().BeTrue(); _account.ChangePasswordCommit(); _account.Contract = new Contract(); - _wallet.Unlock("Satoshi"); _account.ChangePasswordPrepare("b", "Satoshi").Should().BeFalse(); _account.ChangePasswordPrepare("Satoshi", "b").Should().BeTrue(); _account.ChangePasswordCommit(); @@ -52,7 +62,6 @@ public void TestChangePassword() _account.ChangePasswordPrepare("Satoshi", "b").Should().BeTrue(); _account.ChangePasswordRoolback(); _account.VerifyPassword("Satoshi").Should().BeTrue(); - _wallet.Lock(); } [TestMethod] @@ -66,10 +75,10 @@ public void TestConstructorWithNep2Key() [TestMethod] public void TestConstructorWithKeyPair() { - var wallet = TestUtils.GenerateTestWallet(); + string password = "hello world"; + var wallet = TestUtils.GenerateTestWallet(password); byte[] array1 = { 0x01 }; var hash = new UInt160(Crypto.Hash160(array1)); - string password = "hello world"; NEP6Account account = new(wallet, hash, _keyPair, password); account.ScriptHash.Should().Be(hash); account.Decrypted.Should().BeTrue(); @@ -88,7 +97,7 @@ public void TestFromJson() json["contract"] = null; json["extra"] = null; NEP6Account account = NEP6Account.FromJson(json, _wallet); - account.ScriptHash.Should().Be("NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf".ToScriptHash(ProtocolSettings.Default.AddressVersion)); + account.ScriptHash.Should().Be("NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf".ToScriptHash(TestProtocolSettings.Default.AddressVersion)); account.Label.Should().BeNull(); account.IsDefault.Should().BeTrue(); account.Lock.Should().BeFalse(); @@ -107,7 +116,6 @@ public void TestFromJson() public void TestGetKey() { _account.GetKey().Should().BeNull(); - _wallet.Unlock("Satoshi"); _account = new NEP6Account(_wallet, _hash, _nep2); _account.GetKey().Should().Be(_keyPair); } @@ -136,7 +144,7 @@ public void TestToJson() nep6contract["deployed"] = false; _account.Contract = NEP6Contract.FromJson(nep6contract); JObject json = _account.ToJson(); - json["address"].Should().Equals("AZk5bAanTtD6AvpeesmYgL8CLRYUt5JQsX"); + json["address"].AsString().Should().Be("NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf"); json["label"].Should().BeNull(); json["isDefault"].ToString().Should().Be("false"); json["lock"].ToString().Should().Be("false"); diff --git a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs b/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs similarity index 77% rename from tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs rename to tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs index aad22b2b73..37f816c240 100644 --- a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs +++ b/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs @@ -1,6 +1,17 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_NEP6Contract.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.Wallets.NEP6; using System; @@ -22,7 +33,7 @@ public void TestFromJson() { string json = "{\"script\":\"IQPviR30wLfu+5N9IeoPuIzejg2Cp/8RhytecEeWna+062h0dHaq\"," + "\"parameters\":[{\"name\":\"signature\",\"type\":\"Signature\"}],\"deployed\":false}"; - JObject @object = JObject.Parse(json); + JObject @object = (JObject)JToken.Parse(json); NEP6Contract nep6Contract = NEP6Contract.FromJson(@object); nep6Contract.Script.Should().BeEquivalentTo("2103ef891df4c0b7eefb937d21ea0fb88cde8e0d82a7ff11872b5e7047969dafb4eb68747476aa".HexToBytes()); @@ -54,14 +65,14 @@ public void TestToJson() JArray parameters = (JArray)@object["parameters"]; parameters.Count.Should().Be(2); - jString = (JString)(parameters[0]["name"]); + jString = (JString)parameters[0]["name"]; jString.Value.Should().Be("param1"); - jString = (JString)(parameters[0]["type"]); + jString = (JString)parameters[0]["type"]; jString.Value.Should().Be(ContractParameterType.Boolean.ToString()); - jString = (JString)(parameters[1]["name"]); + jString = (JString)parameters[1]["name"]; jString.Value.Should().Be("param2"); - jString = (JString)(parameters[1]["type"]); + jString = (JString)parameters[1]["type"]; jString.Value.Should().Be(ContractParameterType.Integer.ToString()); } } diff --git a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs b/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs similarity index 82% rename from tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs rename to tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs index 43629acff1..aff3561a75 100644 --- a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs +++ b/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs @@ -1,17 +1,26 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_NEP6Wallet.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; +using Neo.Json; using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.Wallets; using Neo.Wallets.NEP6; -using Neo.Wallets.SQLite; using System; using System.Collections.Generic; using System.IO; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; -using System.Threading; using Contract = Neo.SmartContract.Contract; namespace Neo.UnitTests.Wallets.NEP6 @@ -26,10 +35,11 @@ public class UT_NEP6Wallet private static UInt160 testScriptHash; private string rootPath; - public static string GetRandomPath() + public static string GetRandomPath(string ext = null) { - string threadName = Thread.CurrentThread.ManagedThreadId.ToString(); - return Path.GetFullPath(string.Format("Wallet_{0}", new Random().Next(1, 1000000).ToString("X8")) + threadName); + int rnd = new Random().Next(1, 1000000); + string threadName = Environment.CurrentManagedThreadId.ToString(); + return Path.GetFullPath($"Wallet_{rnd:X8}{threadName}{ext}"); } [ClassInitialize] @@ -42,12 +52,7 @@ public static void ClassInit(TestContext context) } keyPair = new KeyPair(privateKey); testScriptHash = Contract.CreateSignatureContract(keyPair.PublicKey).ScriptHash; - nep2key = keyPair.Export("123", ProtocolSettings.Default.AddressVersion, 2, 1, 1); - } - - private NEP6Wallet CreateWallet() - { - return TestUtils.GenerateTestWallet(); + nep2key = keyPair.Export("123", TestProtocolSettings.Default.AddressVersion, 2, 1, 1); } private string CreateWalletFile() @@ -62,7 +67,7 @@ private string CreateWalletFile() [TestInitialize] public void TestSetup() { - uut = CreateWallet(); + uut = TestUtils.GenerateTestWallet("123"); wPath = CreateWalletFile(); } @@ -76,8 +81,6 @@ public void TestCleanUp() [TestMethod] public void TestCreateAccount() { - var uut = new NEP6Wallet(wPath, ProtocolSettings.Default); - uut.Unlock("123"); var acc = uut.CreateAccount("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632549".HexToBytes()); var tx = new Transaction() { @@ -85,10 +88,10 @@ public void TestCreateAccount() Script = new byte[1], Signers = new Signer[] { new Signer() { Account = acc.ScriptHash } }, }; - var ctx = new ContractParametersContext(TestBlockchain.GetTestSnapshot(), tx, ProtocolSettings.Default.Network); - var sig = uut.Sign(ctx); + var ctx = new ContractParametersContext(TestBlockchain.GetTestSnapshot(), tx, TestProtocolSettings.Default.Network); + Assert.IsTrue(uut.Sign(ctx)); tx.Witnesses = ctx.GetWitnesses(); - Assert.IsTrue(tx.VerifyWitnesses(ProtocolSettings.Default, TestBlockchain.GetTestSnapshot(), long.MaxValue)); + Assert.IsTrue(tx.VerifyWitnesses(TestProtocolSettings.Default, TestBlockchain.GetTestSnapshot(), long.MaxValue)); Assert.ThrowsException(() => uut.CreateAccount((byte[])null)); Assert.ThrowsException(() => uut.CreateAccount("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551".HexToBytes())); } @@ -103,24 +106,22 @@ public void TestChangePassword() wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); File.WriteAllText(wPath, wallet.ToString()); - uut = new NEP6Wallet(wPath, ProtocolSettings.Default); - uut.Unlock("123"); + uut = new NEP6Wallet(wPath, "123", TestProtocolSettings.Default); uut.CreateAccount(keyPair.PrivateKey); uut.ChangePassword("456", "123").Should().BeFalse(); uut.ChangePassword("123", "456").Should().BeTrue(); uut.VerifyPassword("456").Should().BeTrue(); uut.ChangePassword("456", "123").Should().BeTrue(); - uut.Lock(); } [TestMethod] public void TestConstructorWithPathAndName() { - NEP6Wallet wallet = new(wPath, ProtocolSettings.Default); + NEP6Wallet wallet = new(wPath, "123", TestProtocolSettings.Default); Assert.AreEqual("name", wallet.Name); Assert.AreEqual(new ScryptParameters(2, 1, 1).ToJson().ToString(), wallet.Scrypt.ToJson().ToString()); Assert.AreEqual(new Version("1.0").ToString(), wallet.Version.ToString()); - wallet = new NEP6Wallet("", ProtocolSettings.Default, "test"); + wallet = new NEP6Wallet("", "123", TestProtocolSettings.Default, "test"); Assert.AreEqual("test", wallet.Name); Assert.AreEqual(ScryptParameters.Default.ToJson().ToString(), wallet.Scrypt.ToJson().ToString()); Assert.AreEqual(Version.Parse("1.0"), wallet.Version); @@ -136,7 +137,7 @@ public void TestConstructorWithJObject() wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); wallet.ToString().Should().Be("{\"name\":\"test\",\"version\":\"1.0\",\"scrypt\":{\"n\":16384,\"r\":8,\"p\":8},\"accounts\":[],\"extra\":{}}"); - NEP6Wallet w = new(null, ProtocolSettings.Default, wallet); + NEP6Wallet w = new(null, "123", TestProtocolSettings.Default, wallet); Assert.AreEqual("test", w.Name); Assert.AreEqual(Version.Parse("1.0").ToString(), w.Version.ToString()); } @@ -171,7 +172,6 @@ public void TestAddCount() WalletAccount account = uut.GetAccount(testScriptHash); Assert.IsTrue(account.WatchOnly); Assert.IsFalse(account.HasKey); - uut.Unlock("123"); uut.CreateAccount(keyPair.PrivateKey); account = uut.GetAccount(testScriptHash); Assert.IsFalse(account.WatchOnly); @@ -191,7 +191,6 @@ public void TestCreateAccountWithPrivateKey() { bool result = uut.Contains(testScriptHash); Assert.AreEqual(false, result); - uut.Unlock("123"); uut.CreateAccount(keyPair.PrivateKey); result = uut.Contains(testScriptHash); Assert.AreEqual(true, result); @@ -209,7 +208,6 @@ public void TestCreateAccountWithKeyPair() uut.DeleteAccount(testScriptHash); result = uut.Contains(testScriptHash); Assert.AreEqual(false, result); - uut.Unlock("123"); uut.CreateAccount(contract, keyPair); result = uut.Contains(testScriptHash); Assert.AreEqual(true, result); @@ -229,7 +227,6 @@ public void TestCreateAccountWithScriptHash() public void TestDecryptKey() { string nep2key = keyPair.Export("123", ProtocolSettings.Default.AddressVersion, 2, 1, 1); - uut.Unlock("123"); KeyPair key1 = uut.DecryptKey(nep2key); bool result = key1.Equals(keyPair); Assert.AreEqual(true, result); @@ -253,7 +250,6 @@ public void TestGetAccount() { bool result = uut.Contains(testScriptHash); Assert.AreEqual(false, result); - uut.Unlock("123"); uut.CreateAccount(keyPair.PrivateKey); result = uut.Contains(testScriptHash); Assert.AreEqual(true, result); @@ -265,7 +261,6 @@ public void TestGetAccount() public void TestGetAccounts() { Dictionary keys = new(); - uut.Unlock("123"); byte[] privateKey = new byte[32]; using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) { @@ -307,7 +302,6 @@ public void TestImportCert() X509Certificate2 cert = NewCertificate(); Assert.IsNotNull(cert); Assert.AreEqual(true, cert.HasPrivateKey); - uut.Unlock("123"); WalletAccount account = uut.Import(cert); Assert.IsNotNull(account); } @@ -318,7 +312,6 @@ public void TestImportWif() string wif = keyPair.Export(); bool result = uut.Contains(testScriptHash); Assert.AreEqual(false, result); - uut.Unlock("123"); uut.Import(wif); result = uut.Contains(testScriptHash); Assert.AreEqual(true, result); @@ -341,7 +334,7 @@ public void TestImportNep2() wallet["scrypt"] = new ScryptParameters(2, 1, 1).ToJson(); wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); - uut = new NEP6Wallet(null, ProtocolSettings.Default, wallet); + uut = new NEP6Wallet(null, "123", ProtocolSettings.Default, wallet); result = uut.Contains(testScriptHash); Assert.AreEqual(false, result); uut.Import(nep2key, "123", 2, 1, 1); @@ -349,27 +342,15 @@ public void TestImportNep2() Assert.AreEqual(true, result); } - [TestMethod] - public void TestLock() - { - Assert.ThrowsException(() => uut.CreateAccount(keyPair.PrivateKey)); - uut.Unlock("123"); - uut.CreateAccount(keyPair.PrivateKey); - bool result = uut.Contains(testScriptHash); - Assert.AreEqual(true, result); - uut.DeleteAccount(testScriptHash); - uut.Lock(); - Assert.ThrowsException(() => uut.CreateAccount(keyPair.PrivateKey)); - } - [TestMethod] public void TestMigrate() { - string path = GetRandomPath(); - UserWallet uw = UserWallet.Create(path, "123", ProtocolSettings.Default); + string path = GetRandomPath(".json"); + Wallet uw = Wallet.Create(null, path, "123", ProtocolSettings.Default); uw.CreateAccount(keyPair.PrivateKey); - string npath = CreateWalletFile(); // Scrypt test values - NEP6Wallet nw = NEP6Wallet.Migrate(npath, path, "123", ProtocolSettings.Default); + uw.Save(); + string npath = GetRandomPath(".json"); + Wallet nw = Wallet.Migrate(npath, path, "123", ProtocolSettings.Default); bool result = nw.Contains(testScriptHash); Assert.AreEqual(true, result); uw.Delete(); @@ -386,8 +367,7 @@ public void TestSave() wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); File.WriteAllText(wPath, wallet.ToString()); - uut = new NEP6Wallet(wPath, ProtocolSettings.Default); - uut.Unlock("123"); + uut = new NEP6Wallet(wPath, "123", ProtocolSettings.Default); uut.CreateAccount(keyPair.PrivateKey); bool result = uut.Contains(testScriptHash); Assert.AreEqual(true, result); @@ -397,14 +377,10 @@ public void TestSave() } [TestMethod] - public void TestUnlock() + public void TestToJson() { - Assert.ThrowsException(() => uut.CreateAccount(keyPair.PrivateKey)); - uut.Unlock("123"); - uut.CreateAccount(keyPair.PrivateKey); - bool result = uut.Contains(testScriptHash); - Assert.AreEqual(true, result); - Assert.ThrowsException(() => uut.Unlock("1")); + var json = uut.ToJson(); + json.ToString().Should().Be("{\"name\":\"noname\",\"version\":\"1.0\",\"scrypt\":{\"n\":2,\"r\":1,\"p\":1},\"accounts\":[],\"extra\":null}"); } [TestMethod] @@ -412,8 +388,6 @@ public void TestVerifyPassword() { bool result = uut.VerifyPassword("123"); Assert.AreEqual(true, result); - Assert.ThrowsException(() => uut.CreateAccount(keyPair.PrivateKey)); - uut.Unlock("123"); uut.CreateAccount(keyPair.PrivateKey); result = uut.Contains(testScriptHash); Assert.AreEqual(true, result); @@ -427,7 +401,7 @@ public void TestVerifyPassword() wallet["scrypt"] = new ScryptParameters(2, 1, 1).ToJson(); wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); - uut = new NEP6Wallet(null, ProtocolSettings.Default, wallet); + uut = new NEP6Wallet(null, "123", ProtocolSettings.Default, wallet); nep2key = keyPair.Export("123", ProtocolSettings.Default.AddressVersion, 2, 1, 1); uut.Import(nep2key, "123", 2, 1, 1); Assert.IsFalse(uut.VerifyPassword("1")); @@ -452,8 +426,7 @@ public void TestIsDefault() wallet["scrypt"] = new ScryptParameters(2, 1, 1).ToJson(); wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); - var w = new NEP6Wallet(null, ProtocolSettings.Default, wallet); - using var l = w.Unlock(""); + var w = new NEP6Wallet(null, "", ProtocolSettings.Default, wallet); var ac = w.CreateAccount(); Assert.AreEqual(ac.Address, w.GetDefaultAccount().Address); var ac2 = w.CreateAccount(); diff --git a/tests/neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs b/tests/Neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs similarity index 79% rename from tests/neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs rename to tests/Neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs index 65d29e9fce..8a468f9f41 100644 --- a/tests/neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs +++ b/tests/Neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs @@ -1,6 +1,17 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ScryptParameters.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; +using Neo.Json; using Neo.Wallets.NEP6; namespace Neo.UnitTests.Wallets.NEP6 diff --git a/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs b/tests/Neo.UnitTests/Wallets/UT_AssetDescriptor.cs similarity index 70% rename from tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs rename to tests/Neo.UnitTests/Wallets/UT_AssetDescriptor.cs index d87811cf7f..8d938492bc 100644 --- a/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs +++ b/tests/Neo.UnitTests/Wallets/UT_AssetDescriptor.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_AssetDescriptor.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract.Native; @@ -14,7 +25,7 @@ public void TestConstructorWithNonexistAssetId() var snapshot = TestBlockchain.GetTestSnapshot(); Action action = () => { - var descriptor = new Neo.Wallets.AssetDescriptor(snapshot, ProtocolSettings.Default, UInt160.Parse("01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4")); + var descriptor = new Neo.Wallets.AssetDescriptor(snapshot, TestProtocolSettings.Default, UInt160.Parse("01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4")); }; action.Should().Throw(); } @@ -23,7 +34,7 @@ public void TestConstructorWithNonexistAssetId() public void Check_GAS() { var snapshot = TestBlockchain.GetTestSnapshot(); - var descriptor = new Neo.Wallets.AssetDescriptor(snapshot, ProtocolSettings.Default, NativeContract.GAS.Hash); + var descriptor = new Neo.Wallets.AssetDescriptor(snapshot, TestProtocolSettings.Default, NativeContract.GAS.Hash); descriptor.AssetId.Should().Be(NativeContract.GAS.Hash); descriptor.AssetName.Should().Be(nameof(GasToken)); descriptor.ToString().Should().Be(nameof(GasToken)); @@ -35,7 +46,7 @@ public void Check_GAS() public void Check_NEO() { var snapshot = TestBlockchain.GetTestSnapshot(); - var descriptor = new Neo.Wallets.AssetDescriptor(snapshot, ProtocolSettings.Default, NativeContract.NEO.Hash); + var descriptor = new Neo.Wallets.AssetDescriptor(snapshot, TestProtocolSettings.Default, NativeContract.NEO.Hash); descriptor.AssetId.Should().Be(NativeContract.NEO.Hash); descriptor.AssetName.Should().Be(nameof(NeoToken)); descriptor.ToString().Should().Be(nameof(NeoToken)); diff --git a/tests/neo.UnitTests/Wallets/UT_KeyPair.cs b/tests/Neo.UnitTests/Wallets/UT_KeyPair.cs similarity index 88% rename from tests/neo.UnitTests/Wallets/UT_KeyPair.cs rename to tests/Neo.UnitTests/Wallets/UT_KeyPair.cs index e14bb83fd7..7ae84960c7 100644 --- a/tests/neo.UnitTests/Wallets/UT_KeyPair.cs +++ b/tests/Neo.UnitTests/Wallets/UT_KeyPair.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_KeyPair.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; @@ -69,7 +80,7 @@ public void TestEqualsWithObj() for (int i = 0; i < privateKey.Length; i++) privateKey[i] = (byte)random.Next(256); KeyPair keyPair = new KeyPair(privateKey); - Object keyPair2 = keyPair as Object; + Object keyPair2 = keyPair; keyPair.Equals(keyPair2).Should().BeTrue(); } @@ -98,8 +109,9 @@ public void TestGetHashCode() { byte[] privateKey = { 0x01,0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; - KeyPair keyPair = new KeyPair(privateKey); - keyPair.GetHashCode().Should().Be(1544360595); + KeyPair keyPair1 = new KeyPair(privateKey); + KeyPair keyPair2 = new KeyPair(privateKey); + keyPair1.GetHashCode().Should().Be(keyPair2.GetHashCode()); } [TestMethod] diff --git a/tests/neo.UnitTests/Wallets/UT_Wallet.cs b/tests/Neo.UnitTests/Wallets/UT_Wallet.cs similarity index 93% rename from tests/neo.UnitTests/Wallets/UT_Wallet.cs rename to tests/Neo.UnitTests/Wallets/UT_Wallet.cs index 0dedce66b1..d134c6aed3 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/tests/Neo.UnitTests/Wallets/UT_Wallet.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Wallet.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; @@ -20,7 +31,7 @@ internal class MyWallet : Wallet private readonly Dictionary accounts = new(); - public MyWallet() : base(null, ProtocolSettings.Default) + public MyWallet() : base(null, TestProtocolSettings.Default) { } @@ -42,7 +53,7 @@ public void AddAccount(WalletAccount account) public override WalletAccount CreateAccount(byte[] privateKey) { KeyPair key = new(privateKey); - Neo.Wallets.SQLite.VerificationContract contract = new() + var contract = new Contract { Script = Contract.CreateSignatureRedeemScript(key.PublicKey), ParameterList = new[] { ContractParameterType.Signature } @@ -96,6 +107,10 @@ public override bool VerifyPassword(string password) { return true; } + + public override void Save() + { + } } [TestClass] @@ -108,7 +123,7 @@ public class UT_Wallet public static void ClassInit(TestContext ctx) { glkey = UT_Crypto.GenerateCertainKey(32); - nep2Key = glkey.Export("pwd", ProtocolSettings.Default.AddressVersion, 2, 1, 1); + nep2Key = glkey.Export("pwd", TestProtocolSettings.Default.AddressVersion, 2, 1, 1); } [TestMethod] @@ -206,13 +221,11 @@ public void TestGetAvailable() var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; - snapshot.Commit(); wallet.GetAvailable(snapshot, NativeContract.GAS.Hash).Should().Be(new BigDecimal(new BigInteger(1000000000000M), 8)); entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 0; - snapshot.Commit(); } [TestMethod] @@ -228,26 +241,18 @@ public void TestGetBalance() var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; - snapshot.Commit(); wallet.GetBalance(snapshot, UInt160.Zero, new UInt160[] { account.ScriptHash }).Should().Be(new BigDecimal(BigInteger.Zero, 0)); wallet.GetBalance(snapshot, NativeContract.GAS.Hash, new UInt160[] { account.ScriptHash }).Should().Be(new BigDecimal(new BigInteger(1000000000000M), 8)); entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 0; - snapshot.Commit(); } [TestMethod] public void TestGetPrivateKeyFromNEP2() { - Action action = () => Wallet.GetPrivateKeyFromNEP2(null, null, ProtocolSettings.Default.AddressVersion, 2, 1, 1); - action.Should().Throw(); - - action = () => Wallet.GetPrivateKeyFromNEP2("TestGetPrivateKeyFromNEP2", null, ProtocolSettings.Default.AddressVersion, 2, 1, 1); - action.Should().Throw(); - - action = () => Wallet.GetPrivateKeyFromNEP2("3vQB7B6MrGQZaxCuFg4oh", "TestGetPrivateKeyFromNEP2", ProtocolSettings.Default.AddressVersion, 2, 1, 1); + Action action = () => Wallet.GetPrivateKeyFromNEP2("3vQB7B6MrGQZaxCuFg4oh", "TestGetPrivateKeyFromNEP2", ProtocolSettings.Default.AddressVersion, 2, 1, 1); action.Should().Throw(); action = () => Wallet.GetPrivateKeyFromNEP2(nep2Key, "Test", ProtocolSettings.Default.AddressVersion, 2, 1, 1); @@ -336,8 +341,6 @@ public void TestMakeTransaction1() var entry2 = snapshot.GetAndChange(key, () => new StorageItem(new NeoToken.NeoAccountState())); entry2.GetInteroperable().Balance = 10000 * NativeContract.NEO.Factor; - snapshot.Commit(); - var tx = wallet.MakeTransaction(snapshot, new TransferOutput[] { new TransferOutput() @@ -365,7 +368,6 @@ public void TestMakeTransaction1() entry2 = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry1.GetInteroperable().Balance = 0; entry2.GetInteroperable().Balance = 0; - snapshot.Commit(); } [TestMethod] @@ -384,7 +386,6 @@ public void TestMakeTransaction2() var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 1000000 * NativeContract.GAS.Factor; - snapshot.Commit(); var tx = wallet.MakeTransaction(snapshot, Array.Empty(), account.ScriptHash, new[]{ new Signer() { @@ -399,7 +400,6 @@ public void TestMakeTransaction2() entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 0; - snapshot.Commit(); } [TestMethod] diff --git a/tests/neo.UnitTests/Wallets/UT_WalletAccount.cs b/tests/Neo.UnitTests/Wallets/UT_WalletAccount.cs similarity index 70% rename from tests/neo.UnitTests/Wallets/UT_WalletAccount.cs rename to tests/Neo.UnitTests/Wallets/UT_WalletAccount.cs index 13ba4d113f..26a8464a7f 100644 --- a/tests/neo.UnitTests/Wallets/UT_WalletAccount.cs +++ b/tests/Neo.UnitTests/Wallets/UT_WalletAccount.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_WalletAccount.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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; @@ -11,7 +22,7 @@ public class MyWalletAccount : WalletAccount public override bool HasKey => key != null; public MyWalletAccount(UInt160 scriptHash) - : base(scriptHash, ProtocolSettings.Default) + : base(scriptHash, TestProtocolSettings.Default) { } diff --git a/tests/neo.UnitTests/Wallets/UT_Wallets_Helper.cs b/tests/Neo.UnitTests/Wallets/UT_Wallets_Helper.cs similarity index 62% rename from tests/neo.UnitTests/Wallets/UT_Wallets_Helper.cs rename to tests/Neo.UnitTests/Wallets/UT_Wallets_Helper.cs index 8c5f23f5a4..843eeae192 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallets_Helper.cs +++ b/tests/Neo.UnitTests/Wallets/UT_Wallets_Helper.cs @@ -1,3 +1,14 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Wallets_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 FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; @@ -15,9 +26,9 @@ public void TestToScriptHash() { byte[] array = { 0x01 }; UInt160 scriptHash = new UInt160(Crypto.Hash160(array)); - "NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf".ToScriptHash(ProtocolSettings.Default.AddressVersion).Should().Be(scriptHash); + "NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf".ToScriptHash(TestProtocolSettings.Default.AddressVersion).Should().Be(scriptHash); - Action action = () => "3vQB7B6MrGQZaxCuFg4oh".ToScriptHash(ProtocolSettings.Default.AddressVersion); + Action action = () => "3vQB7B6MrGQZaxCuFg4oh".ToScriptHash(TestProtocolSettings.Default.AddressVersion); action.Should().Throw(); var address = scriptHash.ToAddress(ProtocolSettings.Default.AddressVersion); diff --git a/tests/Neo.UnitTests/test.config.json b/tests/Neo.UnitTests/test.config.json new file mode 100644 index 0000000000..0d2f885da6 --- /dev/null +++ b/tests/Neo.UnitTests/test.config.json @@ -0,0 +1,66 @@ +{ + "ApplicationConfiguration": { + "Logger": { + "Path": "Logs", + "ConsoleOutput": false, + "Active": false + }, + "Storage": { + "Engine": "LevelDBStore", + "Path": "Data_LevelDB_{0}" + }, + "P2P": { + "Port": 10333, + "WsPort": 10334, + "MinDesiredConnections": 10, + "MaxConnections": 40, + "MaxConnectionsPerAddress": 3 + }, + "UnlockWallet": { + "Path": "", + "Password": "", + "IsActive": false + } + }, + "ProtocolConfiguration": { + "Network": 860833102, + "AddressVersion": 53, + "MillisecondsPerBlock": 15000, + "MaxTransactionsPerBlock": 512, + "MemoryPoolMaxTransactions": 50000, + "MaxTraceableBlocks": 2102400, + "Hardforks": {}, + "InitialGasDistribution": 5200000000000000, + "ValidatorsCount": 7, + "StandbyCommittee": [ + "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", + "02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", + "03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", + "02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", + "024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", + "02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", + "02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", + "023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe", + "03708b860c1de5d87f5b151a12c2a99feebd2e8b315ee8e7cf8aa19692a9e18379", + "03c6aa6e12638b36e88adc1ccdceac4db9929575c3e03576c617c49cce7114a050", + "03204223f8c86b8cd5c89ef12e4f0dbb314172e9241e30c9ef2293790793537cf0", + "02a62c915cf19c7f19a50ec217e79fac2439bbaad658493de0c7d8ffa92ab0aa62", + "03409f31f0d66bdc2f70a9730b66fe186658f84a8018204db01c106edc36553cd0", + "0288342b141c30dc8ffcde0204929bb46aed5756b41ef4a56778d15ada8f0c6654", + "020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639", + "0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30", + "03d281b42002647f0113f36c7b8efb30db66078dfaaa9ab3ff76d043a98d512fde", + "02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad", + "0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d", + "03cdcea66032b82f5c30450e381e5295cae85c5e6943af716cc6b646352a6067dc", + "02cd5a5547119e24feaa7c2a0f37b8c9366216bab7054de0065c9be42084003c8a" + ], + "SeedList": [ + "seed1.neo.org:10333", + "seed2.neo.org:10333", + "seed3.neo.org:10333", + "seed4.neo.org:10333", + "seed5.neo.org:10333" + ] + } +} diff --git a/tests/Neo.VM.Tests/Converters/ScriptConverter.cs b/tests/Neo.VM.Tests/Converters/ScriptConverter.cs new file mode 100644 index 0000000000..064df4fb30 --- /dev/null +++ b/tests/Neo.VM.Tests/Converters/ScriptConverter.cs @@ -0,0 +1,162 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ScriptConverter.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.VisualStudio.TestTools.UnitTesting; +using Neo.Test.Extensions; +using Neo.VM; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Linq; + +namespace Neo.Test.Converters +{ + internal class ScriptConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType == typeof(byte[]) || objectType == typeof(string); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + switch (reader.TokenType) + { + case JsonToken.String: + { + if (reader.Value is string str) + { + Assert.IsTrue(str.StartsWith("0x"), $"'0x' prefix required for value: '{str}'"); + return str.FromHexString(); + } + break; + } + case JsonToken.Bytes: + { + if (reader.Value is byte[] data) return data; + break; + } + case JsonToken.StartArray: + { + using var script = new ScriptBuilder(); + + foreach (var entry in JArray.Load(reader)) + { + var mul = 1; + var value = entry.Value(); + + if (Enum.IsDefined(typeof(OpCode), value) && Enum.TryParse(value, out var opCode)) + { + for (int x = 0; x < mul; x++) + { + script.Emit(opCode); + } + } + else + { + for (int x = 0; x < mul; x++) + { + Assert.IsTrue(value.StartsWith("0x"), $"'0x' prefix required for value: '{value}'"); + script.EmitRaw(value.FromHexString()); + } + } + } + + return script.ToArray(); + } + } + + throw new FormatException(); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value is byte[] data) + { + int ip = 0; + var array = new JArray(); + + try + { + for (ip = 0; ip < data.Length;) + { + var instruction = new Instruction(data, ip); + + array.Add(instruction.OpCode.ToString().ToUpperInvariant()); + + // Operand Size + + if (instruction.Size - 1 - instruction.Operand.Length > 0) + { + array.Add(data.Skip(ip + 1).Take(instruction.Size - 1 - instruction.Operand.Length).ToArray().ToHexString()); + } + + if (!instruction.Operand.IsEmpty) + { + // Data + + array.Add(instruction.Operand.ToArray().ToHexString()); + } + + ip += instruction.Size; + } + } + catch + { + // Something was wrong, but maybe it's intentioned + + if (Enum.IsDefined(typeof(OpCode), data[ip])) + { + // Check if it was the content and not the opcode + + array.Add(((OpCode)data[ip]).ToString().ToUpperInvariant()); + array.Add(data[(ip + 1)..].ToHexString()); + } + else + { + array.Add(data[ip..].ToHexString()); + } + } + + // Write the script + + writer.WriteStartArray(); + foreach (var entry in array) writer.WriteValue(entry.Value()); + writer.WriteEndArray(); + + // Double check - Ensure that the format is exactly the same + + using var script = new ScriptBuilder(); + + foreach (var entry in array) + { + if (Enum.TryParse(entry.Value(), out var opCode)) + { + script.Emit(opCode); + } + else + { + script.EmitRaw(entry.Value().FromHexString()); + } + } + + if (script.ToArray().ToHexString() != data.ToHexString()) + { + throw new FormatException(); + } + } + else + { + throw new FormatException(); + } + } + } +} diff --git a/tests/Neo.VM.Tests/Converters/UppercaseEnum.cs b/tests/Neo.VM.Tests/Converters/UppercaseEnum.cs new file mode 100644 index 0000000000..0da336e698 --- /dev/null +++ b/tests/Neo.VM.Tests/Converters/UppercaseEnum.cs @@ -0,0 +1,34 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UppercaseEnum.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 Newtonsoft.Json; +using System; + +namespace Neo.Test.Converters +{ + internal class UppercaseEnum : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType.IsEnum; + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return Enum.Parse(objectType, reader.Value.ToString(), true); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(value.ToString().ToUpperInvariant()); + } + } +} diff --git a/tests/Neo.VM.Tests/Extensions/JsonExtensions.cs b/tests/Neo.VM.Tests/Extensions/JsonExtensions.cs new file mode 100644 index 0000000000..01dd057ca8 --- /dev/null +++ b/tests/Neo.VM.Tests/Extensions/JsonExtensions.cs @@ -0,0 +1,58 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JsonExtensions.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 Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; + +namespace Neo.Test.Extensions +{ + public static class JsonExtensions + { + private static readonly JsonSerializerSettings _settings; + + /// + /// Static constructor + /// + static JsonExtensions() + { + _settings = new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + Formatting = Formatting.Indented, + NullValueHandling = NullValueHandling.Ignore, + }; + + _settings.Converters.Add(new StringEnumConverter(new CamelCaseNamingStrategy())); + } + + /// + /// Deserialize json to object + /// + /// Type + /// Json + /// Unit test + public static T DeserializeJson(this string input) + { + return JsonConvert.DeserializeObject(input, _settings); + } + + /// + /// Serialize UT to json + /// + /// Unit test + /// Json + public static string ToJson(this object ut) + { + return JsonConvert.SerializeObject(ut, _settings); + } + } +} diff --git a/tests/Neo.VM.Tests/Extensions/StringExtensions.cs b/tests/Neo.VM.Tests/Extensions/StringExtensions.cs new file mode 100644 index 0000000000..4263d271c4 --- /dev/null +++ b/tests/Neo.VM.Tests/Extensions/StringExtensions.cs @@ -0,0 +1,68 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// StringExtensions.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; +using System.Globalization; + +namespace Neo.Test.Extensions +{ + internal static class StringExtensions + { + /// + /// Convert buffer to hex string + /// + /// Data + /// Return hex string + public static string ToHexString(this byte[] data) + { + if (data == null) return ""; + + var m = data.Length; + if (m == 0) return ""; + + var sb = new char[(m * 2) + 2]; + + sb[0] = '0'; + sb[1] = 'x'; + + for (int x = 0, y = 2; x < m; x++, y += 2) + { + var hex = data[x].ToString("x2"); + + sb[y] = hex[0]; + sb[y + 1] = hex[1]; + } + + return new string(sb); + } + + /// + /// Convert string in Hex format to byte array + /// + /// Hexadecimal string + /// Return byte array + public static byte[] FromHexString(this string value) + { + if (string.IsNullOrEmpty(value)) + return Array.Empty(); + if (value.StartsWith("0x")) + value = value[2..]; + if (value.Length % 2 == 1) + throw new FormatException(); + + var result = new byte[value.Length / 2]; + for (var i = 0; i < result.Length; i++) + result[i] = byte.Parse(value.Substring(i * 2, 2), NumberStyles.AllowHexSpecifier); + + return result; + } + } +} diff --git a/tests/Neo.VM.Tests/Helpers/RandomHelper.cs b/tests/Neo.VM.Tests/Helpers/RandomHelper.cs new file mode 100644 index 0000000000..77dc999145 --- /dev/null +++ b/tests/Neo.VM.Tests/Helpers/RandomHelper.cs @@ -0,0 +1,50 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// RandomHelper.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; + +namespace Neo.Test.Helpers +{ + public class RandomHelper + { + private const string _randchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + private static readonly Random _rand = new(); + + /// + /// Get random buffer + /// + /// Length + /// Buffer + public static byte[] RandBuffer(int length) + { + var buffer = new byte[length]; + _rand.NextBytes(buffer); + return buffer; + } + + /// + /// Get random string + /// + /// Length + /// Buffer + public static string RandString(int length) + { + var stringChars = new char[length]; + + for (int i = 0; i < stringChars.Length; i++) + { + stringChars[i] = _randchars[_rand.Next(_randchars.Length)]; + } + + return new string(stringChars); + } + } +} diff --git a/tests/Neo.VM.Tests/Neo.VM.Tests.csproj b/tests/Neo.VM.Tests/Neo.VM.Tests.csproj new file mode 100644 index 0000000000..1020d1c55a --- /dev/null +++ b/tests/Neo.VM.Tests/Neo.VM.Tests.csproj @@ -0,0 +1,20 @@ + + + + net7.0 + Neo.Test + true + false + + + + + + + + + PreserveNewest + + + + diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/GE.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/GE.json new file mode 100644 index 0000000000..d70ebf3be6 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/GE.json @@ -0,0 +1,443 @@ +{ + "category": "Numeric", + "name": "GE same types", + "tests": [ + { + "name": "Without two pushes", + "script": [ + "GE" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one push", + "script": [ + "PUSH0", + "GE" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "GE", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [0,0]", + "script": [ + "PUSH0", + "PUSH0", + "GE" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "GE", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [1,0]", + "script": [ + "PUSH1", + "PUSH0", + "GE" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "GE", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [0,1]", + "script": [ + "PUSH0", + "PUSH1", + "GE" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "GE", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [null,1]", + "script": [ + "PUSHNULL", + "PUSH1", + "GE" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "GE", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "null" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [1,null]", + "script": [ + "PUSH1", + "PUSHNULL", + "GE" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "GE", + "evaluationStack": [ + { + "type": "null" + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "FAULT test [1,array]", + "script": [ + "PUSH1", + "NEWARRAY0", + "GE" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "GE", + "evaluationStack": [ + { + "type": "array", + "value": [] + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/GT.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/GT.json new file mode 100644 index 0000000000..edc0767b3c --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/GT.json @@ -0,0 +1,443 @@ +{ + "category": "Numeric", + "name": "GT same types", + "tests": [ + { + "name": "Without two pushes", + "script": [ + "GT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one push", + "script": [ + "PUSH0", + "GT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "GT", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [0,0]", + "script": [ + "PUSH0", + "PUSH0", + "GT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "GT", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [1,0]", + "script": [ + "PUSH1", + "PUSH0", + "GT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "GT", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [0,1]", + "script": [ + "PUSH0", + "PUSH1", + "GT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "GT", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [null,1]", + "script": [ + "PUSHNULL", + "PUSH1", + "GT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "GT", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "null" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [1,null]", + "script": [ + "PUSH1", + "PUSHNULL", + "GT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "GT", + "evaluationStack": [ + { + "type": "null" + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "FAULT test [1,array]", + "script": [ + "PUSH1", + "NEWARRAY0", + "GT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "GT", + "evaluationStack": [ + { + "type": "array", + "value": [] + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/LE.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/LE.json new file mode 100644 index 0000000000..eb85215322 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/LE.json @@ -0,0 +1,443 @@ +{ + "category": "Numeric", + "name": "LE same types", + "tests": [ + { + "name": "Without two pushes", + "script": [ + "LE" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one push", + "script": [ + "PUSH0", + "LE" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "LE", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [0,0]", + "script": [ + "PUSH0", + "PUSH0", + "LE" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "LE", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [1,0]", + "script": [ + "PUSH1", + "PUSH0", + "LE" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "LE", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [0,1]", + "script": [ + "PUSH0", + "PUSH1", + "LE" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "LE", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [null,1]", + "script": [ + "PUSHNULL", + "PUSH1", + "LE" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "LE", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "null" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [1,null]", + "script": [ + "PUSH1", + "PUSHNULL", + "LE" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "LE", + "evaluationStack": [ + { + "type": "null" + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "FAULT test [1,array]", + "script": [ + "PUSH1", + "NEWARRAY0", + "LE" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "LE", + "evaluationStack": [ + { + "type": "array", + "value": [] + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/LT.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/LT.json new file mode 100644 index 0000000000..ed2b6fac01 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/LT.json @@ -0,0 +1,443 @@ +{ + "category": "Numeric", + "name": "LT same types", + "tests": [ + { + "name": "Without two pushes", + "script": [ + "LT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one push", + "script": [ + "PUSH0", + "LT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "LT", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [0,0]", + "script": [ + "PUSH0", + "PUSH0", + "LT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "LT", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [1,0]", + "script": [ + "PUSH1", + "PUSH0", + "LT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "LT", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [0,1]", + "script": [ + "PUSH0", + "PUSH1", + "LT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "LT", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [null,1]", + "script": [ + "PUSHNULL", + "PUSH1", + "LT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "LT", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "null" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [1,null]", + "script": [ + "PUSH1", + "PUSHNULL", + "LT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "LT", + "evaluationStack": [ + { + "type": "null" + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "FAULT test [1,array]", + "script": [ + "PUSH1", + "NEWARRAY0", + "LT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "LT", + "evaluationStack": [ + { + "type": "array", + "value": [] + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/MODMUL.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/MODMUL.json new file mode 100644 index 0000000000..e3b93bfb56 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/MODMUL.json @@ -0,0 +1,141 @@ +{ + "category": "Numeric", + "name": "MODMUL", + "tests": [ + { + "name": "Exception - Without items", + "script": [ + "MODMUL" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "MODMUL", + "evaluationStack": [] + } + ] + } + } + ] + }, + { + "name": "Real test (8 * 2 % 3)", + "script": [ + "PUSH8", + "PUSH2", + "PUSH3", + "MODMUL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "MODMUL", + "evaluationStack": [ + { + "type": "Integer", + "value": 3 + }, + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 8 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Real test (16 * 2 % 4)", + "script": [ + "PUSH16", + "PUSH2", + "PUSH4", + "MODMUL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "MODMUL", + "evaluationStack": [ + { + "type": "Integer", + "value": 4 + }, + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 16 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": 0 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/MODPOW.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/MODPOW.json new file mode 100644 index 0000000000..68efa97efa --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/MODPOW.json @@ -0,0 +1,145 @@ +{ + "category": "Numeric", + "name": "MODPOW", + "tests": [ + { + "name": "Exception - Without items", + "script": [ + "MODPOW" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "MODPOW", + "evaluationStack": [] + } + ] + } + } + ] + }, + { + "name": "Real test (19 ModInverse 141 = 52)", + "script": [ + "PUSHINT8", + "0x13", + "PUSHM1", + "PUSHINT16", + "0x8d00", + "MODPOW" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "MODPOW", + "evaluationStack": [ + { + "type": "Integer", + "value": 141 + }, + { + "type": "Integer", + "value": -1 + }, + { + "type": "Integer", + "value": 19 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": 52 + } + ] + } + } + ] + }, + { + "name": "Real test (ModPow 19, 2, 141 = 79)", + "script": [ + "PUSHINT8", + "0x13", + "PUSH2", + "PUSHINT16", + "0x8d00", + "MODPOW" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "MODPOW", + "evaluationStack": [ + { + "type": "Integer", + "value": 141 + }, + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 19 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": 79 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/NOT.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/NOT.json new file mode 100644 index 0000000000..72d36c6fc7 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/NOT.json @@ -0,0 +1,537 @@ +{ + "category": "Numeric", + "name": "NOT", + "tests": [ + { + "name": "Without push", + "script": [ + "NOT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With bool", + "script": [ + "PUSH1", + "NOT", + "NOT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "NOT", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "With integer 0x03", + "script": [ + "PUSH3", + "NOT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "NOT", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "With map", + "script": [ + "NEWMAP", + "NOT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "NOT", + "evaluationStack": [ + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "With array", + "script": [ + "PUSH0", + "NEWARRAY", + "NOT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "NOT", + "evaluationStack": [ + { + "type": "array", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "With struct", + "script": [ + "PUSH0", + "NEWSTRUCT", + "NOT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "NOT", + "evaluationStack": [ + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "With byte array 0x0000", + "script": [ + "PUSHDATA1", + "0x02", + "0x0000", + "NOT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NOT", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "With byte array 0x0001", + "script": [ + "PUSHDATA1", + "0x02", + "0x0001", + "NOT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NOT", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0001" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "With interop", + "script": [ + "SYSCALL", + "0x77777777", + "NOT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "NOT", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/NUMEQUAL.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/NUMEQUAL.json new file mode 100644 index 0000000000..613a2bd31d --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/NUMEQUAL.json @@ -0,0 +1,1058 @@ +{ + "category": "Numeric", + "name": "NUMEQUAL same types", + "tests": [ + { + "name": "Without two pushes", + "script": [ + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one push", + "script": [ + "PUSH0", + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "NUMEQUAL", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [Bool,Bool]=true", + "script": [ + "PUSH0", + "NOT", + "PUSH0", + "NOT", + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NUMEQUAL", + "evaluationStack": [ + { + "type": "boolean", + "value": true + }, + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [Bool,Bool]=false", + "script": [ + "PUSH0", + "NOT", + "PUSH0", + "NOT", + "NOT", + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "NUMEQUAL", + "evaluationStack": [ + { + "type": "boolean", + "value": false + }, + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [Integer,Integer]=true", + "script": [ + "PUSH1", + "PUSH1", + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "NUMEQUAL", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [Integer,Integer]=false", + "script": [ + "PUSH2", + "PUSH1", + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "NUMEQUAL", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [Array,Array]=Fault", + "script": [ + "PUSH0", + "NEWARRAY", + "DUP", + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "NUMEQUAL", + "evaluationStack": [ + { + "type": "array", + "value": [] + }, + { + "type": "array", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Array,Array]=fault", + "script": [ + "PUSH0", + "NEWARRAY", + "PUSH0", + "NEWARRAY", + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NUMEQUAL", + "evaluationStack": [ + { + "type": "array", + "value": [] + }, + { + "type": "array", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Struct,Struct]=fault with clone", + "script": [ + "PUSH0", + "NEWSTRUCT", + "DUP", + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "NUMEQUAL", + "evaluationStack": [ + { + "type": "struct", + "value": [] + }, + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Struct,Struct]=fault without clone", + "script": [ + "PUSH0", + "NEWSTRUCT", + "PUSH0", + "NEWSTRUCT", + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NUMEQUAL", + "evaluationStack": [ + { + "type": "struct", + "value": [] + }, + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Struct,Struct]=fault", + "script": [ + "PUSH0", + "NEWSTRUCT", + "PUSH1", + "NEWSTRUCT", + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NUMEQUAL", + "evaluationStack": [ + { + "type": "struct", + "value": [ + { + "type": "Null" + } + ] + }, + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Map,Map]=Fault", + "script": [ + "NEWMAP", + "DUP", + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "NUMEQUAL", + "evaluationStack": [ + { + "type": "map", + "value": {} + }, + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Map,Map]=Fault", + "script": [ + "NEWMAP", + "NEWMAP", + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "NUMEQUAL", + "evaluationStack": [ + { + "type": "map", + "value": {} + }, + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=true", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "NUMEQUAL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 39, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=false same length", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000124", + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "NUMEQUAL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000124" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 39, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=false different length", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x10", + "0x00000000000000000000000000000124", + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 37, + "nextInstruction": "NUMEQUAL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x00000000000000000000000000000124" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=false different length", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x10", + "0x00000000000000000000000000000124", + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 37, + "nextInstruction": "NUMEQUAL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x00000000000000000000000000000124" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test with clone [Interop,Interop]=fault", + "script": [ + "SYSCALL", + "0x77777777", + "DUP", + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "DUP", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "NUMEQUAL", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + }, + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Interop,Interop]=fault", + "script": [ + "SYSCALL", + "0x77777777", + "SYSCALL", + "0x77777777", + "NUMEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "NUMEQUAL", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + }, + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/NUMNOTEQUAL.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/NUMNOTEQUAL.json new file mode 100644 index 0000000000..386212cfed --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/NUMNOTEQUAL.json @@ -0,0 +1,1058 @@ +{ + "category": "Numeric", + "name": "NUMNOTEQUAL same types", + "tests": [ + { + "name": "Without two pushes", + "script": [ + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one push", + "script": [ + "PUSH0", + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "NUMNOTEQUAL", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [Bool,Bool]=false", + "script": [ + "PUSH0", + "NOT", + "PUSH0", + "NOT", + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NUMNOTEQUAL", + "evaluationStack": [ + { + "type": "boolean", + "value": true + }, + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [Bool,Bool]=true", + "script": [ + "PUSH0", + "NOT", + "PUSH0", + "NOT", + "NOT", + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "NUMNOTEQUAL", + "evaluationStack": [ + { + "type": "boolean", + "value": false + }, + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [Integer,Integer]=false", + "script": [ + "PUSH1", + "PUSH1", + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "NUMNOTEQUAL", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [Integer,Integer]=true", + "script": [ + "PUSH2", + "PUSH1", + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "NUMNOTEQUAL", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [Array,Array]=Fault", + "script": [ + "PUSH0", + "NEWARRAY", + "DUP", + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "NUMNOTEQUAL", + "evaluationStack": [ + { + "type": "array", + "value": [] + }, + { + "type": "array", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Array,Array]=fault", + "script": [ + "PUSH0", + "NEWARRAY", + "PUSH0", + "NEWARRAY", + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NUMNOTEQUAL", + "evaluationStack": [ + { + "type": "array", + "value": [] + }, + { + "type": "array", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Struct,Struct]=fault with clone", + "script": [ + "PUSH0", + "NEWSTRUCT", + "DUP", + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "NUMNOTEQUAL", + "evaluationStack": [ + { + "type": "struct", + "value": [] + }, + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Struct,Struct]=fault without clone", + "script": [ + "PUSH0", + "NEWSTRUCT", + "PUSH0", + "NEWSTRUCT", + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NUMNOTEQUAL", + "evaluationStack": [ + { + "type": "struct", + "value": [] + }, + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Struct,Struct]=fault", + "script": [ + "PUSH0", + "NEWSTRUCT", + "PUSH1", + "NEWSTRUCT", + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NUMNOTEQUAL", + "evaluationStack": [ + { + "type": "struct", + "value": [ + { + "type": "Null" + } + ] + }, + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Map,Map]=Fault", + "script": [ + "NEWMAP", + "DUP", + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "NUMNOTEQUAL", + "evaluationStack": [ + { + "type": "map", + "value": {} + }, + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Map,Map]=Fault", + "script": [ + "NEWMAP", + "NEWMAP", + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "NUMNOTEQUAL", + "evaluationStack": [ + { + "type": "map", + "value": {} + }, + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=false", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "NUMNOTEQUAL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 39, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=true same length", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000124", + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "NUMNOTEQUAL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000124" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 39, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=true different length", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x10", + "0x00000000000000000000000000000124", + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 37, + "nextInstruction": "NUMNOTEQUAL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x00000000000000000000000000000124" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=true different length", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x10", + "0x00000000000000000000000000000124", + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 37, + "nextInstruction": "NUMNOTEQUAL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x00000000000000000000000000000124" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test with clone [Interop,Interop]=fault", + "script": [ + "SYSCALL", + "0x77777777", + "DUP", + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "DUP", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "NUMNOTEQUAL", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + }, + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Interop,Interop]=fault", + "script": [ + "SYSCALL", + "0x77777777", + "SYSCALL", + "0x77777777", + "NUMNOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "NUMNOTEQUAL", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + }, + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/POW.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/POW.json new file mode 100644 index 0000000000..c9a100e2e4 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/POW.json @@ -0,0 +1,195 @@ +{ + "category": "Numeric", + "name": "POW", + "tests": [ + { + "name": "Exception - Without items", + "script": [ + "POW" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "POW", + "evaluationStack": [] + } + ] + } + } + ] + }, + { + "name": "Exception - With only one item", + "script": [ + "PUSH1", + "POW" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "POW", + "evaluationStack": [] + } + ] + } + } + ] + }, + { + "name": "Real test 1", + "script": [ + "PUSH1", + "PUSH1", + "POW" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Real test 1", + "script": [ + "PUSH9", + "PUSH2", + "POW" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 81 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": 81 + } + ] + } + } + ] + }, + { + "name": "Real test 1", + "script": [ + "PUSH2", + "PUSH9", + "POW" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 512 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": 512 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/SHL.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/SHL.json new file mode 100644 index 0000000000..7e660f6c92 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/SHL.json @@ -0,0 +1,245 @@ +{ + "category": "Numeric", + "name": "SHL", + "tests": [ + { + "name": "Exception - Above the limit 0 << 257", + "script": [ + "PUSH0", + "PUSHDATA1", + "0x02", + "0x0101", + "SHL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "SHL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0101" + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Exception - Below the limit 0 << -257", + "script": [ + "PUSH0", + "PUSHDATA1", + "0x02", + "0xfffe", + "SHL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "SHL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0xFFFE" + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test 0 << 256", + "script": [ + "PUSH0", + "PUSHDATA1", + "0x02", + "0x0001", + "SHL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "SHL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0001" + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + } + ] + }, + { + "name": "Real test 16 << 4", + "script": [ + "PUSH16", + "PUSH4", + "SHL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "SHL", + "evaluationStack": [ + { + "type": "integer", + "value": 4 + }, + { + "type": "integer", + "value": 16 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 256 + } + ] + } + } + ] + }, + { + "name": "Real test 16 << 0", + "script": [ + "PUSH16", + "PUSH0", + "SHL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "SHL", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 16 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 16 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/SHR.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/SHR.json new file mode 100644 index 0000000000..818364e92a --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/SHR.json @@ -0,0 +1,247 @@ +{ + "category": "Numeric", + "name": "SHR", + "tests": [ + { + "name": "Exception - Above the limit 0 >> 257", + "script": [ + "PUSH0", + "PUSHDATA1", + "0x02", + "0x0101", + "SHR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "SHR", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0101" + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Exception - Below the limit 0 >> -257", + "script": [ + "PUSH0", + "PUSHDATA1", + "0x02", + "0xfffe", + "SHR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "SHR", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0xFFFE" + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test 0 >> 256", + "script": [ + "PUSH0", + "PUSHDATA1", + "0x02", + "0x0001", + "SHR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "SHR", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0001" + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + } + ] + }, + { + "name": "Real test 256 >> 0", + "script": [ + "PUSHDATA1", + "0x02", + "0x0001", + "PUSH0", + "SHR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "SHR", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "ByteString", + "value": "0x0001" + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "ByteString", + "value": "0x0001" + } + ] + } + } + ] + }, + { + "name": "Real test 4 >> 16", + "script": [ + "PUSH4", + "PUSH16", + "SHR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "SHR", + "evaluationStack": [ + { + "type": "integer", + "value": 16 + }, + { + "type": "integer", + "value": 4 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/SIGN.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/SIGN.json new file mode 100644 index 0000000000..24111873f9 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/SIGN.json @@ -0,0 +1,496 @@ +{ + "category": "Numeric", + "name": "SIGN", + "tests": [ + { + "name": "Without push", + "script": [ + "SIGN" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With bool", + "script": [ + "PUSH1", + "NOT", + "SIGN" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "SIGN", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + } + ] + }, + { + "name": "With integer 0x03", + "script": [ + "PUSH3", + "SIGN" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "SIGN", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "With integer -1", + "script": [ + "PUSHM1", + "SIGN" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "SIGN", + "evaluationStack": [ + { + "type": "integer", + "value": -1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": -1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": -1 + } + ] + } + } + ] + }, + { + "name": "With map", + "script": [ + "NEWMAP", + "SIGN" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "SIGN", + "evaluationStack": [ + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With array", + "script": [ + "PUSH0", + "NEWARRAY", + "SIGN" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "SIGN", + "evaluationStack": [ + { + "type": "array", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With struct", + "script": [ + "PUSH0", + "NEWSTRUCT", + "SIGN" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "SIGN", + "evaluationStack": [ + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With byte array 0x0000", + "script": [ + "PUSHDATA1", + "0x02", + "0x0000", + "SIGN" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "SIGN", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + } + ] + }, + { + "name": "With byte array 0x0001", + "script": [ + "PUSHDATA1", + "0x02", + "0x0001", + "SIGN" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "SIGN", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0001" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "With interop", + "script": [ + "SYSCALL", + "0x77777777", + "SIGN" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "SIGN", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/SQRT.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/SQRT.json new file mode 100644 index 0000000000..8b63674edd --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/SQRT.json @@ -0,0 +1,216 @@ +{ + "category": "Numeric", + "name": "SQRT", + "tests": [ + { + "name": "Exception - Without items", + "script": [ + "SQRT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "SQRT", + "evaluationStack": [] + } + ] + } + } + ] + }, + { + "name": "Real test 1", + "script": [ + "PUSH1", + "SQRT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Real test with 81", + "script": [ + "PUSHINT8", + "0x51", + "SQRT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 9 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": 9 + } + ] + } + } + ] + }, + { + "name": "Real test with 15625", + "script": [ + "PUSHINT16", + "0x093d", + "SQRT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 125 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": 125 + } + ] + } + } + ] + }, + { + "name": "Real test with 4", + "script": [ + "PUSHINT8", + "0x04", + "SQRT" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": 2 + } + ] + } + } + ] + }, + { + "name": "Real test with 2", + "script": [ + "PUSHINT8", + "0x02", + "SQRT" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/APPEND.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/APPEND.json new file mode 100644 index 0000000000..61593452e7 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/APPEND.json @@ -0,0 +1,679 @@ +{ + "category": "Arrays", + "name": "APPEND", + "tests": [ + { + "name": "Without push", + "script": [ + "PUSH1", + "APPEND" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without array", + "script": [ + "PUSH1", + "PUSH2", + "APPEND" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Clone test [Array]", + "script": [ + "INITSSLOT", + "0x01", + "PUSH0", + "NEWARRAY", + "DUP", + "PUSH5", + "APPEND", + "STSFLD0", + "PUSH0", + "NEWARRAY", + "DUP", + "LDSFLD0", + "APPEND", + "LDSFLD0", + "PUSH6", + "APPEND", + "LDSFLD0" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "PUSH0", + "staticFields": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "DUP", + "evaluationStack": [ + { + "type": "array", + "value": [] + } + ], + "staticFields": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 13, + "nextInstruction": "LDSFLD0", + "evaluationStack": [ + { + "type": "array", + "value": [ + { + "type": "Array", + "value": [ + { + "type": "Integer", + "value": "5" + } + ] + } + ] + } + ], + "staticFields": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": "5" + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 17, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": "5" + }, + { + "type": "Integer", + "value": "6" + } + ] + }, + { + "type": "array", + "value": [ + { + "type": "Array", + "value": [ + { + "type": "Integer", + "value": "5" + }, + { + "type": "Integer", + "value": "6" + } + ] + } + ] + } + ], + "staticFields": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": "5" + }, + { + "type": "Integer", + "value": "6" + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": "5" + }, + { + "type": "Integer", + "value": "6" + } + ] + }, + { + "type": "array", + "value": [ + { + "type": "Array", + "value": [ + { + "type": "Integer", + "value": "5" + }, + { + "type": "Integer", + "value": "6" + } + ] + } + ] + } + ] + } + } + ] + }, + { + "name": "Clone test [Struct]", + "script": [ + "INITSSLOT", + "0x01", + "PUSH0", + "NEWSTRUCT", + "DUP", + "PUSH5", + "APPEND", + "STSFLD0", + "PUSH0", + "NEWSTRUCT", + "DUP", + "LDSFLD0", + "APPEND", + "LDSFLD0", + "PUSH6", + "APPEND", + "LDSFLD0" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "PUSH0", + "staticFields": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "DUP", + "evaluationStack": [ + { + "type": "struct", + "value": [] + } + ], + "staticFields": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 13, + "nextInstruction": "LDSFLD0", + "evaluationStack": [ + { + "type": "struct", + "value": [ + { + "type": "Struct", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + ], + "staticFields": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 17, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 5 + }, + { + "type": "Integer", + "value": 6 + } + ] + }, + { + "type": "struct", + "value": [ + { + "type": "Struct", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + ], + "staticFields": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 5 + }, + { + "type": "Integer", + "value": 6 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 5 + }, + { + "type": "Integer", + "value": 6 + } + ] + }, + { + "type": "struct", + "value": [ + { + "type": "Struct", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [Array]", + "script": [ + "PUSH0", + "NEWARRAY", + "DUP", + "PUSH5", + "APPEND" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "APPEND", + "evaluationStack": [ + { + "type": "integer", + "value": 5 + }, + { + "type": "array", + "value": [] + }, + { + "type": "array", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": "5" + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": "5" + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [Struct]", + "script": [ + "PUSH0", + "NEWSTRUCT", + "DUP", + "PUSH5", + "APPEND" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "APPEND", + "evaluationStack": [ + { + "type": "integer", + "value": 5 + }, + { + "type": "struct", + "value": [] + }, + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": "5" + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": "5" + } + ] + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/CLEARITEMS.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/CLEARITEMS.json new file mode 100644 index 0000000000..20f82d0a34 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/CLEARITEMS.json @@ -0,0 +1,126 @@ +{ + "category": "Arrays", + "name": "CLEARITEMS", + "tests": [ + { + "name": "Without push", + "script": [ + "CLEARITEMS" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong type", + "script": [ + "PUSH2", + "CLEARITEMS" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Array]", + "script": [ + "PUSH0", + "PUSH1", + "PUSH2", + "PACK", + "DUP", + "CLEARITEMS" + ], + "steps": [ + { + "actions": [ + "StepInto", + "StepInto", + "StepInto", + "StepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "DUP", + "evaluationStack": [ + { + "type": "Array", + "value": [ + { + "type": "Integer", + "value": 1 + }, + { + "type": "Integer", + "value": 0 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Array", + "value": [] + } + ] + } + } + ] + }, + { + "name": "Real test [Map]", + "script": [ + "NEWMAP", + "DUP", + "PUSH0", + "PUSH0", + "SETITEM", + "DUP", + "CLEARITEMS" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Map", + "value": {} + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/HASKEY.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/HASKEY.json new file mode 100644 index 0000000000..213087bd29 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/HASKEY.json @@ -0,0 +1,276 @@ +{ + "category": "Arrays", + "name": "HASKEY", + "tests": [ + { + "name": "Without push", + "script": [ + "HASKEY" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong key type", + "script": [ + "PUSH1", + "NEWARRAY", + "NEWMAP", + "HASKEY" + ], + "steps": [ + { + "actions": [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "HASKEY", + "evaluationStack": [ + { + "type": "Map", + "value": {} + }, + { + "type": "Array", + "value": [ + { + "type": "Null" + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Buffer]", + "script": [ + "PUSH2", + "NEWBUFFER", + "PUSH0", + "HASKEY" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [Map]", + "script": [ + "INITSSLOT", + "0x01", + "NEWMAP", + "DUP", + "STSFLD0", + "PUSH1", + "PUSH2", + "SETITEM", + "LDSFLD0", + "PUSH3", + "HASKEY", + "DROP", + "LDSFLD0", + "PUSH1", + "HASKEY" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "HASKEY", + "evaluationStack": [ + { + "type": "Integer", + "value": 3 + }, + { + "type": "map", + "value": { + "0x01": { + "type": "Integer", + "value": 2 + } + } + } + ], + "staticFields": [ + { + "type": "map", + "value": { + "0x01": { + "type": "Integer", + "value": 2 + } + } + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 11, + "nextInstruction": "DROP", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ], + "staticFields": [ + { + "type": "map", + "value": { + "0x01": { + "type": "Integer", + "value": 2 + } + } + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Check key size [Map]", + "script": [ + "NEWMAP", + "PUSHINT8", + "0x41", + "NEWBUFFER", + "CONVERT", + "0x28", + "HASKEY" + ], + "steps": [ + { + "actions": [ + "stepinto", + "stepinto", + "stepinto", + "stepinto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "HASKEY", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepinto" + ], + "result": { + "state": "FAULT", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "HASKEY", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "type": "map", + "value": {} + } + ] + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/KEYS.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/KEYS.json new file mode 100644 index 0000000000..fb6a9d985c --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/KEYS.json @@ -0,0 +1,63 @@ +{ + "category": "Arrays", + "name": "KEYS", + "tests": [ + { + "name": "Keys in map", + "script": [ + "NEWMAP", + "DUP", + "DUP", + "PUSH0", + "PUSH0", + "SETITEM", + "PUSH1", + "PUSH1", + "SETITEM", + "KEYS" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 0 + }, + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + } + ] + }, + { + "name": "Invalid StackItem [Integer!=Map]", + "script": [ + "PUSH0", + "KEYS" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/NEWARRAY.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/NEWARRAY.json new file mode 100644 index 0000000000..5244c72f4e --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/NEWARRAY.json @@ -0,0 +1,168 @@ +{ + "category": "Arrays", + "name": "NEWARRAY", + "tests": [ + { + "name": "Without push", + "script": [ + "NEWARRAY" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With wrong type", + "script": [ + "NEWMAP", + "NEWARRAY" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With negative push", + "script": [ + "PUSHM1", + "NEWARRAY" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH2", + "NEWARRAY" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "array", + "value": [ + { + "type": "Null" + }, + { + "type": "Null" + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "array", + "value": [ + { + "type": "Null" + }, + { + "type": "Null" + } + ] + } + ] + } + } + ] + }, + { + "name": "Clone Struct to Array", + "script": [ + "PUSH1", + "NEWSTRUCT", + "DUP", + "PUSH0", + "PUSH5", + "SETITEM", + "NEWARRAY" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "NEWARRAY", + "evaluationStack": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/NEWARRAY0.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/NEWARRAY0.json new file mode 100644 index 0000000000..21cd6436e2 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/NEWARRAY0.json @@ -0,0 +1,28 @@ +{ + "category": "Arrays", + "name": "NEWARRAY0", + "tests": [ + { + "name": "Real test", + "script": [ + "NEWARRAY0" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Array", + "value": [] + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/NEWARRAY_T.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/NEWARRAY_T.json new file mode 100644 index 0000000000..d1cea504ad --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/NEWARRAY_T.json @@ -0,0 +1,289 @@ +{ + "category": "Arrays", + "name": "NEWARRAY_T", + "tests": [ + { + "name": "Real test [Any]", + "script": [ + "PUSH2", + "NEWARRAY_T", + "0x00" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Array", + "value": [ + { + "type": "Null" + }, + { + "type": "Null" + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [Pointer]", + "script": [ + "PUSH2", + "NEWARRAY_T", + "0x10" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Array", + "value": [ + { + "type": "Null" + }, + { + "type": "Null" + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [Integer]", + "script": [ + "PUSH2", + "NEWARRAY_T", + "0x21" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Array", + "value": [ + { + "type": "Integer", + "value": 0 + }, + { + "type": "Integer", + "value": 0 + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString]", + "script": [ + "PUSH2", + "NEWARRAY_T", + "0x28" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Array", + "value": [ + { + "type": "ByteString", + "value": "0x" + }, + { + "type": "ByteString", + "value": "0x" + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [Buffer]", + "script": [ + "PUSH2", + "NEWARRAY_T", + "0x30" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Array", + "value": [ + { + "type": "Null" + }, + { + "type": "Null" + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [Array]", + "script": [ + "PUSH2", + "NEWARRAY_T", + "0x40" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Array", + "value": [ + { + "type": "Null" + }, + { + "type": "Null" + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [Struct]", + "script": [ + "PUSH2", + "NEWARRAY_T", + "0x41" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Array", + "value": [ + { + "type": "Null" + }, + { + "type": "Null" + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [Map]", + "script": [ + "PUSH2", + "NEWARRAY_T", + "0x48" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Array", + "value": [ + { + "type": "Null" + }, + { + "type": "Null" + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [InteropInterface]", + "script": [ + "PUSH2", + "NEWARRAY_T", + "0x60" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Array", + "value": [ + { + "type": "Null" + }, + { + "type": "Null" + } + ] + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/NEWMAP.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/NEWMAP.json new file mode 100644 index 0000000000..a7000735d5 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/NEWMAP.json @@ -0,0 +1,48 @@ +{ + "category": "Arrays", + "name": "NEWMAP", + "tests": [ + { + "name": "Real test", + "script": [ + "NEWMAP" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "map", + "value": {} + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/NEWSTRUCT.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/NEWSTRUCT.json new file mode 100644 index 0000000000..dbe9547dde --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/NEWSTRUCT.json @@ -0,0 +1,168 @@ +{ + "category": "Arrays", + "name": "NEWSTRUCT", + "tests": [ + { + "name": "Without push", + "script": [ + "NEWSTRUCT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With wrong type", + "script": [ + "NEWMAP", + "NEWSTRUCT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With negative push", + "script": [ + "PUSHM1", + "NEWSTRUCT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH2", + "NEWSTRUCT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "struct", + "value": [ + { + "type": "Null" + }, + { + "type": "Null" + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "struct", + "value": [ + { + "type": "Null" + }, + { + "type": "Null" + } + ] + } + ] + } + } + ] + }, + { + "name": "Clone Array to Struct", + "script": [ + "PUSH1", + "NEWARRAY", + "DUP", + "PUSH0", + "PUSH5", + "SETITEM", + "NEWSTRUCT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "NEWSTRUCT", + "evaluationStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/NEWSTRUCT0.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/NEWSTRUCT0.json new file mode 100644 index 0000000000..62d5a595d3 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/NEWSTRUCT0.json @@ -0,0 +1,28 @@ +{ + "category": "Arrays", + "name": "NEWSTRUCT0", + "tests": [ + { + "name": "Real test", + "script": [ + "NEWSTRUCT0" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Struct", + "value": [] + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/PACK.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/PACK.json new file mode 100644 index 0000000000..e4cc0f40ee --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/PACK.json @@ -0,0 +1,127 @@ +{ + "category": "Arrays", + "name": "PACK", + "tests": [ + { + "name": "Real test", + "script": [ + "PUSH5", + "PUSH6", + "PUSH2", + "PACK" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 6 + }, + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + } + ] + }, + { + "name": "Not enough size", + "script": [ + "PUSH5", + "PUSH2", + "PACK" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong type [Map]", + "script": [ + "NEWMAP", + "PACK" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong type [Array]", + "script": [ + "PUSH1", + "NEWARRAY", + "PACK" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong type [Struct]", + "script": [ + "PUSH1", + "NEWSTRUCT", + "PACK" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without items", + "script": [ + "PACK" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/PACKMAP.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/PACKMAP.json new file mode 100644 index 0000000000..dd68bd19c1 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/PACKMAP.json @@ -0,0 +1,127 @@ +{ + "category": "Arrays", + "name": "PACKMAP", + "tests": [ + { + "name": "Real test", + "script": [ + "PUSH5", + "PUSH6", + "PUSH1", + "PACKMAP" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Map", + "value": { + "0x06": { + "type": "Integer", + "value": "5" + } + } + } + ] + } + } + ] + }, + { + "name": "Not enough size", + "script": [ + "PUSH5", + "PUSH1", + "PACKMAP" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong type [Map]", + "script": [ + "PUSH1", + "PUSH1", + "NEWMAP", + "PACKMAP" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong type [Array]", + "script": [ + "PUSH1", + "PUSH1", + "NEWARRAY", + "PACKMAP" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong type [Struct]", + "script": [ + "PUSH1", + "PUSH1", + "NEWSTRUCT", + "PACKMAP" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without items", + "script": [ + "PACKMAP" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/PACKSTRUCT.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/PACKSTRUCT.json new file mode 100644 index 0000000000..e0517d27b9 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/PACKSTRUCT.json @@ -0,0 +1,127 @@ +{ + "category": "Arrays", + "name": "PACK", + "tests": [ + { + "name": "Real test", + "script": [ + "PUSH5", + "PUSH6", + "PUSH2", + "PACKSTRUCT" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 6 + }, + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + } + ] + }, + { + "name": "Not enough size", + "script": [ + "PUSH5", + "PUSH2", + "PACKSTRUCT" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong type [Map]", + "script": [ + "NEWMAP", + "PACKSTRUCT" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong type [Array]", + "script": [ + "PUSH1", + "NEWARRAY", + "PACKSTRUCT" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong type [Struct]", + "script": [ + "PUSH1", + "NEWSTRUCT", + "PACKSTRUCT" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without items", + "script": [ + "PACKSTRUCT" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/PICKITEM.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/PICKITEM.json new file mode 100644 index 0000000000..19a6f54a8c --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/PICKITEM.json @@ -0,0 +1,714 @@ +{ + "category": "Arrays", + "name": "PICKITEM", + "tests": [ + { + "name": "Wrong array", + "script": [ + "PUSH0", + "PUSH0", + "PICKITEM" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without push", + "script": [ + "PICKITEM" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong key type", + "script": [ + "PUSH1", + "NEWARRAY", + "NEWMAP", + "PICKITEM" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "PICKITEM", + "evaluationStack": [ + { + "type": "map", + "value": {} + }, + { + "type": "array", + "value": [ + { + "type": "Null" + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Out of bounds [Array]", + "script": [ + "PUSH2", + "NEWARRAY", + "PUSH3", + "PICKITEM" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "PICKITEM", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "array", + "value": [ + { + "type": "Null" + }, + { + "type": "Null" + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Out of bounds [Struct]", + "script": [ + "PUSH2", + "NEWSTRUCT", + "PUSH3", + "PICKITEM" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "PICKITEM", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "struct", + "value": [ + { + "type": "Null" + }, + { + "type": "Null" + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Array]", + "script": [ + "INITSSLOT", + "0x01", + "PUSH3", + "NEWARRAY", + "STSFLD0", + "LDSFLD0", + "PUSH0", + "PUSH1", + "SETITEM", + "LDSFLD0", + "PUSH1", + "PUSH2", + "SETITEM", + "LDSFLD0", + "PUSH2", + "PUSH3", + "SETITEM", + "LDSFLD0", + "PUSH2", + "PICKITEM" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 19, + "nextInstruction": "PICKITEM", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 1 + }, + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 3 + } + ] + } + ], + "staticFields": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 1 + }, + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 3 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 20, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + } + ], + "staticFields": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 1 + }, + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 3 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 3 + } + ] + } + } + ] + }, + { + "name": "Real test [Struct]", + "script": [ + "INITSSLOT", + "0x01", + "PUSH3", + "NEWSTRUCT", + "STSFLD0", + "LDSFLD0", + "PUSH0", + "PUSH1", + "SETITEM", + "LDSFLD0", + "PUSH1", + "PUSH2", + "SETITEM", + "LDSFLD0", + "PUSH2", + "PUSH3", + "SETITEM", + "LDSFLD0", + "PUSH2", + "PICKITEM" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 19, + "nextInstruction": "PICKITEM", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 1 + }, + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 3 + } + ] + } + ], + "staticFields": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 1 + }, + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 3 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 20, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + } + ], + "staticFields": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 1 + }, + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 3 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 3 + } + ] + } + } + ] + }, + { + "name": "OutOfBounds with -1 [ByteString]", + "script": [ + "PUSHDATA1", + "0x02", + "0x0102", + "PUSHM1", + "PICKITEM" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "PICKITEM", + "evaluationStack": [ + { + "type": "integer", + "value": -1 + }, + { + "type": "ByteString", + "value": "0x0102" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "OutOfBounds with more than length [ByteString]", + "script": [ + "PUSHDATA1", + "0x02", + "0x0102", + "PUSH4", + "PICKITEM" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "PICKITEM", + "evaluationStack": [ + { + "type": "integer", + "value": 4 + }, + { + "type": "ByteString", + "value": "0x0102" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [ByteString]", + "script": [ + "PUSHDATA1", + "0x02", + "0x0102", + "PUSH0", + "PICKITEM" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "PICKITEM", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "ByteString", + "value": "0x0102" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Real test [Buffer]", + "script": [ + "PUSHDATA1", + "0x02", + "0x0102", + "CONVERT", + "0x30", + "PUSH0", + "PICKITEM" + ], + "steps": [ + { + "actions": [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "PICKITEM", + "evaluationStack": [ + { + "type": "Integer", + "value": 0 + }, + { + "type": "Buffer", + "value": "0x0102" + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/REMOVE.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/REMOVE.json new file mode 100644 index 0000000000..cba3a4d0a7 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/REMOVE.json @@ -0,0 +1,299 @@ +{ + "category": "Arrays", + "name": "REMOVE", + "tests": [ + { + "name": "Without push", + "script": [ + "PUSH1", + "REMOVE" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without array", + "script": [ + "PUSH1", + "PUSH2", + "REMOVE" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong key", + "script": [ + "PUSH2", + "NEWMAP", + "REMOVE" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Out of bounds", + "script": [ + "PUSH6", + "PUSH5", + "PUSH2", + "PACK", + "PUSH2", + "REMOVE" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Array]", + "script": [ + "INITSSLOT", + "0x01", + "PUSH6", + "PUSH5", + "PUSH2", + "PACK", + "STSFLD0", + "LDSFLD0", + "PUSH0", + "REMOVE", + "LDSFLD0", + "UNPACK" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 9, + "nextInstruction": "REMOVE", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 5 + }, + { + "type": "Integer", + "value": 6 + } + ] + } + ], + "staticFields": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 5 + }, + { + "type": "Integer", + "value": 6 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "LDSFLD0", + "staticFields": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 6 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 6 + } + ] + } + } + ] + }, + { + "name": "Real test [Struct]", + "script": [ + "INITSSLOT", + "0x01", + "PUSH0", + "NEWSTRUCT", + "DUP", + "PUSH5", + "APPEND", + "STSFLD0", + "LDSFLD0", + "PUSH0", + "REMOVE", + "LDSFLD0", + "UNPACK" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "REMOVE", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ], + "staticFields": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 11, + "nextInstruction": "LDSFLD0", + "staticFields": [ + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/REVERSEITEMS.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/REVERSEITEMS.json new file mode 100644 index 0000000000..b4dec58eaf --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/REVERSEITEMS.json @@ -0,0 +1,138 @@ +{ + "category": "Arrays", + "name": "REVERSEITEMS", + "tests": [ + { + "name": "Without push", + "script": [ + "REVERSEITEMS" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without Array", + "script": [ + "PUSH9", + "REVERSEITEMS" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Array]", + "script": [ + "PUSH9", + "PUSH8", + "PUSH2", + "PACK", + "DUP", + "REVERSEITEMS" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 9 + }, + { + "type": "Integer", + "value": 8 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 9 + }, + { + "type": "Integer", + "value": 8 + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [Buffer]", + "script": [ + "PUSHDATA1", + "0x03", + "0x010203", + "CONVERT", + "0x30", + "DUP", + "REVERSEITEMS" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Buffer", + "value": "0x030201" + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/SETITEM.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/SETITEM.json new file mode 100644 index 0000000000..0ed33f2968 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/SETITEM.json @@ -0,0 +1,1226 @@ +{ + "category": "Arrays", + "name": "SETITEM", + "tests": [ + { + "name": "Map in key", + "script": [ + "PUSH1", + "NEWMAP", + "PUSH0", + "SETITEM" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "SETITEM", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "map", + "value": {} + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without array", + "script": [ + "PUSH0", + "PUSH0", + "PUSH0", + "SETITEM" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "SETITEM", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without push", + "script": [ + "SETITEM" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Out of bounds [Array]", + "script": [ + "PUSH1", + "NEWARRAY", + "PUSH1", + "PUSH5", + "SETITEM" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "SETITEM", + "evaluationStack": [ + { + "type": "integer", + "value": 5 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "array", + "value": [ + { + "type": "Null" + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Out of bounds [Struct]", + "script": [ + "PUSH1", + "NEWSTRUCT", + "PUSH1", + "PUSH5", + "SETITEM" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "SETITEM", + "evaluationStack": [ + { + "type": "integer", + "value": 5 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "struct", + "value": [ + { + "type": "Null" + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Map]", + "script": [ + "INITSSLOT", + "0x01", + "NEWMAP", + "DUP", + "STSFLD0", + "PUSH1", + "PUSH2", + "SETITEM", + "LDSFLD0", + "PUSH3", + "PUSH4", + "SETITEM", + "LDSFLD0", + "PUSH1", + "PUSH2", + "SETITEM", + "LDSFLD0", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "map", + "value": {} + } + ], + "staticFields": [ + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "SETITEM", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "map", + "value": {} + } + ], + "staticFields": [ + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "LDSFLD0", + "staticFields": [ + { + "type": "map", + "value": { + "0x01": { + "type": "Integer", + "value": 2 + } + } + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 9, + "nextInstruction": "PUSH3", + "evaluationStack": [ + { + "type": "map", + "value": { + "0x01": { + "type": "Integer", + "value": 2 + } + } + } + ], + "staticFields": [ + { + "type": "map", + "value": { + "0x01": { + "type": "Integer", + "value": 2 + } + } + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 13, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "map", + "value": { + "0x01": { + "type": "Integer", + "value": 2 + }, + "0x03": { + "type": "Integer", + "value": 4 + } + } + } + ], + "staticFields": [ + { + "type": "map", + "value": { + "0x01": { + "type": "Integer", + "value": 2 + }, + "0x03": { + "type": "Integer", + "value": 4 + } + } + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "map", + "value": { + "0x01": { + "type": "Integer", + "value": 2 + }, + "0x03": { + "type": "Integer", + "value": 4 + } + } + } + ] + } + } + ] + }, + { + "name": "Real test [Array]", + "script": [ + "PUSH1", + "NEWARRAY", + "DUP", + "PUSH0", + "PUSH5", + "SETITEM" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "SETITEM", + "evaluationStack": [ + { + "type": "integer", + "value": 5 + }, + { + "type": "integer", + "value": 0 + }, + { + "type": "array", + "value": [ + { + "type": "Null" + } + ] + }, + { + "type": "array", + "value": [ + { + "type": "Null" + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [Struct]", + "script": [ + "PUSH1", + "NEWSTRUCT", + "DUP", + "PUSH0", + "PUSH5", + "SETITEM" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "SETITEM", + "evaluationStack": [ + { + "type": "integer", + "value": 5 + }, + { + "type": "integer", + "value": 0 + }, + { + "type": "struct", + "value": [ + { + "type": "Null" + } + ] + }, + { + "type": "struct", + "value": [ + { + "type": "Null" + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + } + ] + }, + { + "name": "Clone test [Array]", + "script": [ + "INITSSLOT", + "0x01", + "PUSH1", + "NEWARRAY", + "DUP", + "PUSH0", + "PUSH5", + "SETITEM", + "STSFLD0", + "LDSFLD0", + "DUP", + "PUSH0", + "PUSH4", + "SETITEM", + "LDSFLD0" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "PUSH0", + "evaluationStack": [ + { + "type": "array", + "value": [ + { + "type": "Null" + } + ] + }, + { + "type": "array", + "value": [ + { + "type": "Null" + } + ] + } + ], + "staticFields": [ + { + "type": "Null" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "STSFLD0", + "evaluationStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ], + "staticFields": [ + { + "type": "Null" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 11, + "nextInstruction": "PUSH0", + "evaluationStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + }, + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ], + "staticFields": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 14, + "nextInstruction": "LDSFLD0", + "evaluationStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 4 + } + ] + } + ], + "staticFields": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 4 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 15, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 4 + } + ] + }, + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 4 + } + ] + } + ], + "staticFields": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 4 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 4 + } + ] + }, + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 4 + } + ] + } + ] + } + } + ] + }, + { + "name": "Clone test [Struct]", + "script": [ + "INITSSLOT", + "0x01", + "PUSH1", + "NEWSTRUCT", + "DUP", + "PUSH0", + "PUSH5", + "SETITEM", + "STSFLD0", + "LDSFLD0", + "DUP", + "PUSH0", + "PUSH4", + "SETITEM", + "LDSFLD0" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "PUSH0", + "evaluationStack": [ + { + "type": "struct", + "value": [ + { + "type": "Null" + } + ] + }, + { + "type": "struct", + "value": [ + { + "type": "Null" + } + ] + } + ], + "staticFields": [ + { + "type": "Null" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "STSFLD0", + "evaluationStack": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ], + "staticFields": [ + { + "type": "Null" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 11, + "nextInstruction": "PUSH0", + "evaluationStack": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + }, + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ], + "staticFields": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 14, + "nextInstruction": "LDSFLD0", + "evaluationStack": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 4 + } + ] + } + ], + "staticFields": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 4 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 15, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 4 + } + ] + }, + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 4 + } + ] + } + ], + "staticFields": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 4 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 4 + } + ] + }, + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 4 + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test [Buffer]", + "script": [ + "PUSHDATA1", + "0x02", + "0x0102", + "CONVERT", + "0x30", + "DUP", + "PUSH0", + "PUSH5", + "SETITEM" + ], + "steps": [ + { + "actions": [ + "StepInto", + "StepInto", + "StepInto", + "StepInto", + "StepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 9, + "nextInstruction": "SETITEM", + "evaluationStack": [ + { + "type": "Integer", + "value": 5 + }, + { + "type": "Integer", + "value": 0 + }, + { + "type": "Buffer", + "value": "0x0102" + }, + { + "type": "Buffer", + "value": "0x0102" + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Buffer", + "value": "0x0502" + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/SIZE.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/SIZE.json new file mode 100644 index 0000000000..fa2907411d --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/SIZE.json @@ -0,0 +1,266 @@ +{ + "category": "Arrays", + "name": "SIZE", + "tests": [ + { + "name": "Wrong type SYSCALL[0x77777777]+SIZE", + "script": [ + "SYSCALL", + "0x77777777", + "SIZE" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "SIZE", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without PUSH+SIZE", + "script": [ + "SIZE" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test with ByteString", + "script": [ + "PUSHDATA1", + "0x01", + "0x00", + "SIZE" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Real test with Buffer", + "script": [ + "PUSH10", + "NEWBUFFER", + "SIZE" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 10 + } + ] + } + } + ] + }, + { + "name": "Real test with array", + "script": [ + "PUSH3", + "NEWARRAY", + "SIZE" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 3 + } + ] + } + } + ] + }, + { + "name": "Real test with struct", + "script": [ + "PUSH3", + "NEWSTRUCT", + "SIZE" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 3 + } + ] + } + } + ] + }, + { + "name": "Real test with map", + "script": [ + "NEWMAP", + "SIZE" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/UNPACK.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/UNPACK.json new file mode 100644 index 0000000000..63c92aad3a --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/UNPACK.json @@ -0,0 +1,168 @@ +{ + "category": "Arrays", + "name": "UNPACK", + "tests": [ + { + "name": "Without array", + "script": [ + "PUSH10", + "UNPACK" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without push", + "script": [ + "UNPACK" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without push", + "script": [ + "PUSH5", + "PUSH6", + "PUSH2", + "PACK", + "UNPACK" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "UNPACK", + "evaluationStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 6 + }, + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 6 + }, + { + "type": "integer", + "value": 5 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 6 + }, + { + "type": "integer", + "value": 5 + } + ] + } + } + ] + }, + { + "name": "With map", + "script": [ + "PUSH5", + "PUSH6", + "PUSH1", + "PACKMAP", + "UNPACK" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + }, + { + "type": "Integer", + "value": 6 + }, + { + "type": "Integer", + "value": 5 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/VALUES.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/VALUES.json new file mode 100644 index 0000000000..4cf832eb59 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arrays/VALUES.json @@ -0,0 +1,194 @@ +{ + "category": "Arrays", + "name": "VALUES", + "tests": [ + { + "name": "No StackItem", + "script": [ + "KEYS" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Invalid StackItem [Integer != (Map|Array|Struct)]", + "script": [ + "PUSH0", + "KEYS" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Values in map", + "script": [ + "NEWMAP", + "DUP", + "DUP", + "PUSH0", + "PUSH2", + "SETITEM", + "PUSH1", + "PUSH4", + "SETITEM", + "VALUES" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 4 + } + ] + } + ] + } + } + ] + }, + { + "name": "Simple values in array", + "script": [ + "PUSH2", + "NEWARRAY_T", + "0x00", + "VALUES" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "array", + "value": [ + { + "type": "Null" + }, + { + "type": "Null" + } + ] + } + ] + } + } + ] + }, + { + "name": "Compound value in array [inner struct]", + "script": [ + "PUSH1", + "NEWARRAY", + "DUP", + "PUSH0", + "PUSH2", + "NEWSTRUCT", + "SETITEM", + "VALUES" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "array", + "value": [ + { + "type": "struct", + "value": [ + { + "type": "Null" + }, + { + "type": "Null" + } + ] + } + ] + } + ] + } + } + ] + }, + { + "name": "Compound value in array [inner map]", + "script": [ + "PUSH1", + "NEWARRAY", + "DUP", + "PUSH0", + "NEWMAP", + "DUP", + "PUSH0", + "PUSH1", + "SETITEM", + "SETITEM", + "VALUES" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "array", + "value": [ + { + "type": "map", + "value": { + "": { + "type": "Integer", + "value": 1 + } + } + } + ] + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/BitwiseLogic/AND.json b/tests/Neo.VM.Tests/Tests/OpCodes/BitwiseLogic/AND.json new file mode 100644 index 0000000000..f1e1cc7d14 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/BitwiseLogic/AND.json @@ -0,0 +1,811 @@ +{ + "category": "Bitwise Logic", + "name": "AND", + "tests": [ + { + "name": "Without two pushes", + "script": [ + "AND" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one push", + "script": [ + "PUSH0", + "AND" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "AND", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [true,true]=1", + "script": [ + "PUSH0", + "NOT", + "PUSH0", + "NOT", + "AND" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "AND", + "evaluationStack": [ + { + "type": "boolean", + "value": true + }, + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Real test [false,true]=0", + "script": [ + "PUSH0", + "NOT", + "PUSH0", + "NOT", + "NOT", + "AND" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "AND", + "evaluationStack": [ + { + "type": "boolean", + "value": false + }, + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 0 + } + ] + } + } + ] + }, + { + "name": "Real test [1,1]=1", + "script": [ + "PUSH1", + "PUSH1", + "AND" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "AND", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Real test [1,2]=0", + "script": [ + "PUSH2", + "PUSH1", + "AND" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "AND", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 0 + } + ] + } + } + ] + }, + { + "name": "Real test [Array,Array]=FAULT", + "script": [ + "PUSH0", + "NEWARRAY", + "DUP", + "AND" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "AND", + "evaluationStack": [ + { + "type": "array", + "value": [] + }, + { + "type": "array", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Struct,Struct]=FAULT", + "script": [ + "PUSH0", + "NEWSTRUCT", + "DUP", + "AND" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "AND", + "evaluationStack": [ + { + "type": "struct", + "value": [] + }, + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Map,Map]=FAULT", + "script": [ + "NEWMAP", + "DUP", + "AND" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "AND", + "evaluationStack": [ + { + "type": "map", + "value": {} + }, + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=11911212070228631137091015067172167745536", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "AND" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "AND", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 39, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": "11911212070228631137091015067172167745536" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": "11911212070228631137091015067172167745536" + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=10890364969465815746700891244876863111168", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000124", + "AND" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "AND", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000124" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 39, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": "10890364969465815746700891244876863111168" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": "10890364969465815746700891244876863111168" + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=0", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x10", + "0x00000000000000000000000000000124", + "AND" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 37, + "nextInstruction": "AND", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x00000000000000000000000000000124" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 0 + } + ] + } + } + ] + }, + { + "name": "Real test with clone [Interop,Interop]=FAULT", + "script": [ + "SYSCALL", + "0x77777777", + "DUP", + "AND" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "DUP", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "AND", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + }, + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Buffer,Buffer]=FAULT", + "script": [ + "PUSHDATA1", + "0x01AA", + "CONVERT", + "0x30", + "PUSHDATA1", + "0x01BB", + "CONVERT", + "0x30", + "AND" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "AND", + "evaluationStack": [ + { + "type": "Buffer", + "value": "0xBB" + }, + { + "type": "Buffer", + "value": "0xAA" + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/BitwiseLogic/EQUAL.json b/tests/Neo.VM.Tests/Tests/OpCodes/BitwiseLogic/EQUAL.json new file mode 100644 index 0000000000..75a484d0db --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/BitwiseLogic/EQUAL.json @@ -0,0 +1,1447 @@ +{ + "category": "Bitwise Logic", + "name": "EQUAL same types", + "tests": [ + { + "name": "Without two pushes", + "script": [ + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one push", + "script": [ + "PUSH0", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Bool,Bool]=true", + "script": [ + "PUSH0", + "NOT", + "PUSH0", + "NOT", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "boolean", + "value": true + }, + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [Bool,Bool]=false", + "script": [ + "PUSH0", + "NOT", + "PUSH0", + "NOT", + "NOT", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "boolean", + "value": false + }, + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [Integer,Integer]=true", + "script": [ + "PUSH1", + "PUSH1", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [Integer,Integer]=false", + "script": [ + "PUSH2", + "PUSH1", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [Array,Array]=true", + "script": [ + "PUSH0", + "NEWARRAY", + "DUP", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "array", + "value": [] + }, + { + "type": "array", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [Array,Array]=false", + "script": [ + "PUSH0", + "NEWARRAY", + "PUSH0", + "NEWARRAY", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "array", + "value": [] + }, + { + "type": "array", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [Struct,Struct]=true with clone", + "script": [ + "PUSH0", + "NEWSTRUCT", + "DUP", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "struct", + "value": [] + }, + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [Struct,Struct]=true without clone", + "script": [ + "PUSH0", + "NEWSTRUCT", + "PUSH0", + "NEWSTRUCT", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "struct", + "value": [] + }, + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [Struct,Struct]=false", + "script": [ + "PUSH0", + "NEWSTRUCT", + "PUSH1", + "NEWSTRUCT", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "struct", + "value": [ + { + "type": "Null" + } + ] + }, + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [Map,Map]=true", + "script": [ + "NEWMAP", + "DUP", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "map", + "value": {} + }, + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [Map,Map]=false", + "script": [ + "NEWMAP", + "NEWMAP", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "map", + "value": {} + }, + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=true", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 39, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=false same length", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000124", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000124" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 39, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=false different length", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x10", + "0x00000000000000000000000000000124", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 37, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x00000000000000000000000000000124" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=false different length", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x10", + "0x00000000000000000000000000000124", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 37, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x00000000000000000000000000000124" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test with clone [Interop,Interop]=true", + "script": [ + "SYSCALL", + "0x77777777", + "DUP", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "DUP", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + }, + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [Interop,Interop]=false", + "script": [ + "SYSCALL", + "0x77777777", + "SYSCALL", + "0x77777777", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + }, + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [Buffer,Buffer]=false", + "script": [ + "PUSHDATA1", + "0x01AA", + "CONVERT", + "0x30", + "PUSHDATA1", + "0x01BB", + "CONVERT", + "0x30", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "Buffer", + "value": "0xBB" + }, + { + "type": "Buffer", + "value": "0xAA" + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [Buffer,Buffer]=false", + "script": [ + "PUSHDATA1", + "0x01AA", + "CONVERT", + "0x30", + "PUSHDATA1", + "0x01AA", + "CONVERT", + "0x30", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "Buffer", + "value": "0xAA" + }, + { + "type": "Buffer", + "value": "0xAA" + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [Buffer,Buffer]=true", + "script": [ + "PUSHDATA1", + "0x01AA", + "CONVERT", + "0x30", + "DUP", + "EQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "EQUAL", + "evaluationStack": [ + { + "type": "Buffer", + "value": "0xAA" + }, + { + "type": "Buffer", + "value": "0xAA" + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/BitwiseLogic/INVERT.json b/tests/Neo.VM.Tests/Tests/OpCodes/BitwiseLogic/INVERT.json new file mode 100644 index 0000000000..636e08628b --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/BitwiseLogic/INVERT.json @@ -0,0 +1,657 @@ +{ + "category": "Bitwise Logic", + "name": "INVERT", + "tests": [ + { + "name": "Without two pushes", + "script": [ + "INVERT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With 0", + "script": [ + "PUSH0", + "INVERT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "INVERT", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": -1 + } + ] + } + } + ] + }, + { + "name": "Real test [true]=-2", + "script": [ + "PUSH0", + "NOT", + "INVERT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "INVERT", + "evaluationStack": [ + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": -2 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": -2 + } + ] + } + } + ] + }, + { + "name": "Real test [false]=-1", + "script": [ + "PUSH0", + "NOT", + "NOT", + "INVERT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "INVERT", + "evaluationStack": [ + { + "type": "boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": -1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": -1 + } + ] + } + } + ] + }, + { + "name": "Real test [1]=-2", + "script": [ + "PUSH1", + "INVERT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "INVERT", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": -2 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": -2 + } + ] + } + } + ] + }, + { + "name": "Real test [2]=-3", + "script": [ + "PUSH2", + "INVERT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "INVERT", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": -3 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": -3 + } + ] + } + } + ] + }, + { + "name": "Real test [Array]=FAULT", + "script": [ + "PUSH0", + "NEWARRAY", + "INVERT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "INVERT", + "evaluationStack": [ + { + "type": "array", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Struct]=FAULT", + "script": [ + "PUSH0", + "NEWSTRUCT", + "INVERT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "INVERT", + "evaluationStack": [ + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Map]=FAULT", + "script": [ + "NEWMAP", + "INVERT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "INVERT", + "evaluationStack": [ + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [ByteString]=-11911212070228631137091015067172167745537", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "INVERT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 19, + "nextInstruction": "INVERT", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 20, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": "-11911212070228631137091015067172167745537" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": "-11911212070228631137091015067172167745537" + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString]=-12251494437149569600554389674603935956993", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000124", + "INVERT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 19, + "nextInstruction": "INVERT", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000124" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 20, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": "-12251494437149569600554389674603935956993" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": "-12251494437149569600554389674603935956993" + } + ] + } + } + ] + }, + { + "name": "Real test with clone [Interop]=FAULT", + "script": [ + "SYSCALL", + "0x77777777", + "INVERT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "INVERT", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Buffer,Buffer]=FAULT", + "script": [ + "PUSHDATA1", + "0x01AA", + "CONVERT", + "0x30", + "PUSHDATA1", + "0x01BB", + "CONVERT", + "0x30", + "INVERT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "INVERT", + "evaluationStack": [ + { + "type": "Buffer", + "value": "0xBB" + }, + { + "type": "Buffer", + "value": "0xAA" + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/BitwiseLogic/NOTEQUAL.json b/tests/Neo.VM.Tests/Tests/OpCodes/BitwiseLogic/NOTEQUAL.json new file mode 100644 index 0000000000..95acf9509d --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/BitwiseLogic/NOTEQUAL.json @@ -0,0 +1,1299 @@ +{ + "category": "Bitwise Logic", + "name": "NOTEQUAL", + "tests": [ + { + "name": "Without two pushes", + "script": [ + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one push", + "script": [ + "PUSH0", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "StepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Integer,Integer]=false", + "script": [ + "PUSH1", + "PUSH1", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [Integer,Integer]=true", + "script": [ + "PUSH2", + "PUSH1", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [Array,Array]=false", + "script": [ + "PUSH0", + "NEWARRAY", + "DUP", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "array", + "value": [] + }, + { + "type": "array", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [Array,Array]=true", + "script": [ + "PUSH0", + "NEWARRAY", + "PUSH0", + "NEWARRAY", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "array", + "value": [] + }, + { + "type": "array", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [Struct,Struct]=false with clone", + "script": [ + "PUSH0", + "NEWSTRUCT", + "DUP", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "struct", + "value": [] + }, + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [Struct,Struct]=false without clone", + "script": [ + "PUSH0", + "NEWSTRUCT", + "PUSH0", + "NEWSTRUCT", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "struct", + "value": [] + }, + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [Struct,Struct]=true", + "script": [ + "PUSH0", + "NEWSTRUCT", + "PUSH1", + "NEWSTRUCT", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "struct", + "value": [ + { + "type": "Null" + } + ] + }, + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [Map,Map]=false", + "script": [ + "NEWMAP", + "DUP", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "map", + "value": {} + }, + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [Map,Map]=true", + "script": [ + "NEWMAP", + "NEWMAP", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "map", + "value": {} + }, + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=false", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 39, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=true same length", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000124", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000124" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 39, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=true different length", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x10", + "0x00000000000000000000000000000124", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 37, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x00000000000000000000000000000124" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=true different length", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x10", + "0x00000000000000000000000000000124", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 37, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x00000000000000000000000000000124" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test with clone [Interop,Interop]=false", + "script": [ + "SYSCALL", + "0x77777777", + "DUP", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "DUP", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + }, + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Real test [Interop,Interop]=true", + "script": [ + "SYSCALL", + "0x77777777", + "SYSCALL", + "0x77777777", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + }, + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [Buffer,Buffer]=true", + "script": [ + "PUSHDATA1", + "0x01AA", + "CONVERT", + "0x30", + "PUSHDATA1", + "0x01BB", + "CONVERT", + "0x30", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "Buffer", + "value": "0xBB" + }, + { + "type": "Buffer", + "value": "0xAA" + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [Buffer,Buffer]=true", + "script": [ + "PUSHDATA1", + "0x01AA", + "CONVERT", + "0x30", + "PUSHDATA1", + "0x01AA", + "CONVERT", + "0x30", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "Buffer", + "value": "0xAA" + }, + { + "type": "Buffer", + "value": "0xAA" + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Real test [Buffer,Buffer]=false", + "script": [ + "PUSHDATA1", + "0x01AA", + "CONVERT", + "0x30", + "DUP", + "NOTEQUAL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "NOTEQUAL", + "evaluationStack": [ + { + "type": "Buffer", + "value": "0xAA" + }, + { + "type": "Buffer", + "value": "0xAA" + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/BitwiseLogic/OR.json b/tests/Neo.VM.Tests/Tests/OpCodes/BitwiseLogic/OR.json new file mode 100644 index 0000000000..fb52b32b71 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/BitwiseLogic/OR.json @@ -0,0 +1,811 @@ +{ + "category": "Bitwise Logic", + "name": "OR", + "tests": [ + { + "name": "Without two pushes", + "script": [ + "OR" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one push", + "script": [ + "PUSH0", + "OR" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "OR", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [true,true]=1", + "script": [ + "PUSH0", + "NOT", + "PUSH0", + "NOT", + "OR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "OR", + "evaluationStack": [ + { + "type": "boolean", + "value": true + }, + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Real test [false,true]=1", + "script": [ + "PUSH0", + "NOT", + "PUSH0", + "NOT", + "NOT", + "OR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "OR", + "evaluationStack": [ + { + "type": "boolean", + "value": false + }, + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Real test [1,1]=1", + "script": [ + "PUSH1", + "PUSH1", + "OR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "OR", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Real test [1,2]=3", + "script": [ + "PUSH2", + "PUSH1", + "OR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "OR", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 3 + } + ] + } + } + ] + }, + { + "name": "Real test [Array,Array]=FAULT", + "script": [ + "PUSH0", + "NEWARRAY", + "DUP", + "OR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "OR", + "evaluationStack": [ + { + "type": "array", + "value": [] + }, + { + "type": "array", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Struct,Struct]=FAULT", + "script": [ + "PUSH0", + "NEWSTRUCT", + "DUP", + "OR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "OR", + "evaluationStack": [ + { + "type": "struct", + "value": [] + }, + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Map,Map]=FAULT", + "script": [ + "NEWMAP", + "DUP", + "OR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "OR", + "evaluationStack": [ + { + "type": "map", + "value": {} + }, + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=11911212070228631137091015067172167745536", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "OR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "OR", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 39, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": "11911212070228631137091015067172167745536" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": "11911212070228631137091015067172167745536" + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=13272341537912384990944513496899240591360", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000124", + "OR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "OR", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000124" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 39, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": "13272341537912384990944513496899240591360" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": "13272341537912384990944513496899240591360" + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=11959069470373746643343180651838589370368", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x10", + "0x00000000000000000000000000000124", + "OR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 37, + "nextInstruction": "OR", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x00000000000000000000000000000124" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": "11959069470373746643343180651838589370368" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": "11959069470373746643343180651838589370368" + } + ] + } + } + ] + }, + { + "name": "Real test with clone [Interop,Interop]=FAULT", + "script": [ + "SYSCALL", + "0x77777777", + "DUP", + "OR" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "DUP", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "OR", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + }, + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Buffer,Buffer]=FAULT", + "script": [ + "PUSHDATA1", + "0x01AA", + "CONVERT", + "0x30", + "PUSHDATA1", + "0x01BB", + "CONVERT", + "0x30", + "OR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "OR", + "evaluationStack": [ + { + "type": "Buffer", + "value": "0xBB" + }, + { + "type": "Buffer", + "value": "0xAA" + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/BitwiseLogic/XOR.json b/tests/Neo.VM.Tests/Tests/OpCodes/BitwiseLogic/XOR.json new file mode 100644 index 0000000000..bae4cf557b --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/BitwiseLogic/XOR.json @@ -0,0 +1,811 @@ +{ + "category": "Bitwise Logic", + "name": "XOR", + "tests": [ + { + "name": "Without two pushes", + "script": [ + "XOR" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one push", + "script": [ + "PUSH0", + "XOR" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "XOR", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [true,true]=0", + "script": [ + "PUSH0", + "NOT", + "PUSH0", + "NOT", + "XOR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "XOR", + "evaluationStack": [ + { + "type": "boolean", + "value": true + }, + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 0 + } + ] + } + } + ] + }, + { + "name": "Real test [false,true]=1", + "script": [ + "PUSH0", + "NOT", + "PUSH0", + "NOT", + "NOT", + "XOR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "XOR", + "evaluationStack": [ + { + "type": "boolean", + "value": false + }, + { + "type": "boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Real test [1,1]=0", + "script": [ + "PUSH1", + "PUSH1", + "XOR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "XOR", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 0 + } + ] + } + } + ] + }, + { + "name": "Real test [1,2]=3", + "script": [ + "PUSH2", + "PUSH1", + "XOR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "XOR", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 3 + } + ] + } + } + ] + }, + { + "name": "Real test [Array,Array]=FAULT", + "script": [ + "PUSH0", + "NEWARRAY", + "DUP", + "XOR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "XOR", + "evaluationStack": [ + { + "type": "array", + "value": [] + }, + { + "type": "array", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Struct,Struct]=FAULT", + "script": [ + "PUSH0", + "NEWSTRUCT", + "DUP", + "XOR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "XOR", + "evaluationStack": [ + { + "type": "struct", + "value": [] + }, + { + "type": "struct", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Map,Map]=FAULT", + "script": [ + "NEWMAP", + "DUP", + "XOR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "XOR", + "evaluationStack": [ + { + "type": "map", + "value": {} + }, + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=0", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "XOR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "XOR", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 39, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 0 + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=2381976568446569244243622252022377480192", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000124", + "XOR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "XOR", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000124" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 39, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": "2381976568446569244243622252022377480192" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": "2381976568446569244243622252022377480192" + } + ] + } + } + ] + }, + { + "name": "Real test [ByteString,ByteString]=11959069470373746643343180651838589370368", + "script": [ + "PUSHDATA1", + "0x11", + "0x0000000000000000000000000000000123", + "PUSHDATA1", + "0x10", + "0x00000000000000000000000000000124", + "XOR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 37, + "nextInstruction": "XOR", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x00000000000000000000000000000124" + }, + { + "type": "ByteString", + "value": "0x0000000000000000000000000000000123" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 38, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Integer", + "value": "11959069470373746643343180651838589370368" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": "11959069470373746643343180651838589370368" + } + ] + } + } + ] + }, + { + "name": "Real test with clone [Interop,Interop]=FAULT", + "script": [ + "SYSCALL", + "0x77777777", + "DUP", + "XOR" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "DUP", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "XOR", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + }, + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Buffer,Buffer]=FAULT", + "script": [ + "PUSHDATA1", + "0x01AA", + "CONVERT", + "0x30", + "PUSHDATA1", + "0x01BB", + "CONVERT", + "0x30", + "XOR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "XOR", + "evaluationStack": [ + { + "type": "Buffer", + "value": "0xBB" + }, + { + "type": "Buffer", + "value": "0xAA" + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/ABORT.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/ABORT.json new file mode 100644 index 0000000000..17c3a35e2e --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/ABORT.json @@ -0,0 +1,22 @@ +{ + "category": "Control", + "name": "ABORT", + "tests": [ + { + "name": "Basic Test", + "script": [ + "ABORT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/ABORTMSG.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/ABORTMSG.json new file mode 100644 index 0000000000..a7e795eb2c --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/ABORTMSG.json @@ -0,0 +1,26 @@ +{ + "category": "Control", + "name": "ABORTMSG", + "tests": [ + { + "name": "Basic Test", + "script": [ + "PUSHDATA1", + "0x03", + "0x4e454f", + "ABORTMSG" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT", + "exceptionMessage": "ABORTMSG is executed. Reason: NEO" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/ASSERT.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/ASSERT.json new file mode 100644 index 0000000000..bb7d14c5e8 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/ASSERT.json @@ -0,0 +1,42 @@ +{ + "category": "Control", + "name": "ASSERT", + "tests": [ + { + "name": "Fault Test", + "script": [ + "PUSH0", + "ASSERT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Halt Test", + "script": [ + "PUSH1", + "ASSERT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "execute" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/ASSERTMSG.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/ASSERTMSG.json new file mode 100644 index 0000000000..fc4c572ec3 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/ASSERTMSG.json @@ -0,0 +1,47 @@ +{ + "category": "Control", + "name": "ASSERTMSG", + "tests": [ + { + "name": "Fault Test", + "script": [ + "PUSH0", + "PUSHDATA1", + "0x04", + "0x4641494c", + "ASSERTMSG" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT", + "exceptionMessage": "ASSERTMSG is executed with false result. Reason: FAIL" + } + } + ] + }, + { + "name": "Halt Test", + "script": [ + "PUSH1", + "PUSHDATA1", + "0x04", + "0x50415353", + "ASSERTMSG" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/CALL.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/CALL.json new file mode 100644 index 0000000000..1b717c3a66 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/CALL.json @@ -0,0 +1,134 @@ +{ + "category": "Control", + "name": "CALL", + "tests": [ + { + "name": "Error negative", + "script": [ + "CALL", + "0xFF00" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Error out of bounds", + "script": [ + "CALL", + "0x0A" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "CALL", + "0x03", + "RET", + "PUSH0", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "PUSH0" + }, + { + "instructionPointer": 2, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + }, + { + "instructionPointer": 2, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/CALLA.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/CALLA.json new file mode 100644 index 0000000000..627442a5f1 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/CALLA.json @@ -0,0 +1,158 @@ +{ + "category": "Control", + "name": "CALLA", + "tests": [ + { + "name": "Wrong type", + "script": [ + "PUSH2", + "CALLA" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "CALLA", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSHA", + "0x07000000", + "CALLA", + "RET", + "PUSH0", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "CALLA", + "evaluationStack": [ + { + "type": "pointer", + "value": 7 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "PUSH0" + }, + { + "instructionPointer": 6, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + }, + { + "instructionPointer": 6, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/CALL_L.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/CALL_L.json new file mode 100644 index 0000000000..5f60bda0ca --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/CALL_L.json @@ -0,0 +1,134 @@ +{ + "category": "Control", + "name": "CALL_L", + "tests": [ + { + "name": "Error negative", + "script": [ + "CALL_L", + "0x000000FF" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Error out of bounds", + "script": [ + "CALL_L", + "0x0A000000" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "CALL_L", + "0x06000000", + "RET", + "PUSH0", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "PUSH0" + }, + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + }, + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMP.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMP.json new file mode 100644 index 0000000000..172e50b956 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMP.json @@ -0,0 +1,74 @@ +{ + "category": "Control", + "name": "JMP", + "tests": [ + { + "name": "Out of range [<0]", + "script": [ + "JMP", + "0xff", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Out of range [>length]", + "script": [ + "JMP", + "0x7f", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "JMP", + "0x02", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPEQ.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPEQ.json new file mode 100644 index 0000000000..3e7a3144ba --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPEQ.json @@ -0,0 +1,307 @@ +{ + "category": "Control", + "name": "JMPEQ", + "tests": [ + { + "name": "Without all items", + "script": [ + "JMPEQ", + "0x00" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one", + "script": [ + "PUSH1", + "JMPEQ", + "0x00" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [>]", + "script": [ + "PUSH1", + "PUSH0", + "JMPEQ", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH0", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPEQ", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [==]", + "script": [ + "PUSH1", + "PUSH1", + "JMPEQ", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPEQ", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [<]", + "script": [ + "PUSH0", + "PUSH1", + "JMPEQ", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPEQ", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPEQ_L.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPEQ_L.json new file mode 100644 index 0000000000..78ed4a0e70 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPEQ_L.json @@ -0,0 +1,307 @@ +{ + "category": "Control", + "name": "JMPEQ_L", + "tests": [ + { + "name": "Without all items", + "script": [ + "JMPEQ_L", + "0x00000000" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one", + "script": [ + "PUSH1", + "JMPEQ_L", + "0x00000000" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [>]", + "script": [ + "PUSH1", + "PUSH0", + "JMPEQ_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH0", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPEQ_L", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [==]", + "script": [ + "PUSH1", + "PUSH1", + "JMPEQ_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPEQ_L", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [<]", + "script": [ + "PUSH0", + "PUSH1", + "JMPEQ_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPEQ_L", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPGE.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPGE.json new file mode 100644 index 0000000000..84f7d00c59 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPGE.json @@ -0,0 +1,293 @@ +{ + "category": "Control", + "name": "JMPGE", + "tests": [ + { + "name": "Without all items", + "script": [ + "JMPGE", + "0x00" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one", + "script": [ + "PUSH1", + "JMPGE", + "0x00" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [>]", + "script": [ + "PUSH1", + "PUSH0", + "JMPGE", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH0", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPGE", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [==]", + "script": [ + "PUSH1", + "PUSH1", + "JMPGE", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPGE", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [<]", + "script": [ + "PUSH0", + "PUSH1", + "JMPGE", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPGE", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPGE_L.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPGE_L.json new file mode 100644 index 0000000000..2fa99ff25d --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPGE_L.json @@ -0,0 +1,293 @@ +{ + "category": "Control", + "name": "JMPGE_L", + "tests": [ + { + "name": "Without all items", + "script": [ + "JMPGE_L", + "0x00000000" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one", + "script": [ + "PUSH1", + "JMPGE_L", + "0x00000000" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [>]", + "script": [ + "PUSH1", + "PUSH0", + "JMPGE_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH0", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPGE_L", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [==]", + "script": [ + "PUSH1", + "PUSH1", + "JMPGE_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPGE_L", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [<]", + "script": [ + "PUSH0", + "PUSH1", + "JMPGE_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPGE_L", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPGT.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPGT.json new file mode 100644 index 0000000000..c4ca491cca --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPGT.json @@ -0,0 +1,307 @@ +{ + "category": "Control", + "name": "JMPGT", + "tests": [ + { + "name": "Without all items", + "script": [ + "JMPGT", + "0x00" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one", + "script": [ + "PUSH1", + "JMPGT", + "0x00" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [>]", + "script": [ + "PUSH1", + "PUSH0", + "JMPGT", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH0", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPGT", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [==]", + "script": [ + "PUSH1", + "PUSH1", + "JMPGT", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPGT", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [<]", + "script": [ + "PUSH0", + "PUSH1", + "JMPGT", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPGT", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPGT_L.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPGT_L.json new file mode 100644 index 0000000000..5523aa87c9 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPGT_L.json @@ -0,0 +1,307 @@ +{ + "category": "Control", + "name": "JMPGT_L", + "tests": [ + { + "name": "Without all items", + "script": [ + "JMPGT_L", + "0x00000000" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one", + "script": [ + "PUSH1", + "JMPGT_L", + "0x00000000" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [>]", + "script": [ + "PUSH1", + "PUSH0", + "JMPGT_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH0", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPGT_L", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [==]", + "script": [ + "PUSH1", + "PUSH1", + "JMPGT_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPGT_L", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [<]", + "script": [ + "PUSH0", + "PUSH1", + "JMPGT_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPGT_L", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPIF.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPIF.json new file mode 100644 index 0000000000..121c8b76c7 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPIF.json @@ -0,0 +1,145 @@ +{ + "category": "Control", + "name": "JMPIF", + "tests": [ + { + "name": "Without items", + "script": [ + "JMPIF", + "0x00" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [1]", + "script": [ + "PUSH1", + "JMPIF", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "JMPIF", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [0]", + "script": [ + "PUSH0", + "JMPIF", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "JMPIF", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPIFNOT.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPIFNOT.json new file mode 100644 index 0000000000..e11227ce40 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPIFNOT.json @@ -0,0 +1,145 @@ +{ + "category": "Control", + "name": "JMPIFNOT", + "tests": [ + { + "name": "Without items", + "script": [ + "JMPIFNOT", + "0x00" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [1]", + "script": [ + "PUSH1", + "JMPIFNOT", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "JMPIFNOT", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [0]", + "script": [ + "PUSH0", + "JMPIFNOT", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "JMPIFNOT", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPIFNOT_L.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPIFNOT_L.json new file mode 100644 index 0000000000..90e37bee74 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPIFNOT_L.json @@ -0,0 +1,145 @@ +{ + "category": "Control", + "name": "JMPIFNOT_L", + "tests": [ + { + "name": "Without items", + "script": [ + "JMPIFNOT_L", + "0x00000000" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [1]", + "script": [ + "PUSH1", + "JMPIFNOT_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "JMPIFNOT_L", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [0]", + "script": [ + "PUSH0", + "JMPIFNOT_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "JMPIFNOT_L", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPIF_L.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPIF_L.json new file mode 100644 index 0000000000..a9d0e1e352 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPIF_L.json @@ -0,0 +1,145 @@ +{ + "category": "Control", + "name": "JMPIF_L", + "tests": [ + { + "name": "Without items", + "script": [ + "JMPIF_L", + "0x00000000" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [1]", + "script": [ + "PUSH1", + "JMPIF_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "JMPIF_L", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [0]", + "script": [ + "PUSH0", + "JMPIF_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "JMPIF_L", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPLE.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPLE.json new file mode 100644 index 0000000000..5325d60cf2 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPLE.json @@ -0,0 +1,293 @@ +{ + "category": "Control", + "name": "JMPLE", + "tests": [ + { + "name": "Without all items", + "script": [ + "JMPLE", + "0x00" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one", + "script": [ + "PUSH1", + "JMPLE", + "0x00" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [>]", + "script": [ + "PUSH1", + "PUSH0", + "JMPLE", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH0", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPLE", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [==]", + "script": [ + "PUSH1", + "PUSH1", + "JMPLE", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPLE", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [<]", + "script": [ + "PUSH0", + "PUSH1", + "JMPLE", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPLE", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPLE_L.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPLE_L.json new file mode 100644 index 0000000000..3ccc3c06e8 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPLE_L.json @@ -0,0 +1,293 @@ +{ + "category": "Control", + "name": "JMPLE_L", + "tests": [ + { + "name": "Without all items", + "script": [ + "JMPLE_L", + "0x00000000" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one", + "script": [ + "PUSH1", + "JMPLE_L", + "0x00000000" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [>]", + "script": [ + "PUSH1", + "PUSH0", + "JMPLE_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH0", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPLE_L", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [==]", + "script": [ + "PUSH1", + "PUSH1", + "JMPLE_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPLE_L", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [<]", + "script": [ + "PUSH0", + "PUSH1", + "JMPLE_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPLE_L", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPLT.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPLT.json new file mode 100644 index 0000000000..72946b6c6a --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPLT.json @@ -0,0 +1,307 @@ +{ + "category": "Control", + "name": "JMPLT", + "tests": [ + { + "name": "Without all items", + "script": [ + "JMPLT", + "0x00" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one", + "script": [ + "PUSH1", + "JMPLT", + "0x00" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [>]", + "script": [ + "PUSH1", + "PUSH0", + "JMPLT", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH0", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPLT", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [==]", + "script": [ + "PUSH1", + "PUSH1", + "JMPLT", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPLT", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [<]", + "script": [ + "PUSH0", + "PUSH1", + "JMPLT", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPLT", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPLT_L.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPLT_L.json new file mode 100644 index 0000000000..196b455d0a --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPLT_L.json @@ -0,0 +1,307 @@ +{ + "category": "Control", + "name": "JMPLT_L", + "tests": [ + { + "name": "Without all items", + "script": [ + "JMPLT_L", + "0x00000000" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one", + "script": [ + "PUSH1", + "JMPLT_L", + "0x00000000" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [>]", + "script": [ + "PUSH1", + "PUSH0", + "JMPLT_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH0", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPLT_L", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [==]", + "script": [ + "PUSH1", + "PUSH1", + "JMPLT_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPLT_L", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [<]", + "script": [ + "PUSH0", + "PUSH1", + "JMPLT_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPLT_L", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPNE.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPNE.json new file mode 100644 index 0000000000..53498bc05b --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPNE.json @@ -0,0 +1,293 @@ +{ + "category": "Control", + "name": "JMPNE", + "tests": [ + { + "name": "Without all items", + "script": [ + "JMPNE", + "0x00" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one", + "script": [ + "PUSH1", + "JMPNE", + "0x00" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [>]", + "script": [ + "PUSH1", + "PUSH0", + "JMPNE", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH0", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPNE", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [==]", + "script": [ + "PUSH1", + "PUSH1", + "JMPNE", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPNE", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [<]", + "script": [ + "PUSH0", + "PUSH1", + "JMPNE", + "0x03", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPNE", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPNE_L.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPNE_L.json new file mode 100644 index 0000000000..a1adb96021 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMPNE_L.json @@ -0,0 +1,293 @@ +{ + "category": "Control", + "name": "JMPNE_L", + "tests": [ + { + "name": "Without all items", + "script": [ + "JMPNE_L", + "0x00000000" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without one", + "script": [ + "PUSH1", + "JMPNE_L", + "0x00000000" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [>]", + "script": [ + "PUSH1", + "PUSH0", + "JMPNE_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH0", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPNE_L", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [==]", + "script": [ + "PUSH1", + "PUSH1", + "JMPNE_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPNE_L", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [<]", + "script": [ + "PUSH0", + "PUSH1", + "JMPNE_L", + "0x06000000", + "NOP", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "JMPNE_L", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMP_L.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMP_L.json new file mode 100644 index 0000000000..4f6157d1de --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/JMP_L.json @@ -0,0 +1,74 @@ +{ + "category": "Control", + "name": "JMP_L", + "tests": [ + { + "name": "Out of range [<0]", + "script": [ + "JMP_L", + "0xffffffff", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Out of range [>length]", + "script": [ + "JMP_L", + "0xffffff7f", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "JMP_L", + "0x05000000", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/NOP.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/NOP.json new file mode 100644 index 0000000000..3564f8afa2 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/NOP.json @@ -0,0 +1,96 @@ +{ + "category": "Control", + "name": "NOP", + "tests": [ + { + "name": "Real test", + "script": [ + "NOP", + "NOP", + "NOP", + "NOP", + "NOP" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "NOP" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/RET.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/RET.json new file mode 100644 index 0000000000..993f8af213 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/RET.json @@ -0,0 +1,22 @@ +{ + "category": "Control", + "name": "RET", + "tests": [ + { + "name": "Real test", + "script": [ + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/SYSCALL.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/SYSCALL.json new file mode 100644 index 0000000000..c99da78b24 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/SYSCALL.json @@ -0,0 +1,143 @@ +{ + "category": "Control", + "name": "SYSCALL", + "tests": [ + { + "name": "Syscall that does not exist", + "script": [ + "SYSCALL", + "0x00" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong script", + "script": [ + "SYSCALL", + "0x2a537973", + "DEPTH", + "CALL_L", + "0x6d2e0000", + "PUSHDATA1", + "0x457865637574696f6e456e67696e652e476574536372697074436f6e7461696e6572" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [IMessageProvider]", + "script": [ + "SYSCALL", + "0x77777777" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "interop", + "value": "Object" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "interop", + "value": "Object" + } + ] + } + } + ] + }, + { + "name": "Wrong script", + "script": [ + "SYSCALL", + "0xfdffff00" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong script", + "script": [ + "SYSCALL", + "0xfeffffff", + "0xff", + "PUSH0" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong script", + "script": [ + "SYSCALL", + "0xffffffff", + "0xffffffffff", + "PUSH0" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/THROW.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/THROW.json new file mode 100644 index 0000000000..a382516154 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/THROW.json @@ -0,0 +1,24 @@ +{ + "category": "Control", + "name": "THROW", + "tests": [ + { + "name": "Fault Test", + "script": [ + "PUSH0", + "THROW" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH.json new file mode 100644 index 0000000000..d08f084d95 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH.json @@ -0,0 +1,175 @@ +{ + "category": "Control", + "name": "TRY_CATCH", + "tests": [ + { + "name": "try catch with syscall exception", + "script": [ + "TRY", + "0x0a00", + "SYSCALL", + "0xdeaddead", + "ENDTRY", + "0x05", + "PUSH1", + "ENDTRY", + "0x02", + "PUSH2" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 11, + "nextInstruction": "ENDTRY", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "ByteString", + "value": "0x6572726f72" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "ByteString", + "value": "0x6572726f72" + } + ] + } + } + ] + }, + { + "name": "try catch without exception", + "script": [ + "TRY", + "0x0600", + "PUSH0", + "ENDTRY", + "0x05", + "PUSH3", + "ENDTRY", + "0x02", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 9, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + } + ] + }, + { + "name": "try catch with exception", + "script": [ + "TRY", + "0x0700", + "PUSH0", + "THROW", + "ENDTRY", + "0x05", + "PUSH1", + "ENDTRY", + "0x02", + "PUSH2" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "PUSH2", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY.json new file mode 100644 index 0000000000..769829fc3d --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY.json @@ -0,0 +1,72 @@ +{ + "category": "Control", + "name": "TRY_CATCH_FINALLY", + "tests": [ + { + "name": "try catch finally without exception", + "script": [ + "TRY", + "0x080a", + "PUSH0", + "ENDTRY", + "0x08", + "RET", + "PUSH2", + "ENDTRY", + "0x04", + "PUSH3", + "ENDFINALLY", + "PUSH4" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "PUSH3", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 12, + "nextInstruction": "PUSH4", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY10.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY10.json new file mode 100644 index 0000000000..fae60b985f --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY10.json @@ -0,0 +1,25 @@ +{ + "category": "Control", + "name": "TRY_CATCH_FINALLY", + "tests": [ + { + "name": "try + finally without ENDTRY", + "script": [ + "TRY", + "0x0003", + "ENDFINALLY" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY2.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY2.json new file mode 100644 index 0000000000..37299f2423 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY2.json @@ -0,0 +1,103 @@ +{ + "category": "Control", + "name": "TRY_CATCH_FINALLY", + "tests": [ + { + "name": "try catch finally with exception", + "script": [ + "TRY", + "0x080C", + "PUSH0", + "THROW", + "ENDTRY", + "0x06", + "RET", + "PUSH1", + "ENDTRY", + "0x02", + "RET", + "PUSH2", + "ENDFINALLY", + "PUSH3" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 12, + "nextInstruction": "PUSH2", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 11, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY3.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY3.json new file mode 100644 index 0000000000..b6cd601ff2 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY3.json @@ -0,0 +1,111 @@ +{ + "category": "Control", + "name": "TRY_CATCH_FINALLY", + "tests": [ + { + "name": "try{ try{ throw }catch{ }}catch{ }finally{ }", + "script": [ + "TRY", + "0x0e13", + "TRY", + "0x0700", + "PUSH0", + "THROW", + "ENDTRY", + "0x01", + "PUSH1", + "ENDTRY", + "0x02", + "ENDTRY", + "0x02", + "RET", + "PUSH2", + "ENDTRY", + "0x04", + "PUSH3", + "ENDFINALLY", + "PUSH4" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 13, + "nextInstruction": "ENDTRY", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 15, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY4.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY4.json new file mode 100644 index 0000000000..710cadb12c --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY4.json @@ -0,0 +1,117 @@ +{ + "category": "Control", + "name": "TRY_CATCH_FINALLY", + "tests": [ + { + "name": "try{ try{ throw }catch{ throw } }catch{ }finally{ }", + "script": [ + "TRY", + "0x1014", + "TRY", + "0x0700", + "PUSH0", + "THROW", + "ENDTRY", + "0x01", + "PUSH1", + "THROW", + "ENDTRY", + "0x04", + "ENDTRY", + "0x01", + "PUSH2", + "ENDTRY", + "0x02", + "RET", + "PUSH3", + "ENDFINALLY", + "PUSH4" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "PUSH1", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 16, + "nextInstruction": "PUSH2", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 19, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY5.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY5.json new file mode 100644 index 0000000000..6cb08caa3c --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY5.json @@ -0,0 +1,35 @@ +{ + "category": "Control", + "name": "TRY_CATCH_FINALLY", + "tests": [ + { + "name": "try{ assert false }catch{ push2 }finally{ push3 }", + "script": [ + "TRY", + "0x0608", + "PUSH0", + "ASSERT", + "ENDTRY", + "0x01", + "PUSH2", + "ENDTRY", + "0x01", + "PUSH3", + "ENDFINALLY", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY6.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY6.json new file mode 100644 index 0000000000..40b5bf2667 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY6.json @@ -0,0 +1,33 @@ +{ + "category": "Control", + "name": "TRY_CATCH_FINALLY", + "tests": [ + { + "name": "try{ abort }catch{ push2 }finally{ push3 }", + "script": [ + "TRY", + "0x0507", + "ABORT", + "ENDTRY", + "0x01", + "PUSH2", + "ENDTRY", + "0x01", + "PUSH3", + "ENDFINALLY", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY7.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY7.json new file mode 100644 index 0000000000..766e88f839 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY7.json @@ -0,0 +1,55 @@ +{ + "category": "Control", + "name": "TRY_CATCH_FINALLY", + "tests": [ + { + "name": "try{ throw }catch{ abort }finally{ push3 }", + "script": [ + "TRY", + "0x070a", + "PUSH0", + "THROW", + "ENDTRY", + "0x01", + "ABORT", + "ENDTRY", + "0x01", + "PUSH3", + "ENDFINALLY", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer":7, + "nextInstruction": "ABORT", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY8.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY8.json new file mode 100644 index 0000000000..93f2f84c26 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY8.json @@ -0,0 +1,101 @@ +{ + "category": "Control", + "name": "TRY_CATCH_FINALLY", + "tests": [ + { + "name": "try{ throw }catch{ throw }finally{ push3 }", + "script": [ + "TRY", + "0x070b", + "PUSH0", + "THROW", + "ENDTRY", + "0x01", + "PUSH2", + "THROW", + "ENDTRY", + "0x01", + "PUSH3", + "ENDFINALLY", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "PUSH2", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 11, + "nextInstruction": "PUSH3", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 12, + "nextInstruction": "ENDFINALLY", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY9.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY9.json new file mode 100644 index 0000000000..fe766a025a --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_CATCH_FINALLY9.json @@ -0,0 +1,98 @@ +{ + "category": "Control", + "name": "TRY_CATCH_FINALLY", + "tests": [ + { + "name": "try{ PUSH0, call A: PUSH1 { call B: PUSH2 throw an exception } }catch{ PUSH3 }", + "script": [ + "TRY", + "0x0f00", + "PUSH0", + "CALL", + "0x03", + "RET", + "PUSH1", + "CALL", + "0x02", + "PUSH2", + "THROW", + "RET", + "ENDTRY", + "0x01", + "PUSH3", + "ENDTRY", + "0x02", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 15, + "nextInstruction": "PUSH3", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 18, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_FINALLY.json b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_FINALLY.json new file mode 100644 index 0000000000..abb4788a32 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Control/TRY_FINALLY.json @@ -0,0 +1,49 @@ +{ + "category": "Control", + "name": "TRY_FINALLY", + "tests": [ + { + "name": "try finally with exception", + "script": [ + "TRY", + "0x0009", + "PUSH0", + "THROW", + "ENDTRY", + "0x01", + "JMP", + "0x03", + "PUSH1", + "ENDFINALLY", + "PUSH2" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 9, + "nextInstruction": "PUSH1" + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHA.json b/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHA.json new file mode 100644 index 0000000000..c6a0bf4c4f --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHA.json @@ -0,0 +1,86 @@ +{ + "category": "Push", + "name": "PUSHA", + "tests": [ + { + "name": "Out of range [-1]", + "script": [ + "PUSHA", + "0xffffffff" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Out of range [>length]", + "script": [ + "PUSHA", + "0xffffff7f" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSHA", + "0x00000000" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "pointer", + "value": 0 + } + ] + } + } + ] + }, + { + "name": "Real test [=length]", + "script": [ + "PUSHA", + "0x05000000" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "pointer", + "value": 5 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHDATA1.json b/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHDATA1.json new file mode 100644 index 0000000000..5831718b95 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHDATA1.json @@ -0,0 +1,47 @@ +{ + "category": "Push", + "name": "PUSHDATA1", + "tests": [ + { + "name": "Good definition", + "script": [ + "PUSHDATA1", + "0x04", + "0x01020304" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "ByteString", + "value": "0x01020304" + } + ] + } + } + ] + }, + { + "name": "Without enough length", + "script": [ + "PUSHDATA1", + "0x0501020304" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHDATA2.json b/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHDATA2.json new file mode 100644 index 0000000000..b31e47fb74 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHDATA2.json @@ -0,0 +1,47 @@ +{ + "category": "Push", + "name": "PUSHDATA2", + "tests": [ + { + "name": "Good definition", + "script": [ + "PUSHDATA2", + "0x0400", + "0x01020304" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "ByteString", + "value": "0x01020304" + } + ] + } + } + ] + }, + { + "name": "Without enough length", + "script": [ + "PUSHDATA2", + "0x050001020304" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHDATA4.json b/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHDATA4.json new file mode 100644 index 0000000000..d61a42b3a4 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHDATA4.json @@ -0,0 +1,99 @@ +{ + "category": "Push", + "name": "PUSHDATA4", + "tests": [ + { + "name": "More length than script", + "script": [ + "PUSHDATA4", + "0x00080000" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Negative length", + "script": [ + "PUSHDATA4", + "0xffffffff" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Good definition", + "script": [ + "PUSHDATA4", + "0x04000000", + "0x01020304" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "ByteString", + "value": "0x01020304" + } + ] + } + } + ] + }, + { + "name": "Without enough length", + "script": [ + "PUSHDATA4", + "0x0500000001020304" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Max length (Parse Instruction Error)", + "script": [ + "PUSHDATA4", + "0x01001000", + "0xFF" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHINT8_to_PUSHINT256.json b/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHINT8_to_PUSHINT256.json new file mode 100644 index 0000000000..72cca284bb --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHINT8_to_PUSHINT256.json @@ -0,0 +1,59 @@ +{ + "category": "Push", + "name": "PUSHINT8 to PUSHINT256", + "tests": [ + { + "name": "Basic Test", + "script": [ + "PUSHINT8", + "0xff", + "PUSHINT16", + "0xfeff", + "PUSHINT32", + "0xfdffffff", + "PUSHINT64", + "0xfcffffffffffffff", + "PUSHINT128", + "0xfbffffffffffffffffffffffffffffff", + "PUSHINT256", + "0xfaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": -6 + }, + { + "type": "integer", + "value": -5 + }, + { + "type": "integer", + "value": -4 + }, + { + "type": "integer", + "value": -3 + }, + { + "type": "integer", + "value": -2 + }, + { + "type": "integer", + "value": -1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHM1_to_PUSH16.json b/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHM1_to_PUSH16.json new file mode 100644 index 0000000000..3b7f06ed6b --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHM1_to_PUSH16.json @@ -0,0 +1,219 @@ +{ + "category": "Push", + "name": "From PUSHM1 to PUSH16 [-1 to 16]", + "tests": [ + { + "name": "Basic Test", + "script": [ + "PUSHM1", + "PUSH0", + "PUSH1", + "PUSH2", + "PUSH3", + "PUSH4", + "PUSH5", + "PUSH6", + "PUSH7", + "PUSH8", + "PUSH9", + "PUSH10", + "PUSH11", + "PUSH12", + "PUSH13", + "PUSH14", + "PUSH15", + "PUSH16", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 18, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 16 + }, + { + "type": "integer", + "value": 15 + }, + { + "type": "integer", + "value": 14 + }, + { + "type": "integer", + "value": 13 + }, + { + "type": "integer", + "value": 12 + }, + { + "type": "integer", + "value": 11 + }, + { + "type": "integer", + "value": 10 + }, + { + "type": "integer", + "value": 9 + }, + { + "type": "integer", + "value": 8 + }, + { + "type": "integer", + "value": 7 + }, + { + "type": "integer", + "value": 6 + }, + { + "type": "integer", + "value": 5 + }, + { + "type": "integer", + "value": 4 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": -1 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 16 + }, + { + "type": "integer", + "value": 15 + }, + { + "type": "integer", + "value": 14 + }, + { + "type": "integer", + "value": 13 + }, + { + "type": "integer", + "value": 12 + }, + { + "type": "integer", + "value": 11 + }, + { + "type": "integer", + "value": 10 + }, + { + "type": "integer", + "value": 9 + }, + { + "type": "integer", + "value": 8 + }, + { + "type": "integer", + "value": 7 + }, + { + "type": "integer", + "value": 6 + }, + { + "type": "integer", + "value": 5 + }, + { + "type": "integer", + "value": 4 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": -1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHNULL.json b/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHNULL.json new file mode 100644 index 0000000000..5b5247edb1 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Push/PUSHNULL.json @@ -0,0 +1,46 @@ +{ + "category": "Push", + "name": "PUSHNULL", + "tests": [ + { + "name": "Good definition", + "script": [ + "PUSHNULL" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "null" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "null" + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/INITSLOT.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/INITSLOT.json new file mode 100644 index 0000000000..458463ea55 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/INITSLOT.json @@ -0,0 +1,206 @@ +{ + "category": "Slot", + "name": "INITSLOT", + "tests": [ + { + "name": "Without enough items", + "script": [ + "INITSLOT", + "0x0101" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without 0 items", + "script": [ + "INITSLOT", + "0x0000" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [LocalVariables]", + "script": [ + "INITSLOT", + "0x0100" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "localVariables": [ + { + "type": "Null" + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [Arguments]", + "script": [ + "PUSH1", + "INITSLOT", + "0x0001" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "arguments": [ + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Real test [LocalVariables + Arguments]", + "script": [ + "PUSH1", + "INITSLOT", + "0x0101" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "localVariables": [ + { + "type": "Null" + } + ], + "arguments": [ + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Initialize twice", + "script": [ + "PUSH0", + "INITSLOT", + "0x0101", + "PUSH0", + "INITSLOT", + "0x0101" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "PUSH0", + "localVariables": [ + { + "type": "Null" + } + ], + "arguments": [ + { + "type": "Integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/INITSSLOT.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/INITSSLOT.json new file mode 100644 index 0000000000..e3a3e149f4 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/INITSSLOT.json @@ -0,0 +1,97 @@ +{ + "category": "Slot", + "name": "INITSSLOT", + "tests": [ + { + "name": "Without 0 items", + "script": [ + "INITSSLOT", + "0x00" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSSLOT", + "0x01" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "RET", + "staticFields": [ + { + "type": "Null" + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Initialize twice", + "script": [ + "INITSSLOT", + "0x01", + "INITSSLOT", + "0x02" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "INITSSLOT", + "staticFields": [ + { + "type": "Null" + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG.json new file mode 100644 index 0000000000..e3be41d38c --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG.json @@ -0,0 +1,69 @@ +{ + "category": "Slot", + "name": "LDARG", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDARG", + "0x00" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "PUSH1", + "INITSLOT", + "0x0001", + "LDARG", + "0x01" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH1", + "INITSLOT", + "0x0001", + "LDARG", + "0x00" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG0.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG0.json new file mode 100644 index 0000000000..038e8db3a6 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG0.json @@ -0,0 +1,47 @@ +{ + "category": "Slot", + "name": "LDARG0", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDARG0" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH1", + "INITSLOT", + "0x0001", + "LDARG0" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG1.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG1.json new file mode 100644 index 0000000000..522b1e68ac --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG1.json @@ -0,0 +1,67 @@ +{ + "category": "Slot", + "name": "LDARG1", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDARG1" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "PUSH1", + "INITSLOT", + "0x0001", + "LDARG1" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH1", + "PUSH2", + "INITSLOT", + "0x0002", + "LDARG1" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG2.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG2.json new file mode 100644 index 0000000000..16ec5d743d --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG2.json @@ -0,0 +1,68 @@ +{ + "category": "Slot", + "name": "LDARG2", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDARG2" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "PUSH1", + "INITSLOT", + "0x0001", + "LDARG2" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "INITSLOT", + "0x0003", + "LDARG2" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG3.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG3.json new file mode 100644 index 0000000000..5a4a844a1b --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG3.json @@ -0,0 +1,69 @@ +{ + "category": "Slot", + "name": "LDARG3", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDARG3" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "PUSH1", + "INITSLOT", + "0x0001", + "LDARG3" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "PUSH4", + "INITSLOT", + "0x0004", + "LDARG3" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG4.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG4.json new file mode 100644 index 0000000000..4a091e5ff2 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG4.json @@ -0,0 +1,70 @@ +{ + "category": "Slot", + "name": "LDARG4", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDARG4" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "PUSH1", + "INITSLOT", + "0x0001", + "LDARG4" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "PUSH4", + "PUSH5", + "INITSLOT", + "0x0005", + "LDARG4" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG5.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG5.json new file mode 100644 index 0000000000..1d0d706266 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG5.json @@ -0,0 +1,71 @@ +{ + "category": "Slot", + "name": "LDARG5", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDARG5" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "PUSH1", + "INITSLOT", + "0x0001", + "LDARG5" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "PUSH4", + "PUSH5", + "PUSH6", + "INITSLOT", + "0x0006", + "LDARG5" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG6.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG6.json new file mode 100644 index 0000000000..56da5d948b --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDARG6.json @@ -0,0 +1,72 @@ +{ + "category": "Slot", + "name": "LDARG6", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDARG6" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "PUSH1", + "INITSLOT", + "0x0001", + "LDARG6" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "PUSH4", + "PUSH5", + "PUSH6", + "PUSH7", + "INITSLOT", + "0x0007", + "LDARG6" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC.json new file mode 100644 index 0000000000..87f165dbdf --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC.json @@ -0,0 +1,70 @@ +{ + "category": "Slot", + "name": "LDLOC", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDLOC", + "0x00" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSLOT", + "0x0100", + "LDLOC", + "0x01" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSLOT", + "0x0100", + "PUSH1", + "STLOC", + "0x00", + "LDLOC", + "0x00" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC0.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC0.json new file mode 100644 index 0000000000..294a7b8df0 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC0.json @@ -0,0 +1,48 @@ +{ + "category": "Slot", + "name": "LDLOC0", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDLOC0" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSLOT", + "0x0100", + "PUSH1", + "STLOC0", + "LDLOC0" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC1.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC1.json new file mode 100644 index 0000000000..f9e648200a --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC1.json @@ -0,0 +1,66 @@ +{ + "category": "Slot", + "name": "LDLOC1", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDLOC1" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSLOT", + "0x0100", + "LDLOC1" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSLOT", + "0x0200", + "PUSH1", + "STLOC1", + "LDLOC1" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC2.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC2.json new file mode 100644 index 0000000000..075f338683 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC2.json @@ -0,0 +1,66 @@ +{ + "category": "Slot", + "name": "LDLOC2", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDLOC2" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSLOT", + "0x0100", + "LDLOC2" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSLOT", + "0x0300", + "PUSH1", + "STLOC2", + "LDLOC2" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC3.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC3.json new file mode 100644 index 0000000000..5463edaebc --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC3.json @@ -0,0 +1,66 @@ +{ + "category": "Slot", + "name": "LDLOC3", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDLOC3" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSLOT", + "0x0100", + "LDLOC3" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSLOT", + "0x0400", + "PUSH1", + "STLOC3", + "LDLOC3" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC4.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC4.json new file mode 100644 index 0000000000..23a616975d --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC4.json @@ -0,0 +1,66 @@ +{ + "category": "Slot", + "name": "LDLOC4", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDLOC4" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSLOT", + "0x0100", + "LDLOC4" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSLOT", + "0x0500", + "PUSH1", + "STLOC4", + "LDLOC4" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC5.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC5.json new file mode 100644 index 0000000000..9bde0a550a --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC5.json @@ -0,0 +1,66 @@ +{ + "category": "Slot", + "name": "LDLOC5", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDLOC5" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSLOT", + "0x0100", + "LDLOC5" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSLOT", + "0x0600", + "PUSH1", + "STLOC5", + "LDLOC5" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC6.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC6.json new file mode 100644 index 0000000000..edf7963263 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDLOC6.json @@ -0,0 +1,66 @@ +{ + "category": "Slot", + "name": "LDLOC6", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDLOC6" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSLOT", + "0x0100", + "LDLOC6" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSLOT", + "0x0700", + "PUSH1", + "STLOC6", + "LDLOC6" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD.json new file mode 100644 index 0000000000..311c0a5964 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD.json @@ -0,0 +1,70 @@ +{ + "category": "Slot", + "name": "LDSFLD", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDSFLD", + "0x00" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSSLOT", + "0x01", + "LDSFLD", + "0x01" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSSLOT", + "0x01", + "PUSH1", + "STSFLD", + "0x00", + "LDSFLD", + "0x00" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD0.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD0.json new file mode 100644 index 0000000000..8a750b04f2 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD0.json @@ -0,0 +1,48 @@ +{ + "category": "Slot", + "name": "LDSFLD0", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDSFLD0" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSSLOT", + "0x01", + "PUSH1", + "STSFLD0", + "LDSFLD0" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD1.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD1.json new file mode 100644 index 0000000000..4289e1aa91 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD1.json @@ -0,0 +1,66 @@ +{ + "category": "Slot", + "name": "LDSFLD1", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDSFLD1" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSSLOT", + "0x01", + "LDSFLD1" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSSLOT", + "0x02", + "PUSH1", + "STSFLD1", + "LDSFLD1" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD2.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD2.json new file mode 100644 index 0000000000..e03ab04d48 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD2.json @@ -0,0 +1,66 @@ +{ + "category": "Slot", + "name": "LDSFLD2", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDSFLD2" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSSLOT", + "0x01", + "LDSFLD2" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSSLOT", + "0x03", + "PUSH1", + "STSFLD2", + "LDSFLD2" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD3.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD3.json new file mode 100644 index 0000000000..d10624f5a7 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD3.json @@ -0,0 +1,66 @@ +{ + "category": "Slot", + "name": "LDSFLD3", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDSFLD3" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSSLOT", + "0x01", + "LDSFLD3" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSSLOT", + "0x04", + "PUSH1", + "STSFLD3", + "LDSFLD3" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD4.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD4.json new file mode 100644 index 0000000000..b405defe0f --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD4.json @@ -0,0 +1,66 @@ +{ + "category": "Slot", + "name": "LDSFLD4", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDSFLD4" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSSLOT", + "0x01", + "LDSFLD4" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSSLOT", + "0x05", + "PUSH1", + "STSFLD4", + "LDSFLD4" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD5.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD5.json new file mode 100644 index 0000000000..c6b3ee058f --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD5.json @@ -0,0 +1,66 @@ +{ + "category": "Slot", + "name": "LDSFLD5", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDSFLD5" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSSLOT", + "0x01", + "LDSFLD5" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSSLOT", + "0x06", + "PUSH1", + "STSFLD5", + "LDSFLD5" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD6.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD6.json new file mode 100644 index 0000000000..cd7ae9b500 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/LDSFLD6.json @@ -0,0 +1,66 @@ +{ + "category": "Slot", + "name": "LDSFLD6", + "tests": [ + { + "name": "Without slot", + "script": [ + "LDSFLD6" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSSLOT", + "0x01", + "LDSFLD6" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSSLOT", + "0x07", + "PUSH1", + "STSFLD6", + "LDSFLD6" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG.json new file mode 100644 index 0000000000..bdf9f2f5c5 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG.json @@ -0,0 +1,72 @@ +{ + "category": "Slot", + "name": "STARG", + "tests": [ + { + "name": "Without slot", + "script": [ + "PUSH1", + "STARG", + "0x00" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "PUSH0", + "INITSLOT", + "0x0001", + "PUSH1", + "STARG", + "0x01" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH1", + "INITSLOT", + "0x0001", + "PUSH0", + "STARG", + "0x00" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "arguments": [ + { + "type": "Integer", + "value": 0 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG0.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG0.json new file mode 100644 index 0000000000..b5666c3754 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG0.json @@ -0,0 +1,49 @@ +{ + "category": "Slot", + "name": "STARG0", + "tests": [ + { + "name": "Without slot", + "script": [ + "PUSH1", + "STARG0" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH0", + "INITSLOT", + "0x0001", + "PUSH1", + "STARG0" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "arguments": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG1.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG1.json new file mode 100644 index 0000000000..da52006943 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG1.json @@ -0,0 +1,74 @@ +{ + "category": "Slot", + "name": "STARG1", + "tests": [ + { + "name": "Without slot", + "script": [ + "PUSH1", + "STARG1" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "PUSH0", + "INITSLOT", + "0x0001", + "PUSH1", + "STARG1" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH1", + "PUSH2", + "INITSLOT", + "0x0002", + "PUSH0", + "STARG1" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "arguments": [ + { + "type": "Integer", + "value": 1 + }, + { + "type": "Integer", + "value": 0 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG2.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG2.json new file mode 100644 index 0000000000..8c6952eedd --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG2.json @@ -0,0 +1,79 @@ +{ + "category": "Slot", + "name": "STARG2", + "tests": [ + { + "name": "Without slot", + "script": [ + "PUSH1", + "STARG2" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "PUSH0", + "INITSLOT", + "0x0001", + "PUSH1", + "STARG2" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "INITSLOT", + "0x0003", + "PUSH0", + "STARG2" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "arguments": [ + { + "type": "Integer", + "value": 1 + }, + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 0 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG3.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG3.json new file mode 100644 index 0000000000..e8ed64b00d --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG3.json @@ -0,0 +1,84 @@ +{ + "category": "Slot", + "name": "STARG3", + "tests": [ + { + "name": "Without slot", + "script": [ + "PUSH1", + "STARG3" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "PUSH0", + "INITSLOT", + "0x0001", + "PUSH1", + "STARG3" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "PUSH4", + "INITSLOT", + "0x0004", + "PUSH0", + "STARG3" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "arguments": [ + { + "type": "Integer", + "value": 1 + }, + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 3 + }, + { + "type": "Integer", + "value": 0 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG4.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG4.json new file mode 100644 index 0000000000..7b32e0e09a --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG4.json @@ -0,0 +1,89 @@ +{ + "category": "Slot", + "name": "STARG4", + "tests": [ + { + "name": "Without slot", + "script": [ + "PUSH1", + "STARG4" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "PUSH0", + "INITSLOT", + "0x0001", + "PUSH1", + "STARG4" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "PUSH4", + "PUSH5", + "INITSLOT", + "0x0005", + "PUSH0", + "STARG4" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "arguments": [ + { + "type": "Integer", + "value": 1 + }, + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 3 + }, + { + "type": "Integer", + "value": 4 + }, + { + "type": "Integer", + "value": 0 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG5.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG5.json new file mode 100644 index 0000000000..9bcef878b4 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG5.json @@ -0,0 +1,94 @@ +{ + "category": "Slot", + "name": "STARG5", + "tests": [ + { + "name": "Without slot", + "script": [ + "PUSH1", + "STARG5" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "PUSH0", + "INITSLOT", + "0x0001", + "PUSH1", + "STARG5" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "PUSH4", + "PUSH5", + "PUSH6", + "INITSLOT", + "0x0006", + "PUSH0", + "STARG5" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "arguments": [ + { + "type": "Integer", + "value": 1 + }, + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 3 + }, + { + "type": "Integer", + "value": 4 + }, + { + "type": "Integer", + "value": 5 + }, + { + "type": "Integer", + "value": 0 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG6.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG6.json new file mode 100644 index 0000000000..e8c0f09b9f --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STARG6.json @@ -0,0 +1,99 @@ +{ + "category": "Slot", + "name": "STARG6", + "tests": [ + { + "name": "Without slot", + "script": [ + "PUSH1", + "STARG6" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "PUSH0", + "INITSLOT", + "0x0001", + "PUSH1", + "STARG6" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "PUSH4", + "PUSH5", + "PUSH6", + "PUSH7", + "INITSLOT", + "0x0007", + "PUSH0", + "STARG6" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "arguments": [ + { + "type": "Integer", + "value": 1 + }, + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 3 + }, + { + "type": "Integer", + "value": 4 + }, + { + "type": "Integer", + "value": 5 + }, + { + "type": "Integer", + "value": 6 + }, + { + "type": "Integer", + "value": 0 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STLOC.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STLOC.json new file mode 100644 index 0000000000..aae5bbb9ea --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STLOC.json @@ -0,0 +1,92 @@ +{ + "category": "Slot", + "name": "STLOC", + "tests": [ + { + "name": "Without slot", + "script": [ + "STLOC", + "0x00" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without enough items", + "script": [ + "INITSLOT", + "0x0100", + "PUSH2", + "STLOC" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSLOT", + "0x0100", + "PUSH1", + "STLOC", + "0x00", + "LDLOC", + "0x00" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "LDLOC", + "localVariables": [ + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD.json new file mode 100644 index 0000000000..149244cbbd --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD.json @@ -0,0 +1,84 @@ +{ + "category": "Slot", + "name": "STSFLD", + "tests": [ + { + "name": "Without slot", + "script": [ + "STSFLD", + "0x00" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSSLOT", + "0x01", + "STSFLD", + "0x01" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSSLOT", + "0x01", + "PUSH1", + "STSFLD", + "0x00" + ], + "steps": [ + { + "actions": [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "staticFields": [ + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD0.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD0.json new file mode 100644 index 0000000000..d2ee516f22 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD0.json @@ -0,0 +1,63 @@ +{ + "category": "Slot", + "name": "STSFLD0", + "tests": [ + { + "name": "Without slot", + "script": [ + "STSFLD0" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSSLOT", + "0x01", + "PUSH1", + "STSFLD0" + ], + "steps": [ + { + "actions": [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "staticFields": [ + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD1.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD1.json new file mode 100644 index 0000000000..5ba84602c1 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD1.json @@ -0,0 +1,84 @@ +{ + "category": "Slot", + "name": "STSFLD1", + "tests": [ + { + "name": "Without slot", + "script": [ + "STSFLD1" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSSLOT", + "0x01", + "STSFLD1" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSSLOT", + "0x02", + "PUSH1", + "STSFLD1" + ], + "steps": [ + { + "actions": [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "staticFields": [ + { + "type": "Null" + }, + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD2.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD2.json new file mode 100644 index 0000000000..d5c42ba76c --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD2.json @@ -0,0 +1,87 @@ +{ + "category": "Slot", + "name": "STSFLD2", + "tests": [ + { + "name": "Without slot", + "script": [ + "STSFLD2" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSSLOT", + "0x01", + "STSFLD2" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSSLOT", + "0x03", + "PUSH1", + "STSFLD2" + ], + "steps": [ + { + "actions": [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "staticFields": [ + { + "type": "Null" + }, + { + "type": "Null" + }, + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD3.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD3.json new file mode 100644 index 0000000000..f4ba41a606 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD3.json @@ -0,0 +1,90 @@ +{ + "category": "Slot", + "name": "STSFLD3", + "tests": [ + { + "name": "Without slot", + "script": [ + "STSFLD3" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSSLOT", + "0x01", + "STSFLD3" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSSLOT", + "0x04", + "PUSH1", + "STSFLD3" + ], + "steps": [ + { + "actions": [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "staticFields": [ + { + "type": "Null" + }, + { + "type": "Null" + }, + { + "type": "Null" + }, + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD4.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD4.json new file mode 100644 index 0000000000..48f4d09aa9 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD4.json @@ -0,0 +1,93 @@ +{ + "category": "Slot", + "name": "STSFLD4", + "tests": [ + { + "name": "Without slot", + "script": [ + "STSFLD4" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSSLOT", + "0x01", + "STSFLD4" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSSLOT", + "0x05", + "PUSH1", + "STSFLD4" + ], + "steps": [ + { + "actions": [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "staticFields": [ + { + "type": "Null" + }, + { + "type": "Null" + }, + { + "type": "Null" + }, + { + "type": "Null" + }, + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD5.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD5.json new file mode 100644 index 0000000000..f3d2573d63 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD5.json @@ -0,0 +1,96 @@ +{ + "category": "Slot", + "name": "STSFLD5", + "tests": [ + { + "name": "Without slot", + "script": [ + "STSFLD5" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSSLOT", + "0x01", + "STSFLD5" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSSLOT", + "0x06", + "PUSH1", + "STSFLD5" + ], + "steps": [ + { + "actions": [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "staticFields": [ + { + "type": "Null" + }, + { + "type": "Null" + }, + { + "type": "Null" + }, + { + "type": "Null" + }, + { + "type": "Null" + }, + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD6.json b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD6.json new file mode 100644 index 0000000000..2d89c71887 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Slot/STSFLD6.json @@ -0,0 +1,99 @@ +{ + "category": "Slot", + "name": "STSFLD6", + "tests": [ + { + "name": "Without slot", + "script": [ + "STSFLD6" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index out of range", + "script": [ + "INITSSLOT", + "0x01", + "STSFLD6" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "INITSSLOT", + "0x07", + "PUSH1", + "STSFLD6" + ], + "steps": [ + { + "actions": [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "staticFields": [ + { + "type": "Null" + }, + { + "type": "Null" + }, + { + "type": "Null" + }, + { + "type": "Null" + }, + { + "type": "Null" + }, + { + "type": "Null" + }, + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Splice/CAT.json b/tests/Neo.VM.Tests/Tests/OpCodes/Splice/CAT.json new file mode 100644 index 0000000000..f7b6cf3430 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Splice/CAT.json @@ -0,0 +1,436 @@ +{ + "category": "Splice", + "name": "CAT", + "tests": [ + { + "name": "Max Item Length", + "script": [ + "INITSLOT", + "0x0200", + "PUSHINT32", + "0x00000100", + "STLOC0", + "PUSH1", + "STLOC1", + "PUSHDATA2", + "0x1000", + "0x000102030405060708090A0B0C0D0E0F", + "PUSHDATA2", + "0x1000", + "0x000102030405060708090A0B0C0D0E0F", + "CAT", + "LDLOC1", + "INC", + "STLOC1", + "LDLOC1", + "LDLOC0", + "LT", + "JMPIF_L", + "0xE6FFFFFF", + "PUSHDATA1", + "0x01", + "0x00", + "CAT" + ], + "steps": [ + { + "actions": ["execute"], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong type [Map,ByteString]", + "script": [ + "PUSH0", + "NEWMAP", + "CAT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong type [ByteString,Map]", + "script": [ + "NEWMAP", + "PUSH0", + "CAT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong push", + "script": [ + "CAT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSHDATA1", + "0x01", + "0x01", + "PUSHDATA1", + "0x02", + "0x0203", + "CAT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Buffer", + "value": "0x010203" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Buffer", + "value": "0x010203" + } + ] + } + } + ] + }, + { + "name": "CAT int(0) with empty ByteString", + "script": [ + "PUSH1", + "DEC", + "PUSH0", + "CAT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "CAT", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Buffer", + "value": "" + } + ] + } + } + ] + }, + { + "name": "CAT 0x01 with empty ByteString", + "script": [ + "PUSH1", + "PUSH0", + "CAT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "CAT", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Buffer", + "value": "0x01" + } + ] + } + } + ] + }, + { + "name": "CAT empty ByteString with 0x01", + "script": [ + "PUSH0", + "PUSH1", + "CAT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "CAT", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Buffer", + "value": "0x01" + } + ] + } + } + ] + }, + { + "name": "CAT Buffers", + "script": [ + "PUSHDATA1", + "0x01AA", + "CONVERT", + "0x30", + "PUSHDATA1", + "0x01BB", + "CONVERT", + "0x30", + "INITSLOT", + "0x0002", + "LDARG1", + "LDARG0", + "CAT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 10, + "nextInstruction": "INITSLOT", + "evaluationStack": [ + { + "type": "Buffer", + "value": "0xBB" + }, + { + "type": "Buffer", + "value": "0xAA" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 13, + "nextInstruction": "LDARG1", + "arguments": [ + { + "type": "Buffer", + "value": "0xBB" + }, + { + "type": "Buffer", + "value": "0xAA" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 15, + "nextInstruction": "CAT", + "evaluationStack": [ + { + "type": "Buffer", + "value": "0xBB" + }, + { + "type": "Buffer", + "value": "0xAA" + } + ], + "arguments": [ + { + "type": "Buffer", + "value": "0xBB" + }, + { + "type": "Buffer", + "value": "0xAA" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 16, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Buffer", + "value": "0xAABB" + } + ], + "arguments": [ + { + "type": "Buffer", + "value": "0xBB" + }, + { + "type": "Buffer", + "value": "0xAA" + } + ] + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Splice/LEFT.json b/tests/Neo.VM.Tests/Tests/OpCodes/Splice/LEFT.json new file mode 100644 index 0000000000..02c0dbc0ce --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Splice/LEFT.json @@ -0,0 +1,228 @@ +{ + "category": "Splice", + "name": "LEFT", + "tests": [ + { + "name": "Without push", + "script": [ + "PUSH11", + "LEFT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong type [Map]", + "script": [ + "PUSH4", + "NEWMAP", + "LEFT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "LEFT", + "evaluationStack": [ + { + "type": "map", + "value": {} + }, + { + "type": "integer", + "value": 4 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Negative value", + "script": [ + "PUSHDATA1", + "0x03", + "0x010203", + "PUSHM1", + "LEFT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "LEFT", + "evaluationStack": [ + { + "type": "integer", + "value": -1 + }, + { + "type": "ByteString", + "value": "0x010203" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Overflow string", + "script": [ + "PUSHDATA1", + "0x03", + "0x010203", + "PUSH4", + "LEFT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "LEFT", + "evaluationStack": [ + { + "type": "integer", + "value": 4 + }, + { + "type": "ByteString", + "value": "0x010203" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSHDATA1", + "0x03", + "0x010203", + "PUSH2", + "LEFT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "LEFT", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "ByteString", + "value": "0x010203" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Buffer", + "value": "0x0102" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Buffer", + "value": "0x0102" + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Splice/MEMCPY.json b/tests/Neo.VM.Tests/Tests/OpCodes/Splice/MEMCPY.json new file mode 100644 index 0000000000..fcff55af20 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Splice/MEMCPY.json @@ -0,0 +1,382 @@ +{ + "category": "Splice", + "name": "MEMCPY", + "tests": [ + { + "name": "Max Item Length", + "script": [ + "PUSH4", + "NEWBUFFER", + "PUSHINT32", + "0x00001000", + "PUSHDATA1", + "0x02", + "0x1111", + "PUSH0", + "PUSH2", + "MEMCPY" + ], + "steps": [ + { + "actions": [ + "StepInto", + "StepInto", + "StepInto", + "StepInto", + "StepInto", + "StepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 13, + "nextInstruction": "MEMCPY", + "evaluationStack": [ + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 0 + }, + { + "type": "ByteString", + "value": "0x1111" + }, + { + "type": "Integer", + "value": 1048576 + }, + { + "type": "Buffer", + "value": "0x00000000" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Negative di", + "script": [ + "PUSH4", + "NEWBUFFER", + "PUSHM1", + "PUSHDATA1", + "0x02", + "0x1111", + "PUSH0", + "PUSH2", + "MEMCPY" + ], + "steps": [ + { + "actions": [ + "StepInto", + "StepInto", + "StepInto", + "StepInto", + "StepInto", + "StepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 9, + "nextInstruction": "MEMCPY", + "evaluationStack": [ + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 0 + }, + { + "type": "ByteString", + "value": "0x1111" + }, + { + "type": "Integer", + "value": -1 + }, + { + "type": "Buffer", + "value": "0x00000000" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Negative si", + "script": [ + "PUSH4", + "NEWBUFFER", + "PUSHINT32", + "0x00001000", + "PUSHDATA1", + "0x02", + "0x1111", + "PUSHM1", + "PUSH2", + "MEMCPY" + ], + "steps": [ + { + "actions": [ + "StepInto", + "StepInto", + "StepInto", + "StepInto", + "StepInto", + "StepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 13, + "nextInstruction": "MEMCPY", + "evaluationStack": [ + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": -1 + }, + { + "type": "ByteString", + "value": "0x1111" + }, + { + "type": "Integer", + "value": 1048576 + }, + { + "type": "Buffer", + "value": "0x00000000" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Negative n", + "script": [ + "PUSH4", + "NEWBUFFER", + "PUSHINT32", + "0x00001000", + "PUSHDATA1", + "0x02", + "0x1111", + "PUSH0", + "PUSHM1", + "MEMCPY" + ], + "steps": [ + { + "actions": [ + "StepInto", + "StepInto", + "StepInto", + "StepInto", + "StepInto", + "StepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 13, + "nextInstruction": "MEMCPY", + "evaluationStack": [ + { + "type": "Integer", + "value": -1 + }, + { + "type": "Integer", + "value": 0 + }, + { + "type": "ByteString", + "value": "0x1111" + }, + { + "type": "Integer", + "value": 1048576 + }, + { + "type": "Buffer", + "value": "0x00000000" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong type [Array]", + "script": [ + "PUSH0", + "NEWARRAY", + "PUSHINT32", + "0x00001000", + "PUSHDATA1", + "0x02", + "0x1111", + "PUSH0", + "PUSH2", + "MEMCPY" + ], + "steps": [ + { + "actions": [ + "StepInto", + "StepInto", + "StepInto", + "StepInto", + "StepInto", + "StepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 13, + "nextInstruction": "MEMCPY", + "evaluationStack": [ + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 0 + }, + { + "type": "ByteString", + "value": "0x1111" + }, + { + "type": "Integer", + "value": 1048576 + }, + { + "type": "Array", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "No push", + "script": [ + "MEMCPY" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH4", + "NEWBUFFER", + "DUP", + "PUSH1", + "PUSHDATA1", + "0x02", + "0x1111", + "PUSH0", + "PUSH2", + "MEMCPY" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Buffer", + "value": "0x00111100" + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Splice/NEWBUFFER.json b/tests/Neo.VM.Tests/Tests/OpCodes/Splice/NEWBUFFER.json new file mode 100644 index 0000000000..f3c60bb802 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Splice/NEWBUFFER.json @@ -0,0 +1,159 @@ +{ + "category": "Splice", + "name": "NEWBUFFER", + "tests": [ + { + "name": "Max Item Length", + "script": [ + "PUSHINT256", + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "NEWBUFFER" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 33, + "nextInstruction": "NEWBUFFER", + "evaluationStack": [ + { + "type": "Integer", + "value": "57896044618658097711785492504343953926634992332820282019728792003956564819967" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "-1 Length", + "script": [ + "PUSHM1", + "NEWBUFFER" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "NEWBUFFER", + "evaluationStack": [ + { + "type": "Integer", + "value": -1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong type [Array]", + "script": [ + "PUSH0", + "NEWARRAY", + "NEWBUFFER" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "NEWBUFFER", + "evaluationStack": [ + { + "type": "Array", + "value": [] + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "No push", + "script": [ + "NEWBUFFER" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test [Integer]", + "script": [ + "PUSH10", + "NEWBUFFER" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Buffer", + "value": "0x00000000000000000000" + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Splice/RIGHT.json b/tests/Neo.VM.Tests/Tests/OpCodes/Splice/RIGHT.json new file mode 100644 index 0000000000..6c30ea8c91 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Splice/RIGHT.json @@ -0,0 +1,299 @@ +{ + "category": "Splice", + "name": "RIGHT", + "tests": [ + { + "name": "Without push", + "script": [ + "PUSH11", + "RIGHT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong type [Map]", + "script": [ + "PUSH4", + "NEWMAP", + "RIGHT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "RIGHT", + "evaluationStack": [ + { + "type": "map", + "value": {} + }, + { + "type": "integer", + "value": 4 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Negative value", + "script": [ + "PUSHDATA1", + "0x03", + "0x010203", + "PUSHM1", + "RIGHT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "RIGHT", + "evaluationStack": [ + { + "type": "integer", + "value": -1 + }, + { + "type": "ByteString", + "value": "0x010203" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Overflow string", + "script": [ + "PUSHDATA1", + "0x03", + "0x010203", + "PUSH4", + "RIGHT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "RIGHT", + "evaluationStack": [ + { + "type": "integer", + "value": 4 + }, + { + "type": "ByteString", + "value": "0x010203" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSHDATA1", + "0x03", + "0x010203", + "PUSH2", + "RIGHT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "RIGHT", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "ByteString", + "value": "0x010203" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Buffer", + "value": "0x0203" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Buffer", + "value": "0x0203" + } + ] + } + } + ] + }, + { + "name": "Real test [whole input]", + "script": [ + "PUSHDATA1", + "0x03", + "0x010203", + "PUSH3", + "RIGHT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "RIGHT", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "ByteString", + "value": "0x010203" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 7, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Buffer", + "value": "0x010203" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Buffer", + "value": "0x010203" + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Splice/SUBSTR.json b/tests/Neo.VM.Tests/Tests/OpCodes/Splice/SUBSTR.json new file mode 100644 index 0000000000..7b78d7e053 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Splice/SUBSTR.json @@ -0,0 +1,478 @@ +{ + "category": "Splice", + "name": "SUBSTR", + "tests": [ + { + "name": "Without 3 items", + "script": [ + "PUSH2", + "PUSH3", + "SUBSTR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "SUBSTR", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With negative count", + "script": [ + "PUSH0", + "PUSH0", + "PUSHM1", + "SUBSTR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "SUBSTR", + "evaluationStack": [ + { + "type": "integer", + "value": -1 + }, + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With map as string", + "script": [ + "NEWMAP", + "PUSH0", + "PUSH0", + "SUBSTR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "SUBSTR", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 0 + }, + { + "type": "map", + "value": {} + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With map as count", + "script": [ + "PUSH0", + "PUSH0", + "NEWMAP", + "SUBSTR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "SUBSTR", + "evaluationStack": [ + { + "type": "map", + "value": {} + }, + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With map as index", + "script": [ + "PUSH0", + "NEWMAP", + "PUSH0", + "SUBSTR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "SUBSTR", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "map", + "value": {} + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With negative index", + "script": [ + "PUSH0", + "PUSHM1", + "PUSH0", + "SUBSTR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "SUBSTR", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": -1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Overflow string index", + "script": [ + "PUSHDATA1", + "0x02", + "0x0001", + "PUSH9", + "PUSH2", + "SUBSTR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "SUBSTR", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 9 + }, + { + "type": "ByteString", + "value": "0x0001" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Overflow string count", + "script": [ + "PUSHDATA1", + "0x0a", + "0x00010203040506070809", + "PUSH2", + "PUSH9", + "SUBSTR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 14, + "nextInstruction": "SUBSTR", + "evaluationStack": [ + { + "type": "integer", + "value": 9 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "ByteString", + "value": "0x00010203040506070809" + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSHDATA1", + "0x0a", + "0x00010203040506070809", + "PUSH2", + "PUSH1", + "SUBSTR" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 14, + "nextInstruction": "SUBSTR", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "ByteString", + "value": "0x00010203040506070809" + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Buffer", + "value": "0x02" + } + ] + } + } + ] + }, + { + "name": "Integer overflow Test", + "script": [ + "PUSHDATA1", + "0xff", + "0x414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141", + "PUSHDATA1", + "0x04", + "0xfd000000", + "PUSHDATA1", + "0x04", + "0x03ffff7f", + "SUBSTR" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Stack/CLEAR.json b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/CLEAR.json new file mode 100644 index 0000000000..36e9fcdd09 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/CLEAR.json @@ -0,0 +1,59 @@ +{ + "category": "Stack", + "name": "CLEAR", + "tests": [ + { + "name": "Without push", + "script": [ + "CLEAR" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "With push", + "script": [ + "PUSH2", + "CLEAR" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "CLEAR", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Stack/DEPTH.json b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/DEPTH.json new file mode 100644 index 0000000000..9a2ed2f774 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/DEPTH.json @@ -0,0 +1,223 @@ +{ + "category": "Stack", + "name": "DEPTH", + "tests": [ + { + "name": "Without push", + "script": [ + "DEPTH" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + } + ] + }, + { + "name": "With push", + "script": [ + "PUSH2", + "DEPTH" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "DEPTH", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + } + ] + } + } + ] + }, + { + "name": "3xDEPTH", + "script": [ + "DEPTH", + "DEPTH", + "DEPTH" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "DEPTH", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "DEPTH", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 0 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Stack/DROP.json b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/DROP.json new file mode 100644 index 0000000000..804a710958 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/DROP.json @@ -0,0 +1,73 @@ +{ + "category": "Stack", + "name": "DROP", + "tests": [ + { + "name": "Without push", + "script": [ + "DROP" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without push", + "script": [ + "PUSH5", + "DROP" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "DROP", + "evaluationStack": [ + { + "type": "integer", + "value": 5 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Stack/NIP.json b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/NIP.json new file mode 100644 index 0000000000..e363d56444 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/NIP.json @@ -0,0 +1,132 @@ +{ + "category": "Stack", + "name": "NIP", + "tests": [ + { + "name": "Without push", + "script": [ + "NIP" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without two stack items", + "script": [ + "PUSH5", + "NIP" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "NIP", + "evaluationStack": [ + { + "type": "integer", + "value": 5 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH0", + "PUSHDATA1", + "0x09", + "0x000000000000000000", + "NOT", + "NIP" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 13, + "nextInstruction": "NIP", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + }, + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 14, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Stack/OVER.json b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/OVER.json new file mode 100644 index 0000000000..03ec0d16ee --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/OVER.json @@ -0,0 +1,243 @@ +{ + "category": "Stack", + "name": "OVER", + "tests": [ + { + "name": "Without push", + "script": [ + "OVER" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With one push", + "script": [ + "PUSH0", + "OVER" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "OVER", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test with 2 items", + "script": [ + "PUSH1", + "PUSH2", + "OVER" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "OVER", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Real test with 3 items", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "OVER" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "OVER", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Stack/PICK.json b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/PICK.json new file mode 100644 index 0000000000..5791acec70 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/PICK.json @@ -0,0 +1,397 @@ +{ + "category": "Stack", + "name": "PICK", + "tests": [ + { + "name": "Without push", + "script": [ + "PICK" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Pick outside", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "PUSH4", + "PICK" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "PICK", + "evaluationStack": [ + { + "type": "integer", + "value": 4 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Less than 0", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "PUSHM1", + "PICK" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "PICK", + "evaluationStack": [ + { + "type": "integer", + "value": -1 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong type", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "NEWMAP", + "PICK" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "PICK", + "evaluationStack": [ + { + "type": "map", + "value": {} + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH3", + "PUSH2", + "PUSH1", + "PUSH2", + "PICK" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "PICK", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + } + ] + } + } + ] + }, + { + "name": "Real test with 0", + "script": [ + "PUSH3", + "PUSH2", + "PUSH1", + "PUSH0", + "PICK" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "PICK", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Stack/REVERSE3.json b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/REVERSE3.json new file mode 100644 index 0000000000..1260a70d68 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/REVERSE3.json @@ -0,0 +1,85 @@ +{ + "category": "Stack", + "name": "REVERSE3", + "tests": [ + { + "name": "Without push", + "script": [ + "REVERSE3" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With push", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "REVERSE3" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "REVERSE3", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Stack/REVERSE4.json b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/REVERSE4.json new file mode 100644 index 0000000000..d3e04e437e --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/REVERSE4.json @@ -0,0 +1,95 @@ +{ + "category": "Stack", + "name": "REVERSE4", + "tests": [ + { + "name": "Without push", + "script": [ + "REVERSE4" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With push", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "PUSH4", + "REVERSE4" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "REVERSE4", + "evaluationStack": [ + { + "type": "integer", + "value": 4 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 4 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Stack/REVERSEN.json b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/REVERSEN.json new file mode 100644 index 0000000000..e9a1518826 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/REVERSEN.json @@ -0,0 +1,201 @@ +{ + "category": "Stack", + "name": "REVERSEN", + "tests": [ + { + "name": "Without push", + "script": [ + "REVERSEN" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "N = -1", + "script": [ + "PUSH1", + "PUSHM1", + "REVERSEN" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "N < DEPTH", + "script": [ + "PUSH1", + "REVERSEN" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT", + "resultStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Reverse 0 item", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "PUSH0", + "REVERSEN" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "REVERSEN", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Reverse 3 items", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "PUSH3", + "REVERSEN" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "REVERSEN", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Stack/ROLL.json b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/ROLL.json new file mode 100644 index 0000000000..882c577d4a --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/ROLL.json @@ -0,0 +1,398 @@ +{ + "category": "Stack", + "name": "ROLL", + "tests": [ + { + "name": "Without push", + "script": [ + "ROLL" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With -1", + "script": [ + "PUSHM1", + "ROLL" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Pick outside", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "PUSH4", + "ROLL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "ROLL", + "evaluationStack": [ + { + "type": "integer", + "value": 4 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Less than 0", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "PUSHM1", + "ROLL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "ROLL", + "evaluationStack": [ + { + "type": "integer", + "value": -1 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With 0", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "PUSH0", + "ROLL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "ROLL", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Wrong type", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "NEWMAP", + "ROLL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "ROLL", + "evaluationStack": [ + { + "type": "map", + "value": {} + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH3", + "PUSH2", + "PUSH1", + "PUSH2", + "ROLL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "ROLL", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Stack/ROT.json b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/ROT.json new file mode 100644 index 0000000000..48c8771af3 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/ROT.json @@ -0,0 +1,193 @@ +{ + "category": "Stack", + "name": "ROT", + "tests": [ + { + "name": "Without push", + "script": [ + "ROT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With one push", + "script": [ + "PUSH0", + "ROT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "ROT", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With 2 items", + "script": [ + "PUSH1", + "PUSH2", + "ROT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "ROT", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test with 3 items", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "ROT" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "ROT", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Stack/SWAP.json b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/SWAP.json new file mode 100644 index 0000000000..c5e93a9b59 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/SWAP.json @@ -0,0 +1,209 @@ +{ + "category": "Stack", + "name": "SWAP", + "tests": [ + { + "name": "Without push", + "script": [ + "SWAP" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With one push", + "script": [ + "PUSH0", + "SWAP" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "SWAP", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test with 2 items", + "script": [ + "PUSH1", + "PUSH2", + "SWAP" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "SWAP", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test with 3 items", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "SWAP" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "SWAP", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Stack/TUCK.json b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/TUCK.json new file mode 100644 index 0000000000..031855f31f --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/TUCK.json @@ -0,0 +1,243 @@ +{ + "category": "Stack", + "name": "TUCK", + "tests": [ + { + "name": "Without push", + "script": [ + "TUCK" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Outside", + "script": [ + "PUSH0", + "TUCK" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "TUCK", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test - Last item", + "script": [ + "PUSH1", + "PUSH2", + "TUCK" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "TUCK", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + } + ] + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH1", + "PUSH2", + "PUSH3", + "TUCK" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "TUCK", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Stack/XDROP.json b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/XDROP.json new file mode 100644 index 0000000000..8666fed600 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Stack/XDROP.json @@ -0,0 +1,238 @@ +{ + "category": "Stack", + "name": "XDROP", + "tests": [ + { + "name": "Without push", + "script": [ + "XDROP" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "With -1", + "script": [ + "PUSHM1", + "XDROP" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Overflow drop", + "script": [ + "PUSH3", + "PUSH2", + "PUSH1", + "PUSH3", + "XDROP" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "XDROP", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Wrong index type [Map]", + "script": [ + "PUSH3", + "PUSH2", + "PUSH1", + "NEWMAP", + "XDROP" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "XDROP", + "evaluationStack": [ + { + "type": "map", + "value": {} + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Real test", + "script": [ + "PUSH3", + "PUSH2", + "PUSH1", + "PUSH1", + "XDROP" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "XDROP", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 1 + }, + { + "type": "integer", + "value": 3 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Types/CONVERT.json b/tests/Neo.VM.Tests/Tests/OpCodes/Types/CONVERT.json new file mode 100644 index 0000000000..473ed8bba4 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Types/CONVERT.json @@ -0,0 +1,898 @@ +{ + "category": "Types", + "name": "CONVERT", + "tests": [ + { + "name": "Null to Buffer", + "script": [ + "PUSHNULL", + "CONVERT", + "0x30" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "CONVERT", + "evaluationStack": [ + { + "type": "null" + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "null" + } + ] + } + } + ] + }, + { + "name": "Null to Boolean", + "script": [ + "PUSHNULL", + "CONVERT", + "0x20" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "CONVERT", + "evaluationStack": [ + { + "type": "null" + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "null" + } + ] + } + } + ] + }, + { + "name": "Struct to Array", + "script": [ + "PUSH1", + "NEWSTRUCT", + "DUP", + "PUSH0", + "PUSH5", + "SETITEM", + "CONVERT", + "0x40" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "CONVERT", + "evaluationStack": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + } + ] + }, + { + "name": "Struct to Boolean", + "script": [ + "PUSH0", + "NEWSTRUCT", + "CONVERT", + "0x20" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "CONVERT", + "evaluationStack": [ + { + "type": "struct", + "value": [ + ] + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Array to Boolean", + "script": [ + "PUSH0", + "NEWARRAY", + "CONVERT", + "0x20" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "CONVERT", + "evaluationStack": [ + { + "type": "array", + "value": [ + ] + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Array to Struct", + "script": [ + "PUSH1", + "NEWARRAY", + "DUP", + "PUSH0", + "PUSH5", + "SETITEM", + "CONVERT", + "0x41" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "CONVERT", + "evaluationStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "struct", + "value": [ + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + } + ] + }, + { + "name": "Array to Integer", + "script": [ + "PUSH1", + "NEWARRAY", + "DUP", + "PUSH0", + "PUSH4", + "SETITEM", + "CONVERT", + "0x21" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "CONVERT", + "evaluationStack": [ + { + "type": "array", + "value": [ + { + "type": "Integer", + "value": 4 + } + ] + } + ] + } + ] + } + }, + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Integer to ByteString", + "script": [ + "PUSHINT8", + "0x0A", + "CONVERT", + "0x28" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "ByteString", + "value": "0x0A" + } + ] + } + } + ] + }, + { + "name": "Integer to Boolean", + "script": [ + "PUSHINT8", + "0x0A", + "CONVERT", + "0x20" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Integer to Boolean", + "script": [ + "PUSHINT8", + "0x00", + "CONVERT", + "0x20" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Integer to Boolean", + "script": [ + "PUSHINT8", + "0x01", + "CONVERT", + "0x20" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Integer to Buffer", + "script": [ + "PUSHINT8", + "0x0A", + "CONVERT", + "0x30" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Buffer", + "value": "0x0A" + } + ] + } + } + ] + }, + { + "name": "ByteString to Buffer", + "script": [ + "PUSHDATA1", + "0x0A", + "0x00000000000000000000", + "CONVERT", + "0x30" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Buffer", + "value": "0x00000000000000000000" + } + ] + } + } + ] + }, + { + "name": "ByteString to Integer", + "script": [ + "PUSHDATA1", + "0x02", + "0x0A0B", + "CONVERT", + "0x21" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 2826 + } + ] + } + } + ] + }, + { + "name": "ByteString to Boolean", + "script": [ + "PUSHDATA1", + "0x02", + "0x0A0B", + "CONVERT", + "0x20" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "ByteString to Boolean", + "script": [ + "PUSHDATA1", + "0x02", + "0x0000", + "CONVERT", + "0x20" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "ByteString to Boolean", + "script": [ + "PUSHDATA1", + "0x02", + "0x0001", + "CONVERT", + "0x20" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "ByteString to Boolean", + "script": [ + "PUSHDATA1", + "0x01", + "0x01", + "CONVERT", + "0x20" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Buffer to ByteString", + "script": [ + "PUSHDATA1", + "0x0A", + "0x00000000000000000000", + "CONVERT", + "0x30", + "CONVERT", + "0x28" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "ByteString", + "value": "0x00000000000000000000" + } + ] + } + } + ] + }, + { + "name": "Buffer to Boolean", + "script": [ + "PUSHDATA1", + "0x01", + "0x00", + "CONVERT", + "0x30", + "CONVERT", + "0x20" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Buffer to Boolean", + "script": [ + "PUSHDATA1", + "0x01", + "0x02", + "CONVERT", + "0x30", + "CONVERT", + "0x20" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Buffer to Integer", + "script": [ + "PUSHDATA1", + "0x02", + "0x0102", + "CONVERT", + "0x30", + "CONVERT", + "0x21" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Integer", + "value": 513 + } + ] + } + } + ] + }, + { + "name": "Buffer to Integer (Exceed)", + "script": [ + "PUSHDATA1", + "0x21", + "0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "CONVERT", + "0x30", + "CONVERT", + "0x21" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Buffer to Any", + "script": [ + "PUSHDATA1", + "0x02", + "0x0102", + "CONVERT", + "0x30", + "CONVERT", + "0x00" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Buffer to Interop", + "script": [ + "PUSHDATA1", + "0x02", + "0x0102", + "CONVERT", + "0x30", + "CONVERT", + "0x60" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "ByteString to non-defined", + "script": [ + "PUSHDATA1", + "0x01", + "0xAA", + "CONVERT", + "0xFF" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Array to non-defined", + "script": [ + "NEWARRAY0", + "CONVERT", + "0xFF" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Struct to non-defined", + "script": [ + "NEWSTRUCT0", + "CONVERT", + "0xFF" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Null to non-defined", + "script": [ + "PUSHNULL", + "CONVERT", + "0xFF" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Types/ISNULL.json b/tests/Neo.VM.Tests/Tests/OpCodes/Types/ISNULL.json new file mode 100644 index 0000000000..b87fa84726 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Types/ISNULL.json @@ -0,0 +1,147 @@ +{ + "category": "Types", + "name": "ISNULL", + "tests": [ + { + "name": "Without push", + "script": [ + "ISNULL" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Good definition", + "script": [ + "PUSHNULL", + "ISNULL" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "ISNULL", + "evaluationStack": [ + { + "type": "null" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "With empty ByteString", + "script": [ + "PUSH0", + "ISNULL" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "ISNULL", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Types/ISTYPE.json b/tests/Neo.VM.Tests/Tests/OpCodes/Types/ISTYPE.json new file mode 100644 index 0000000000..6bdbd7495c --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Types/ISTYPE.json @@ -0,0 +1,226 @@ +{ + "category": "Types", + "name": "ISTYPE", + "tests": [ + { + "name": "Array", + "script": [ + "NEWARRAY0", + "ISTYPE", + "0x40" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Buffer", + "script": [ + "PUSH0", + "NEWBUFFER", + "ISTYPE", + "0x30" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "ByteString", + "script": [ + "PUSHDATA1", + "0x00", + "ISTYPE", + "0x28" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Integer", + "script": [ + "PUSH0", + "ISTYPE", + "0x21" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "InteropInterface", + "script": [ + "SYSCALL", + "0x77777777", + "ISTYPE", + "0x60" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Map", + "script": [ + "NEWMAP", + "ISTYPE", + "0x48" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Null", + "script": [ + "PUSHNULL", + "ISTYPE", + "0x20" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + } + ] + }, + { + "name": "Pointer", + "script": [ + "PUSHA", + "0x00000000", + "ISTYPE", + "0x10" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + }, + { + "name": "Struct", + "script": [ + "NEWSTRUCT0", + "ISTYPE", + "0x41" + ], + "steps": [ + { + "actions": [ + "Execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/Others/Debugger.json b/tests/Neo.VM.Tests/Tests/Others/Debugger.json new file mode 100644 index 0000000000..7dd1096d55 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/Others/Debugger.json @@ -0,0 +1,426 @@ +{ + "category": "Others", + "name": "Debugger", + "tests": [ + { + "name": "Step Into", + "script": [ + "PUSH1", + "CALL", + "0x04", + "PUSH3", + "RET", + "PUSH2", + "RET" + ], + "steps": [ + { + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 0, + "nextInstruction": "PUSH1" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "CALL", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "PUSH2", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + }, + { + "instructionPointer": 3, + "nextInstruction": "PUSH3", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 6, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + }, + { + "instructionPointer": 3, + "nextInstruction": "PUSH3", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "PUSH3", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Step Out", + "script": [ + "PUSH1", + "CALL", + "0x04", + "PUSH3", + "RET", + "PUSH2", + "RET" + ], + "steps": [ + { + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 0, + "nextInstruction": "PUSH1" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "CALL", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepOut" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Step Over", + "script": [ + "PUSH1", + "CALL", + "0x04", + "PUSH3", + "RET", + "PUSH2", + "RET" + ], + "steps": [ + { + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 0, + "nextInstruction": "PUSH1" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "CALL", + "evaluationStack": [ + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepOver" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "PUSH3", + "evaluationStack": [ + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepOver" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET", + "evaluationStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": [ + "stepOver" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Execute", + "script": [ + "PUSH1", + "CALL", + "0x04", + "PUSH3", + "RET", + "PUSH2", + "RET" + ], + "steps": [ + { + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 0, + "nextInstruction": "PUSH1" + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "resultStack": [ + { + "type": "integer", + "value": 3 + }, + { + "type": "integer", + "value": 2 + }, + { + "type": "integer", + "value": 1 + } + ] + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/Others/Init.json b/tests/Neo.VM.Tests/Tests/Others/Init.json new file mode 100644 index 0000000000..ab95168cde --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/Others/Init.json @@ -0,0 +1,41 @@ +{ + "category": "Others", + "name": "Init", + "tests": [ + { + "name": "Init script", + "script": [ + "RET" + ], + "steps": [ + { + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 0, + "nextInstruction": "RET" + } + ] + } + } + ] + }, + { + "name": "Init script", + "script": [ + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/Others/InvocationLimits.json b/tests/Neo.VM.Tests/Tests/Others/InvocationLimits.json new file mode 100644 index 0000000000..5939dabf80 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/Others/InvocationLimits.json @@ -0,0 +1,120 @@ +{ + "category": "Limits", + "name": "Invocation limits", + "tests": [ + { + "name": "More than 1024 ExecutionContext", + "script": [ + "INITSSLOT", + "0x01", + "PUSHDATA1", + "0x02", + "0x0004", + "INC", + "STSFLD0", + "LDSFLD0", + "DEC", + "DUP", + "STSFLD0", + "JMPIFNOT", + "0x04", + "CALL", + "0xfa", + "RET" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "LDSFLD0", + "staticFields": [ + { + "type": "integer", + "value": 1025 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 12, + "nextInstruction": "JMPIFNOT", + "evaluationStack": [ + { + "type": "integer", + "value": 1024 + } + ], + "staticFields": [ + { + "type": "integer", + "value": 1024 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 8, + "nextInstruction": "LDSFLD0", + "staticFields": [ + { + "type": "integer", + "value": 1024 + } + ] + }, + { + "instructionPointer": 16, + "nextInstruction": "RET", + "staticFields": [ + { + "type": "integer", + "value": 1024 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/Others/OtherCases.json b/tests/Neo.VM.Tests/Tests/Others/OtherCases.json new file mode 100644 index 0000000000..693bf7cba7 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/Others/OtherCases.json @@ -0,0 +1,36 @@ +{ + "category": "Limits", + "name": "OtherCases", + "tests": [ + { + "name": "Wrong script", + "script": [ + "0xff" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Without script", + "script": [], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/Others/ScriptLogic.json b/tests/Neo.VM.Tests/Tests/Others/ScriptLogic.json new file mode 100644 index 0000000000..a49be7a8bf --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/Others/ScriptLogic.json @@ -0,0 +1,99 @@ +{ + "category": "Others", + "name": "ScriptLogic", + "tests": [ + { + "name": "Script logic", + "script": [ + "PUSH0", + "NOT", + "NOT", + "DROP" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 1, + "nextInstruction": "NOT", + "evaluationStack": [ + { + "type": "integer", + "value": 0 + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 2, + "nextInstruction": "NOT", + "evaluationStack": [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "DROP", + "evaluationStack": [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "RET" + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "HALT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/Others/StackItemLimits.json b/tests/Neo.VM.Tests/Tests/Others/StackItemLimits.json new file mode 100644 index 0000000000..77293f9f89 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/Others/StackItemLimits.json @@ -0,0 +1,71 @@ +{ + "category": "Limits", + "name": "Stack item limits [StackItemLimits] [StackItemLimits] [StackItemLimits]", + "tests": [ + { + "name": "Max boolean ByteString", + "script": [ + "PUSHDATA1", + "0x21", + "0x000000000000000000000000000000000000000000000000000000000000000000", + "NOT" + ], + "steps": [ + { + "actions": [ + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 35, + "nextInstruction": "NOT", + "evaluationStack": [ + { + "type": "ByteString", + "value": "0x000000000000000000000000000000000000000000000000000000000000000000" + } + ] + } + ] + } + }, + { + "actions": [ + "stepInto" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Max items with PUSHDATA (2048+1)", + "script": [ + "PUSHINT16", + "0x0004", + "NEWARRAY", + "UNPACK", + "PUSHINT16", + "0xfe03", + "NEWARRAY", + "UNPACK", + "PUSHDATA1", + "0x01", + "0x01" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Tests/Others/StackLimits.json b/tests/Neo.VM.Tests/Tests/Others/StackLimits.json new file mode 100644 index 0000000000..8191112a26 --- /dev/null +++ b/tests/Neo.VM.Tests/Tests/Others/StackLimits.json @@ -0,0 +1,6181 @@ +{ + "category": "Limits", + "name": "Stack limits [StackLimits] [StackLimits] [StackLimits]", + "tests": [ + { + "name": "Good: 2048 PUSH1 + 2048 DROP", + "script": [ + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP", + "DROP" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT" + } + } + ] + }, + { + "name": "Bad: 2049 PUSH1", + "script": [ + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1", + "PUSH1" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + } + ] +} diff --git a/tests/Neo.VM.Tests/Types/TestEngine.cs b/tests/Neo.VM.Tests/Types/TestEngine.cs new file mode 100644 index 0000000000..cf99314892 --- /dev/null +++ b/tests/Neo.VM.Tests/Types/TestEngine.cs @@ -0,0 +1,56 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TestEngine.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.VM; +using Neo.VM.Types; +using System; + +namespace Neo.Test.Types +{ + class TestEngine : ExecutionEngine + { + public Exception FaultException { get; private set; } + + public TestEngine() : base(ComposeJumpTable()) { } + + private static JumpTable ComposeJumpTable() + { + JumpTable jumpTable = new JumpTable(); + jumpTable[OpCode.SYSCALL] = OnSysCall; + return jumpTable; + } + + private static void OnSysCall(ExecutionEngine engine, Instruction instruction) + { + uint method = instruction.TokenU32; + + if (method == 0x77777777) + { + engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new object())); + return; + } + + if (method == 0xaddeadde) + { + engine.JumpTable.ExecuteThrow(engine, "error"); + return; + } + + throw new Exception(); + } + + protected override void OnFault(Exception ex) + { + FaultException = ex; + base.OnFault(ex); + } + } +} diff --git a/tests/Neo.VM.Tests/Types/VMUT.cs b/tests/Neo.VM.Tests/Types/VMUT.cs new file mode 100644 index 0000000000..b24255d372 --- /dev/null +++ b/tests/Neo.VM.Tests/Types/VMUT.cs @@ -0,0 +1,27 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// VMUT.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 Newtonsoft.Json; + +namespace Neo.Test.Types +{ + public class VMUT + { + [JsonProperty] + public string Category { get; set; } + + [JsonProperty] + public string Name { get; set; } + + [JsonProperty] + public VMUTEntry[] Tests { get; set; } + } +} diff --git a/tests/Neo.VM.Tests/Types/VMUTActionType.cs b/tests/Neo.VM.Tests/Types/VMUTActionType.cs new file mode 100644 index 0000000000..5ac8b5b48e --- /dev/null +++ b/tests/Neo.VM.Tests/Types/VMUTActionType.cs @@ -0,0 +1,24 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// VMUTActionType.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.Test.Types +{ + public enum VMUTActionType + { + Execute, + + // Steps + + StepInto, + StepOut, + StepOver, + } +} diff --git a/tests/Neo.VM.Tests/Types/VMUTEntry.cs b/tests/Neo.VM.Tests/Types/VMUTEntry.cs new file mode 100644 index 0000000000..7761b08de5 --- /dev/null +++ b/tests/Neo.VM.Tests/Types/VMUTEntry.cs @@ -0,0 +1,28 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// VMUTEntry.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.Converters; +using Newtonsoft.Json; + +namespace Neo.Test.Types +{ + public class VMUTEntry + { + [JsonProperty(Order = 1)] + public string Name { get; set; } + + [JsonProperty(Order = 2), JsonConverter(typeof(ScriptConverter))] + public byte[] Script { get; set; } + + [JsonProperty(Order = 3)] + public VMUTStep[] Steps { get; set; } + } +} diff --git a/tests/Neo.VM.Tests/Types/VMUTExecutionContextState.cs b/tests/Neo.VM.Tests/Types/VMUTExecutionContextState.cs new file mode 100644 index 0000000000..101142dc3a --- /dev/null +++ b/tests/Neo.VM.Tests/Types/VMUTExecutionContextState.cs @@ -0,0 +1,42 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// VMUTExecutionContextState.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.Converters; +using Neo.VM; +using Newtonsoft.Json; + +namespace Neo.Test.Types +{ + public class VMUTExecutionContextState + { + [JsonProperty] + public int InstructionPointer { get; set; } + + [JsonProperty, JsonConverter(typeof(UppercaseEnum))] + public OpCode NextInstruction { get; set; } + + // Stacks + + [JsonProperty] + public VMUTStackItem[] EvaluationStack { get; set; } + + // Slots + + [JsonProperty] + public VMUTStackItem[] StaticFields { get; set; } + + [JsonProperty] + public VMUTStackItem[] Arguments { get; set; } + + [JsonProperty] + public VMUTStackItem[] LocalVariables { get; set; } + } +} diff --git a/tests/Neo.VM.Tests/Types/VMUTExecutionEngineState.cs b/tests/Neo.VM.Tests/Types/VMUTExecutionEngineState.cs new file mode 100644 index 0000000000..3f931b3c32 --- /dev/null +++ b/tests/Neo.VM.Tests/Types/VMUTExecutionEngineState.cs @@ -0,0 +1,32 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// VMUTExecutionEngineState.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.Converters; +using Neo.VM; +using Newtonsoft.Json; + +namespace Neo.Test.Types +{ + public class VMUTExecutionEngineState + { + [JsonProperty, JsonConverter(typeof(UppercaseEnum))] + public VMState State { get; set; } + + [JsonProperty] + public VMUTStackItem[] ResultStack { get; set; } + + [JsonProperty] + public VMUTExecutionContextState[] InvocationStack { get; set; } + + [JsonProperty] + public string ExceptionMessage { get; set; } + } +} diff --git a/tests/Neo.VM.Tests/Types/VMUTStackItem.cs b/tests/Neo.VM.Tests/Types/VMUTStackItem.cs new file mode 100644 index 0000000000..5ce55bbfd3 --- /dev/null +++ b/tests/Neo.VM.Tests/Types/VMUTStackItem.cs @@ -0,0 +1,25 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// VMUTStackItem.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 Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Neo.Test.Types +{ + public class VMUTStackItem + { + [JsonProperty] + public VMUTStackItemType Type { get; set; } + + [JsonProperty] + public JToken Value { get; set; } + } +} diff --git a/tests/Neo.VM.Tests/Types/VMUTStackItemType.cs b/tests/Neo.VM.Tests/Types/VMUTStackItemType.cs new file mode 100644 index 0000000000..845c062a02 --- /dev/null +++ b/tests/Neo.VM.Tests/Types/VMUTStackItemType.cs @@ -0,0 +1,71 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// VMUTStackItemType.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.Test.Types +{ + public enum VMUTStackItemType + { + /// + /// Null + /// + Null, + + /// + /// An address of function + /// + Pointer, + + /// + /// Boolean (true,false) + /// + Boolean, + + /// + /// ByteString + /// + ByteString, + + /// + /// ByteString as UTF8 string + /// + String, + + /// + /// Mutable byte array + /// + Buffer, + + /// + /// InteropInterface + /// + Interop, + + /// + /// BigInteger + /// + Integer, + + /// + /// Array + /// + Array, + + /// + /// Struct + /// + Struct, + + /// + /// Map + /// + Map + } +} diff --git a/tests/Neo.VM.Tests/Types/VMUTStep.cs b/tests/Neo.VM.Tests/Types/VMUTStep.cs new file mode 100644 index 0000000000..ad49606d4b --- /dev/null +++ b/tests/Neo.VM.Tests/Types/VMUTStep.cs @@ -0,0 +1,27 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// VMUTStep.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 Newtonsoft.Json; + +namespace Neo.Test.Types +{ + public class VMUTStep + { + [JsonProperty] + public string Name { get; set; } + + [JsonProperty] + public VMUTActionType[] Actions { get; set; } + + [JsonProperty] + public VMUTExecutionEngineState Result { get; set; } + } +} diff --git a/tests/Neo.VM.Tests/UT_Debugger.cs b/tests/Neo.VM.Tests/UT_Debugger.cs new file mode 100644 index 0000000000..64f543f65e --- /dev/null +++ b/tests/Neo.VM.Tests/UT_Debugger.cs @@ -0,0 +1,218 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Debugger.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.VisualStudio.TestTools.UnitTesting; +using Neo.VM; + +namespace Neo.Test +{ + [TestClass] + public class UT_Debugger + { + [TestMethod] + public void TestBreakPoint() + { + using ExecutionEngine engine = new(); + using ScriptBuilder script = new(); + script.Emit(OpCode.NOP); + script.Emit(OpCode.NOP); + script.Emit(OpCode.NOP); + script.Emit(OpCode.NOP); + + engine.LoadScript(script.ToArray()); + + Debugger debugger = new(engine); + + Assert.IsFalse(debugger.RemoveBreakPoint(engine.CurrentContext.Script, 3)); + + Assert.AreEqual(OpCode.NOP, engine.CurrentContext.NextInstruction.OpCode); + + debugger.AddBreakPoint(engine.CurrentContext.Script, 2); + debugger.AddBreakPoint(engine.CurrentContext.Script, 3); + debugger.Execute(); + Assert.AreEqual(OpCode.NOP, engine.CurrentContext.NextInstruction.OpCode); + Assert.AreEqual(2, engine.CurrentContext.InstructionPointer); + Assert.AreEqual(VMState.BREAK, engine.State); + + Assert.IsTrue(debugger.RemoveBreakPoint(engine.CurrentContext.Script, 2)); + Assert.IsFalse(debugger.RemoveBreakPoint(engine.CurrentContext.Script, 2)); + Assert.IsTrue(debugger.RemoveBreakPoint(engine.CurrentContext.Script, 3)); + Assert.IsFalse(debugger.RemoveBreakPoint(engine.CurrentContext.Script, 3)); + debugger.Execute(); + Assert.AreEqual(VMState.HALT, engine.State); + } + + [TestMethod] + public void TestWithoutBreakPoints() + { + using ExecutionEngine engine = new(); + using ScriptBuilder script = new(); + script.Emit(OpCode.NOP); + script.Emit(OpCode.NOP); + script.Emit(OpCode.NOP); + script.Emit(OpCode.NOP); + + engine.LoadScript(script.ToArray()); + + Debugger debugger = new(engine); + + Assert.AreEqual(OpCode.NOP, engine.CurrentContext.NextInstruction.OpCode); + + debugger.Execute(); + + Assert.IsNull(engine.CurrentContext); + Assert.AreEqual(VMState.HALT, engine.State); + } + + [TestMethod] + public void TestWithoutDebugger() + { + using ExecutionEngine engine = new(); + using ScriptBuilder script = new(); + script.Emit(OpCode.NOP); + script.Emit(OpCode.NOP); + script.Emit(OpCode.NOP); + script.Emit(OpCode.NOP); + + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(OpCode.NOP, engine.CurrentContext.NextInstruction.OpCode); + + engine.Execute(); + + Assert.IsNull(engine.CurrentContext); + Assert.AreEqual(VMState.HALT, engine.State); + } + + [TestMethod] + public void TestStepOver() + { + using ExecutionEngine engine = new(); + using ScriptBuilder script = new(); + /* ┌ CALL + │ ┌> NOT + │ │ RET + └> │ PUSH0 + └─┘ RET */ + script.EmitCall(4); + script.Emit(OpCode.NOT); + script.Emit(OpCode.RET); + script.Emit(OpCode.PUSH0); + script.Emit(OpCode.RET); + + engine.LoadScript(script.ToArray()); + + Debugger debugger = new(engine); + + Assert.AreEqual(OpCode.NOT, engine.CurrentContext.NextInstruction.OpCode); + Assert.AreEqual(VMState.BREAK, debugger.StepOver()); + Assert.AreEqual(2, engine.CurrentContext.InstructionPointer); + Assert.AreEqual(VMState.BREAK, engine.State); + Assert.AreEqual(OpCode.RET, engine.CurrentContext.NextInstruction.OpCode); + + debugger.Execute(); + + Assert.AreEqual(true, engine.ResultStack.Pop().GetBoolean()); + Assert.AreEqual(VMState.HALT, engine.State); + + // Test step over again + + Assert.AreEqual(VMState.HALT, debugger.StepOver()); + Assert.AreEqual(VMState.HALT, engine.State); + } + + [TestMethod] + public void TestStepInto() + { + using ExecutionEngine engine = new(); + using ScriptBuilder script = new(); + /* ┌ CALL + │ ┌> NOT + │ │ RET + └> │ PUSH0 + └─┘ RET */ + script.EmitCall(4); + script.Emit(OpCode.NOT); + script.Emit(OpCode.RET); + script.Emit(OpCode.PUSH0); + script.Emit(OpCode.RET); + + engine.LoadScript(script.ToArray()); + + Debugger debugger = new(engine); + + var context = engine.CurrentContext; + + Assert.AreEqual(context, engine.CurrentContext); + Assert.AreEqual(context, engine.EntryContext); + Assert.AreEqual(OpCode.NOT, engine.CurrentContext.NextInstruction.OpCode); + + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + + Assert.AreNotEqual(context, engine.CurrentContext); + Assert.AreEqual(context, engine.EntryContext); + Assert.AreEqual(OpCode.RET, engine.CurrentContext.NextInstruction.OpCode); + + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + + Assert.AreEqual(context, engine.CurrentContext); + Assert.AreEqual(context, engine.EntryContext); + Assert.AreEqual(OpCode.RET, engine.CurrentContext.NextInstruction.OpCode); + + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(VMState.HALT, debugger.StepInto()); + + Assert.AreEqual(true, engine.ResultStack.Pop().GetBoolean()); + Assert.AreEqual(VMState.HALT, engine.State); + + // Test step into again + + Assert.AreEqual(VMState.HALT, debugger.StepInto()); + Assert.AreEqual(VMState.HALT, engine.State); + } + + [TestMethod] + public void TestBreakPointStepOver() + { + using ExecutionEngine engine = new(); + using ScriptBuilder script = new(); + /* ┌ CALL + │ ┌> NOT + │ │ RET + └>X│ PUSH0 + └┘ RET */ + script.EmitCall(4); + script.Emit(OpCode.NOT); + script.Emit(OpCode.RET); + script.Emit(OpCode.PUSH0); + script.Emit(OpCode.RET); + + engine.LoadScript(script.ToArray()); + + Debugger debugger = new(engine); + + Assert.AreEqual(OpCode.NOT, engine.CurrentContext.NextInstruction.OpCode); + + debugger.AddBreakPoint(engine.CurrentContext.Script, 5); + Assert.AreEqual(VMState.BREAK, debugger.StepOver()); + + Assert.IsNull(engine.CurrentContext.NextInstruction); + Assert.AreEqual(5, engine.CurrentContext.InstructionPointer); + Assert.AreEqual(VMState.BREAK, engine.State); + + debugger.Execute(); + + Assert.AreEqual(true, engine.ResultStack.Pop().GetBoolean()); + Assert.AreEqual(VMState.HALT, engine.State); + } + } +} diff --git a/tests/Neo.VM.Tests/UT_EvaluationStack.cs b/tests/Neo.VM.Tests/UT_EvaluationStack.cs new file mode 100644 index 0000000000..51df42ffc1 --- /dev/null +++ b/tests/Neo.VM.Tests/UT_EvaluationStack.cs @@ -0,0 +1,225 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_EvaluationStack.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.VisualStudio.TestTools.UnitTesting; +using Neo.Test.Extensions; +using Neo.Test.Helpers; +using Neo.VM; +using Neo.VM.Types; +using System; +using System.Collections; +using System.Linq; + +namespace Neo.Test +{ + [TestClass] + public class UT_EvaluationStack + { + private static EvaluationStack CreateOrderedStack(int count) + { + var check = new Integer[count]; + var stack = new EvaluationStack(new ReferenceCounter()); + + for (int x = 1; x <= count; x++) + { + stack.Push(x); + check[x - 1] = x; + } + + Assert.AreEqual(count, stack.Count); + CollectionAssert.AreEqual(check, stack.ToArray()); + + return stack; + } + + public static IEnumerable GetEnumerable(IEnumerator enumerator) + { + while (enumerator.MoveNext()) yield return enumerator.Current; + } + + [TestMethod] + public void TestClear() + { + var stack = CreateOrderedStack(3); + stack.Clear(); + Assert.AreEqual(0, stack.Count); + } + + [TestMethod] + public void TestCopyTo() + { + var stack = CreateOrderedStack(3); + var copy = new EvaluationStack(new ReferenceCounter()); + + Assert.ThrowsException(() => stack.CopyTo(copy, -2)); + Assert.ThrowsException(() => stack.CopyTo(copy, 4)); + + stack.CopyTo(copy, 0); + + Assert.AreEqual(3, stack.Count); + Assert.AreEqual(0, copy.Count); + CollectionAssert.AreEqual(new Integer[] { 1, 2, 3 }, stack.ToArray()); + + stack.CopyTo(copy, -1); + + Assert.AreEqual(3, stack.Count); + Assert.AreEqual(3, copy.Count); + CollectionAssert.AreEqual(new Integer[] { 1, 2, 3 }, stack.ToArray()); + + // Test IEnumerable + + var enumerable = (IEnumerable)copy; + var enumerator = enumerable.GetEnumerator(); + + CollectionAssert.AreEqual(new Integer[] { 1, 2, 3 }, GetEnumerable(enumerator).Cast().ToArray()); + + copy.CopyTo(stack, 2); + + Assert.AreEqual(5, stack.Count); + Assert.AreEqual(3, copy.Count); + + CollectionAssert.AreEqual(new Integer[] { 1, 2, 3, 2, 3 }, stack.ToArray()); + CollectionAssert.AreEqual(new Integer[] { 1, 2, 3 }, copy.ToArray()); + } + + [TestMethod] + public void TestMoveTo() + { + var stack = CreateOrderedStack(3); + var other = new EvaluationStack(new ReferenceCounter()); + + stack.MoveTo(other, 0); + + Assert.AreEqual(3, stack.Count); + Assert.AreEqual(0, other.Count); + CollectionAssert.AreEqual(new Integer[] { 1, 2, 3 }, stack.ToArray()); + + stack.MoveTo(other, -1); + + Assert.AreEqual(0, stack.Count); + Assert.AreEqual(3, other.Count); + CollectionAssert.AreEqual(new Integer[] { 1, 2, 3 }, other.ToArray()); + + // Test IEnumerable + + var enumerable = (IEnumerable)other; + var enumerator = enumerable.GetEnumerator(); + + CollectionAssert.AreEqual(new Integer[] { 1, 2, 3 }, GetEnumerable(enumerator).Cast().ToArray()); + + other.MoveTo(stack, 2); + + Assert.AreEqual(2, stack.Count); + Assert.AreEqual(1, other.Count); + + CollectionAssert.AreEqual(new Integer[] { 2, 3 }, stack.ToArray()); + CollectionAssert.AreEqual(new Integer[] { 1 }, other.ToArray()); + } + + [TestMethod] + public void TestInsertPeek() + { + var stack = new EvaluationStack(new ReferenceCounter()); + + stack.Insert(0, 3); + stack.Insert(1, 1); + stack.Insert(1, 2); + + Assert.ThrowsException(() => stack.Insert(4, 2)); + + Assert.AreEqual(3, stack.Count); + CollectionAssert.AreEqual(new Integer[] { 1, 2, 3 }, stack.ToArray()); + + Assert.AreEqual(3, stack.Peek(0)); + Assert.AreEqual(2, stack.Peek(1)); + Assert.AreEqual(1, stack.Peek(-1)); + + Assert.ThrowsException(() => stack.Peek(-4)); + } + + [TestMethod] + public void TestPopPush() + { + var stack = CreateOrderedStack(3); + + Assert.AreEqual(3, stack.Pop()); + Assert.AreEqual(2, stack.Pop()); + Assert.AreEqual(1, stack.Pop()); + + Assert.ThrowsException(() => stack.Pop()); + + stack = CreateOrderedStack(3); + + Assert.IsTrue(stack.Pop().Equals(3)); + Assert.IsTrue(stack.Pop().Equals(2)); + Assert.IsTrue(stack.Pop().Equals(1)); + + Assert.ThrowsException(() => stack.Pop()); + } + + [TestMethod] + public void TestRemove() + { + var stack = CreateOrderedStack(3); + + Assert.IsTrue(stack.Remove(0).Equals(3)); + Assert.IsTrue(stack.Remove(0).Equals(2)); + Assert.IsTrue(stack.Remove(-1).Equals(1)); + + Assert.ThrowsException(() => stack.Remove(0)); + Assert.ThrowsException(() => stack.Remove(-1)); + } + + [TestMethod] + public void TestReverse() + { + var stack = CreateOrderedStack(3); + + stack.Reverse(3); + Assert.IsTrue(stack.Pop().Equals(1)); + Assert.IsTrue(stack.Pop().Equals(2)); + Assert.IsTrue(stack.Pop().Equals(3)); + Assert.ThrowsException(() => stack.Pop().Equals(0)); + + stack = CreateOrderedStack(3); + + Assert.ThrowsException(() => stack.Reverse(-1)); + Assert.ThrowsException(() => stack.Reverse(4)); + + stack.Reverse(1); + Assert.IsTrue(stack.Pop().Equals(3)); + Assert.IsTrue(stack.Pop().Equals(2)); + Assert.IsTrue(stack.Pop().Equals(1)); + Assert.ThrowsException(() => stack.Pop().Equals(0)); + } + + [TestMethod] + public void TestEvaluationStackPrint() + { + var stack = new EvaluationStack(new ReferenceCounter()); + + stack.Insert(0, 3); + stack.Insert(1, 1); + stack.Insert(2, "test"); + stack.Insert(3, true); + + Assert.AreEqual("[Boolean(True), ByteString(\"test\"), Integer(1), Integer(3)]", stack.ToString()); + } + + [TestMethod] + public void TestPrintInvalidUTF8() + { + var stack = new EvaluationStack(new ReferenceCounter()); + stack.Insert(0, "4CC95219999D421243C8161E3FC0F4290C067845".FromHexString()); + Assert.AreEqual("[ByteString(\"Base64: TMlSGZmdQhJDyBYeP8D0KQwGeEU=\")]", stack.ToString()); + } + } +} diff --git a/tests/Neo.VM.Tests/UT_ExecutionContext.cs b/tests/Neo.VM.Tests/UT_ExecutionContext.cs new file mode 100644 index 0000000000..b53003a7e6 --- /dev/null +++ b/tests/Neo.VM.Tests/UT_ExecutionContext.cs @@ -0,0 +1,66 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ExecutionContext.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.VisualStudio.TestTools.UnitTesting; +using Neo.VM; +using System; +using System.Collections.Generic; + +namespace Neo.Test +{ + [TestClass] + public class UT_ExecutionContext + { + class TestState + { + public bool Flag = false; + } + + [TestMethod] + public void TestStateTest() + { + var context = new ExecutionContext(Array.Empty(), -1, new ReferenceCounter()); + + // Test factory + + var flag = context.GetState(() => new TestState() { Flag = true }); + Assert.IsTrue(flag.Flag); + + flag.Flag = false; + + flag = context.GetState(() => new TestState() { Flag = true }); + Assert.IsFalse(flag.Flag); + + // Test new + + var stack = context.GetState>(); + Assert.AreEqual(0, stack.Count); + stack.Push(100); + stack = context.GetState>(); + Assert.AreEqual(100, stack.Pop()); + stack.Push(100); + + // Test clone + + var copy = context.Clone(); + var copyStack = copy.GetState>(); + Assert.AreEqual(1, copyStack.Count); + copyStack.Push(200); + copyStack = context.GetState>(); + Assert.AreEqual(200, copyStack.Pop()); + Assert.AreEqual(100, copyStack.Pop()); + copyStack.Push(200); + + stack = context.GetState>(); + Assert.AreEqual(200, stack.Pop()); + } + } +} diff --git a/tests/Neo.VM.Tests/UT_ReferenceCounter.cs b/tests/Neo.VM.Tests/UT_ReferenceCounter.cs new file mode 100644 index 0000000000..f974670805 --- /dev/null +++ b/tests/Neo.VM.Tests/UT_ReferenceCounter.cs @@ -0,0 +1,244 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ReferenceCounter.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.VisualStudio.TestTools.UnitTesting; +using Neo.VM; +using Neo.VM.Types; + +namespace Neo.Test +{ + [TestClass] + public class UT_ReferenceCounter + { + [TestMethod] + public void TestCircularReferences() + { + using ScriptBuilder sb = new(); + sb.Emit(OpCode.INITSSLOT, new byte[] { 1 }); //{}|{null}:1 + sb.EmitPush(0); //{0}|{null}:2 + sb.Emit(OpCode.NEWARRAY); //{A[]}|{null}:2 + sb.Emit(OpCode.DUP); //{A[],A[]}|{null}:3 + sb.Emit(OpCode.DUP); //{A[],A[],A[]}|{null}:4 + sb.Emit(OpCode.APPEND); //{A[A]}|{null}:3 + sb.Emit(OpCode.DUP); //{A[A],A[A]}|{null}:4 + sb.EmitPush(0); //{A[A],A[A],0}|{null}:5 + sb.Emit(OpCode.NEWARRAY); //{A[A],A[A],B[]}|{null}:5 + sb.Emit(OpCode.STSFLD0); //{A[A],A[A]}|{B[]}:4 + sb.Emit(OpCode.LDSFLD0); //{A[A],A[A],B[]}|{B[]}:5 + sb.Emit(OpCode.APPEND); //{A[A,B]}|{B[]}:4 + sb.Emit(OpCode.LDSFLD0); //{A[A,B],B[]}|{B[]}:5 + sb.EmitPush(0); //{A[A,B],B[],0}|{B[]}:6 + sb.Emit(OpCode.NEWARRAY); //{A[A,B],B[],C[]}|{B[]}:6 + sb.Emit(OpCode.TUCK); //{A[A,B],C[],B[],C[]}|{B[]}:7 + sb.Emit(OpCode.APPEND); //{A[A,B],C[]}|{B[C]}:6 + sb.EmitPush(0); //{A[A,B],C[],0}|{B[C]}:7 + sb.Emit(OpCode.NEWARRAY); //{A[A,B],C[],D[]}|{B[C]}:7 + sb.Emit(OpCode.TUCK); //{A[A,B],D[],C[],D[]}|{B[C]}:8 + sb.Emit(OpCode.APPEND); //{A[A,B],D[]}|{B[C[D]]}:7 + sb.Emit(OpCode.LDSFLD0); //{A[A,B],D[],B[C]}|{B[C[D]]}:8 + sb.Emit(OpCode.APPEND); //{A[A,B]}|{B[C[D[B]]]}:7 + sb.Emit(OpCode.PUSHNULL); //{A[A,B],null}|{B[C[D[B]]]}:8 + sb.Emit(OpCode.STSFLD0); //{A[A,B[C[D[B]]]]}|{null}:7 + sb.Emit(OpCode.DUP); //{A[A,B[C[D[B]]]],A[A,B]}|{null}:8 + sb.EmitPush(1); //{A[A,B[C[D[B]]]],A[A,B],1}|{null}:9 + sb.Emit(OpCode.REMOVE); //{A[A]}|{null}:3 + sb.Emit(OpCode.STSFLD0); //{}|{A[A]}:2 + sb.Emit(OpCode.RET); //{}:0 + + using ExecutionEngine engine = new(); + Debugger debugger = new(engine); + engine.LoadScript(sb.ToArray()); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(1, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(2, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(2, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(3, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(4, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(3, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(4, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(5, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(5, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(4, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(5, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(4, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(5, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(6, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(6, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(7, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(6, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(7, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(7, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(8, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(7, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(8, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(7, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(8, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(7, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(8, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(9, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(6, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(5, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.HALT, debugger.Execute()); + Assert.AreEqual(4, engine.ReferenceCounter.Count); + } + + [TestMethod] + public void TestRemoveReferrer() + { + using ScriptBuilder sb = new(); + sb.Emit(OpCode.INITSSLOT, new byte[] { 1 }); //{}|{null}:1 + sb.EmitPush(0); //{0}|{null}:2 + sb.Emit(OpCode.NEWARRAY); //{A[]}|{null}:2 + sb.Emit(OpCode.DUP); //{A[],A[]}|{null}:3 + sb.EmitPush(0); //{A[],A[],0}|{null}:4 + sb.Emit(OpCode.NEWARRAY); //{A[],A[],B[]}|{null}:4 + sb.Emit(OpCode.STSFLD0); //{A[],A[]}|{B[]}:3 + sb.Emit(OpCode.LDSFLD0); //{A[],A[],B[]}|{B[]}:4 + sb.Emit(OpCode.APPEND); //{A[B]}|{B[]}:3 + sb.Emit(OpCode.DROP); //{}|{B[]}:1 + sb.Emit(OpCode.RET); //{}:0 + + using ExecutionEngine engine = new(); + Debugger debugger = new(engine); + engine.LoadScript(sb.ToArray()); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(1, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(2, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(2, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(3, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(4, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(4, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(3, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(4, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(3, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.BREAK, debugger.StepInto()); + Assert.AreEqual(2, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.HALT, debugger.Execute()); + Assert.AreEqual(1, engine.ReferenceCounter.Count); + } + + [TestMethod] + public void TestCheckZeroReferredWithArray() + { + using ScriptBuilder sb = new(); + + sb.EmitPush(ExecutionEngineLimits.Default.MaxStackSize - 1); + sb.Emit(OpCode.NEWARRAY); + + // Good with MaxStackSize + + using (ExecutionEngine engine = new()) + { + engine.LoadScript(sb.ToArray()); + Assert.AreEqual(0, engine.ReferenceCounter.Count); + + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.AreEqual((int)ExecutionEngineLimits.Default.MaxStackSize, engine.ReferenceCounter.Count); + } + + // Fault with MaxStackSize+1 + + sb.Emit(OpCode.PUSH1); + + using (ExecutionEngine engine = new()) + { + engine.LoadScript(sb.ToArray()); + Assert.AreEqual(0, engine.ReferenceCounter.Count); + + Assert.AreEqual(VMState.FAULT, engine.Execute()); + Assert.AreEqual((int)ExecutionEngineLimits.Default.MaxStackSize + 1, engine.ReferenceCounter.Count); + } + } + + [TestMethod] + public void TestCheckZeroReferred() + { + using ScriptBuilder sb = new(); + + for (int x = 0; x < ExecutionEngineLimits.Default.MaxStackSize; x++) + sb.Emit(OpCode.PUSH1); + + // Good with MaxStackSize + + using (ExecutionEngine engine = new()) + { + engine.LoadScript(sb.ToArray()); + Assert.AreEqual(0, engine.ReferenceCounter.Count); + + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.AreEqual((int)ExecutionEngineLimits.Default.MaxStackSize, engine.ReferenceCounter.Count); + } + + // Fault with MaxStackSize+1 + + sb.Emit(OpCode.PUSH1); + + using (ExecutionEngine engine = new()) + { + engine.LoadScript(sb.ToArray()); + Assert.AreEqual(0, engine.ReferenceCounter.Count); + + Assert.AreEqual(VMState.FAULT, engine.Execute()); + Assert.AreEqual((int)ExecutionEngineLimits.Default.MaxStackSize + 1, engine.ReferenceCounter.Count); + } + } + + [TestMethod] + public void TestArrayNoPush() + { + using ScriptBuilder sb = new(); + sb.Emit(OpCode.RET); + using ExecutionEngine engine = new(); + engine.LoadScript(sb.ToArray()); + Assert.AreEqual(0, engine.ReferenceCounter.Count); + Array array = new(engine.ReferenceCounter, new StackItem[] { 1, 2, 3, 4 }); + Assert.AreEqual(array.Count, engine.ReferenceCounter.Count); + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.AreEqual(array.Count, engine.ReferenceCounter.Count); + } + } +} diff --git a/tests/Neo.VM.Tests/UT_Script.cs b/tests/Neo.VM.Tests/UT_Script.cs new file mode 100644 index 0000000000..c703d2685f --- /dev/null +++ b/tests/Neo.VM.Tests/UT_Script.cs @@ -0,0 +1,104 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Script.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.VisualStudio.TestTools.UnitTesting; +using Neo.VM; +using System; +using System.Text; + +namespace Neo.Test +{ + [TestClass] + public class UT_Script + { + [TestMethod] + public void TestConversion() + { + byte[] rawScript; + using (var builder = new ScriptBuilder()) + { + builder.Emit(OpCode.PUSH0); + builder.Emit(OpCode.CALL, new byte[] { 0x00, 0x01 }); + builder.EmitSysCall(123); + + rawScript = builder.ToArray(); + } + + var script = new Script(rawScript); + + ReadOnlyMemory scriptConversion = script; + Assert.AreEqual(rawScript, scriptConversion); + } + + [TestMethod] + public void TestStrictMode() + { + var rawScript = new byte[] { (byte)OpCode.PUSH0, 0xFF }; + Assert.ThrowsException(() => new Script(rawScript, true)); + + var script = new Script(rawScript, false); + Assert.AreEqual(2, script.Length); + + rawScript = new byte[] { (byte)OpCode.PUSHDATA1 }; + Assert.ThrowsException(() => new Script(rawScript, true)); + + rawScript = new byte[] { (byte)OpCode.PUSHDATA2 }; + Assert.ThrowsException(() => new Script(rawScript, true)); + + rawScript = new byte[] { (byte)OpCode.PUSHDATA4 }; + Assert.ThrowsException(() => new Script(rawScript, true)); + } + + [TestMethod] + public void TestParse() + { + Script script; + + using (var builder = new ScriptBuilder()) + { + builder.Emit(OpCode.PUSH0); + builder.Emit(OpCode.CALL_L, new byte[] { 0x00, 0x01, 0x00, 0x00 }); + builder.EmitSysCall(123); + + script = new Script(builder.ToArray()); + } + + Assert.AreEqual(11, script.Length); + + var ins = script.GetInstruction(0); + + Assert.AreEqual(OpCode.PUSH0, ins.OpCode); + Assert.IsTrue(ins.Operand.IsEmpty); + Assert.AreEqual(1, ins.Size); + Assert.ThrowsException(() => { var x = ins.TokenI16; }); + Assert.ThrowsException(() => { var x = ins.TokenU32; }); + + ins = script.GetInstruction(1); + + Assert.AreEqual(OpCode.CALL_L, ins.OpCode); + CollectionAssert.AreEqual(new byte[] { 0x00, 0x01, 0x00, 0x00 }, ins.Operand.ToArray()); + Assert.AreEqual(5, ins.Size); + Assert.AreEqual(256, ins.TokenI32); + Assert.AreEqual(Encoding.ASCII.GetString(new byte[] { 0x00, 0x01, 0x00, 0x00 }), ins.TokenString); + + ins = script.GetInstruction(6); + + Assert.AreEqual(OpCode.SYSCALL, ins.OpCode); + CollectionAssert.AreEqual(new byte[] { 123, 0x00, 0x00, 0x00 }, ins.Operand.ToArray()); + Assert.AreEqual(5, ins.Size); + Assert.AreEqual(123, ins.TokenI16); + Assert.AreEqual(Encoding.ASCII.GetString(new byte[] { 123, 0x00, 0x00, 0x00 }), ins.TokenString); + Assert.AreEqual(123U, ins.TokenU32); + + Assert.ThrowsException(() => script.GetInstruction(100)); + } + } +} diff --git a/tests/Neo.VM.Tests/UT_ScriptBuilder.cs b/tests/Neo.VM.Tests/UT_ScriptBuilder.cs new file mode 100644 index 0000000000..faa57f3345 --- /dev/null +++ b/tests/Neo.VM.Tests/UT_ScriptBuilder.cs @@ -0,0 +1,275 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_ScriptBuilder.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.VisualStudio.TestTools.UnitTesting; +using Neo.Test.Extensions; +using Neo.Test.Helpers; +using Neo.VM; +using System; +using System.Linq; +using System.Numerics; +using System.Text; + +namespace Neo.Test +{ + [TestClass] + public class UT_ScriptBuilder + { + [TestMethod] + public void TestEmit() + { + using (ScriptBuilder script = new()) + { + Assert.AreEqual(0, script.Length); + script.Emit(OpCode.NOP); + Assert.AreEqual(1, script.Length); + + CollectionAssert.AreEqual(new byte[] { 0x21 }, script.ToArray()); + } + + using (ScriptBuilder script = new()) + { + script.Emit(OpCode.NOP, new byte[] { 0x66 }); + CollectionAssert.AreEqual(new byte[] { 0x21, 0x66 }, script.ToArray()); + } + } + + [TestMethod] + public void TestBigInteger() + { + using (ScriptBuilder script = new()) + { + Assert.AreEqual(0, script.Length); + script.EmitPush(-100000); + Assert.AreEqual(5, script.Length); + + CollectionAssert.AreEqual(new byte[] { 2, 96, 121, 254, 255 }, script.ToArray()); + } + + using (ScriptBuilder script = new()) + { + Assert.AreEqual(0, script.Length); + script.EmitPush(100000); + Assert.AreEqual(5, script.Length); + + CollectionAssert.AreEqual(new byte[] { 2, 160, 134, 1, 0 }, script.ToArray()); + } + } + + [TestMethod] + public void TestEmitSysCall() + { + using ScriptBuilder script = new(); + script.EmitSysCall(0xE393C875); + CollectionAssert.AreEqual(new byte[] { (byte)OpCode.SYSCALL, 0x75, 0xC8, 0x93, 0xE3 }.ToArray(), script.ToArray()); + } + + [TestMethod] + public void TestEmitCall() + { + using (ScriptBuilder script = new()) + { + script.EmitCall(0); + CollectionAssert.AreEqual(new[] { (byte)OpCode.CALL, (byte)0 }, script.ToArray()); + } + using (ScriptBuilder script = new()) + { + script.EmitCall(12345); + CollectionAssert.AreEqual(new[] { (byte)OpCode.CALL_L }.Concat(BitConverter.GetBytes(12345)).ToArray(), script.ToArray()); + } + using (ScriptBuilder script = new()) + { + script.EmitCall(-12345); + CollectionAssert.AreEqual(new[] { (byte)OpCode.CALL_L }.Concat(BitConverter.GetBytes(-12345)).ToArray(), script.ToArray()); + } + } + + [TestMethod] + public void TestEmitJump() + { + var offset_i8 = sbyte.MaxValue; + var offset_i32 = int.MaxValue; + + foreach (OpCode op in Enum.GetValues(typeof(OpCode))) + { + using ScriptBuilder script = new(); + if (op < OpCode.JMP || op > OpCode.JMPLE_L) + { + Assert.ThrowsException(() => script.EmitJump(op, offset_i8)); + Assert.ThrowsException(() => script.EmitJump(op, offset_i32)); + } + else + { + script.EmitJump(op, offset_i8); + script.EmitJump(op, offset_i32); + if ((int)op % 2 == 0) + CollectionAssert.AreEqual(new[] { (byte)op, (byte)offset_i8, (byte)(op + 1) }.Concat(BitConverter.GetBytes(offset_i32)).ToArray(), script.ToArray()); + else + CollectionAssert.AreEqual(new[] { (byte)op }.Concat(BitConverter.GetBytes((int)offset_i8)).Concat(new[] { (byte)op }).Concat(BitConverter.GetBytes(offset_i32)).ToArray(), script.ToArray()); + } + } + + offset_i8 = sbyte.MinValue; + offset_i32 = int.MinValue; + + foreach (OpCode op in Enum.GetValues(typeof(OpCode))) + { + using ScriptBuilder script = new(); + if (op < OpCode.JMP || op > OpCode.JMPLE_L) + { + Assert.ThrowsException(() => script.EmitJump(op, offset_i8)); + Assert.ThrowsException(() => script.EmitJump(op, offset_i32)); + } + else + { + script.EmitJump(op, offset_i8); + script.EmitJump(op, offset_i32); + if ((int)op % 2 == 0) + CollectionAssert.AreEqual(new[] { (byte)op, (byte)offset_i8, (byte)(op + 1) }.Concat(BitConverter.GetBytes(offset_i32)).ToArray(), script.ToArray()); + else + CollectionAssert.AreEqual(new[] { (byte)op }.Concat(BitConverter.GetBytes((int)offset_i8)).Concat(new[] { (byte)op }).Concat(BitConverter.GetBytes(offset_i32)).ToArray(), script.ToArray()); + } + } + } + + [TestMethod] + public void TestEmitPushBigInteger() + { + using (ScriptBuilder script = new()) + { + script.EmitPush(BigInteger.MinusOne); + CollectionAssert.AreEqual(new byte[] { 0x0F }, script.ToArray()); + } + + using (ScriptBuilder script = new()) + { + script.EmitPush(BigInteger.Zero); + CollectionAssert.AreEqual(new byte[] { 0x10 }, script.ToArray()); + } + + for (byte x = 1; x <= 16; x++) + { + using ScriptBuilder script = new(); + script.EmitPush(new BigInteger(x)); + CollectionAssert.AreEqual(new byte[] { (byte)(OpCode.PUSH0 + x) }, script.ToArray()); + } + + CollectionAssert.AreEqual("0080".FromHexString(), new ScriptBuilder().EmitPush(sbyte.MinValue).ToArray()); + CollectionAssert.AreEqual("007f".FromHexString(), new ScriptBuilder().EmitPush(sbyte.MaxValue).ToArray()); + CollectionAssert.AreEqual("01ff00".FromHexString(), new ScriptBuilder().EmitPush(byte.MaxValue).ToArray()); + CollectionAssert.AreEqual("010080".FromHexString(), new ScriptBuilder().EmitPush(short.MinValue).ToArray()); + CollectionAssert.AreEqual("01ff7f".FromHexString(), new ScriptBuilder().EmitPush(short.MaxValue).ToArray()); + CollectionAssert.AreEqual("02ffff0000".FromHexString(), new ScriptBuilder().EmitPush(ushort.MaxValue).ToArray()); + CollectionAssert.AreEqual("0200000080".FromHexString(), new ScriptBuilder().EmitPush(int.MinValue).ToArray()); + CollectionAssert.AreEqual("02ffffff7f".FromHexString(), new ScriptBuilder().EmitPush(int.MaxValue).ToArray()); + CollectionAssert.AreEqual("03ffffffff00000000".FromHexString(), new ScriptBuilder().EmitPush(uint.MaxValue).ToArray()); + CollectionAssert.AreEqual("030000000000000080".FromHexString(), new ScriptBuilder().EmitPush(long.MinValue).ToArray()); + CollectionAssert.AreEqual("03ffffffffffffff7f".FromHexString(), new ScriptBuilder().EmitPush(long.MaxValue).ToArray()); + CollectionAssert.AreEqual("04ffffffffffffffff0000000000000000".FromHexString(), new ScriptBuilder().EmitPush(ulong.MaxValue).ToArray()); + CollectionAssert.AreEqual("050100000000000000feffffffffffffff00000000000000000000000000000000".FromHexString(), new ScriptBuilder().EmitPush(new BigInteger(ulong.MaxValue) * new BigInteger(ulong.MaxValue)).ToArray()); + + Assert.ThrowsException(() => new ScriptBuilder().EmitPush( + new BigInteger("050100000000000000feffffffffffffff0100000000000000feffffffffffffff00000000000000000000000000000000".FromHexString()))); + } + + [TestMethod] + public void TestEmitPushBool() + { + using (ScriptBuilder script = new()) + { + script.EmitPush(true); + CollectionAssert.AreEqual(new byte[] { (byte)OpCode.PUSHT }, script.ToArray()); + } + + using (ScriptBuilder script = new()) + { + script.EmitPush(false); + CollectionAssert.AreEqual(new byte[] { (byte)OpCode.PUSHF }, script.ToArray()); + } + } + + [TestMethod] + public void TestEmitPushReadOnlySpan() + { + using ScriptBuilder script = new(); + var data = new byte[] { 0x01, 0x02 }; + script.EmitPush(new ReadOnlySpan(data)); + + CollectionAssert.AreEqual(new byte[] { (byte)OpCode.PUSHDATA1, (byte)data.Length }.Concat(data).ToArray(), script.ToArray()); + } + + [TestMethod] + public void TestEmitPushByteArray() + { + using (ScriptBuilder script = new()) + { + Assert.ThrowsException(() => script.EmitPush((byte[])null)); + } + + using (ScriptBuilder script = new()) + { + var data = RandomHelper.RandBuffer(0x4C); + + script.EmitPush(data); + CollectionAssert.AreEqual(new byte[] { (byte)OpCode.PUSHDATA1, (byte)data.Length }.Concat(data).ToArray(), script.ToArray()); + } + + using (ScriptBuilder script = new()) + { + var data = RandomHelper.RandBuffer(0x100); + + script.EmitPush(data); + CollectionAssert.AreEqual(new byte[] { (byte)OpCode.PUSHDATA2 }.Concat(BitConverter.GetBytes((short)data.Length)).Concat(data).ToArray(), script.ToArray()); + } + + using (ScriptBuilder script = new()) + { + var data = RandomHelper.RandBuffer(0x10000); + + script.EmitPush(data); + CollectionAssert.AreEqual(new byte[] { (byte)OpCode.PUSHDATA4 }.Concat(BitConverter.GetBytes(data.Length)).Concat(data).ToArray(), script.ToArray()); + } + } + + [TestMethod] + public void TestEmitPushString() + { + using (ScriptBuilder script = new()) + { + Assert.ThrowsException(() => script.EmitPush((string)null)); + } + + using (ScriptBuilder script = new()) + { + var data = RandomHelper.RandString(0x4C); + + script.EmitPush(data); + CollectionAssert.AreEqual(new byte[] { (byte)OpCode.PUSHDATA1, (byte)data.Length }.Concat(Encoding.UTF8.GetBytes(data)).ToArray(), script.ToArray()); + } + + using (ScriptBuilder script = new()) + { + var data = RandomHelper.RandString(0x100); + + script.EmitPush(data); + CollectionAssert.AreEqual(new byte[] { (byte)OpCode.PUSHDATA2 }.Concat(BitConverter.GetBytes((short)data.Length)).Concat(Encoding.UTF8.GetBytes(data)).ToArray(), script.ToArray()); + } + + using (ScriptBuilder script = new()) + { + var data = RandomHelper.RandString(0x10000); + + script.EmitPush(data); + CollectionAssert.AreEqual(new byte[] { (byte)OpCode.PUSHDATA4 }.Concat(BitConverter.GetBytes(data.Length)).Concat(Encoding.UTF8.GetBytes(data)).ToArray(), script.ToArray()); + } + } + } +} diff --git a/tests/Neo.VM.Tests/UT_Slot.cs b/tests/Neo.VM.Tests/UT_Slot.cs new file mode 100644 index 0000000000..ea2f38029b --- /dev/null +++ b/tests/Neo.VM.Tests/UT_Slot.cs @@ -0,0 +1,101 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Slot.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.VisualStudio.TestTools.UnitTesting; +using Neo.VM; +using Neo.VM.Types; +using System; +using System.Collections; +using System.Linq; +using System.Numerics; + +namespace Neo.Test +{ + [TestClass] + public class UT_Slot + { + private static Slot CreateOrderedSlot(int count) + { + var check = new Integer[count]; + + for (int x = 1; x <= count; x++) + { + check[x - 1] = x; + } + + var slot = new Slot(check, new ReferenceCounter()); + + Assert.AreEqual(count, slot.Count); + CollectionAssert.AreEqual(check, slot.ToArray()); + + return slot; + } + + public static IEnumerable GetEnumerable(IEnumerator enumerator) + { + while (enumerator.MoveNext()) yield return enumerator.Current; + } + + [TestMethod] + public void TestGet() + { + var slot = CreateOrderedSlot(3); + + Assert.IsTrue(slot[0] is Integer item0 && item0.Equals(1)); + Assert.IsTrue(slot[1] is Integer item1 && item1.Equals(2)); + Assert.IsTrue(slot[2] is Integer item2 && item2.Equals(3)); + Assert.ThrowsException(() => slot[3] is Integer item3); + } + + [TestMethod] + public void TestEnumerable() + { + var slot = CreateOrderedSlot(3); + + BigInteger i = 1; + foreach (Integer item in slot) + { + Assert.AreEqual(item.GetInteger(), i); + i++; + } + + CollectionAssert.AreEqual(new Integer[] { 1, 2, 3 }, slot.ToArray()); + + // Test IEnumerable + + var enumerable = (IEnumerable)slot; + var enumerator = enumerable.GetEnumerator(); + + CollectionAssert.AreEqual(new Integer[] { 1, 2, 3 }, GetEnumerable(enumerator).Cast().ToArray()); + + Assert.AreEqual(3, slot.Count); + + CollectionAssert.AreEqual(new Integer[] { 1, 2, 3 }, slot.ToArray()); + + // Empty + + slot = CreateOrderedSlot(0); + + CollectionAssert.AreEqual(System.Array.Empty(), slot.ToArray()); + + // Test IEnumerable + + enumerable = slot; + enumerator = enumerable.GetEnumerator(); + + CollectionAssert.AreEqual(System.Array.Empty(), GetEnumerable(enumerator).Cast().ToArray()); + + Assert.AreEqual(0, slot.Count); + + CollectionAssert.AreEqual(System.Array.Empty(), slot.ToArray()); + } + } +} diff --git a/tests/Neo.VM.Tests/UT_StackItem.cs b/tests/Neo.VM.Tests/UT_StackItem.cs new file mode 100644 index 0000000000..210fa0d433 --- /dev/null +++ b/tests/Neo.VM.Tests/UT_StackItem.cs @@ -0,0 +1,210 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_StackItem.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.VisualStudio.TestTools.UnitTesting; +using Neo.VM; +using Neo.VM.Types; +using System.Numerics; + +namespace Neo.Test +{ + [TestClass] + public class UT_StackItem + { + [TestMethod] + public void TestHashCode() + { + StackItem itemA = "NEO"; + StackItem itemB = "NEO"; + StackItem itemC = "SmartEconomy"; + + Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode()); + + itemA = new VM.Types.Buffer(1); + itemB = new VM.Types.Buffer(1); + + Assert.IsTrue(itemA.GetHashCode() != itemB.GetHashCode()); + + itemA = true; + itemB = true; + itemC = false; + + Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode()); + + itemA = 1; + itemB = 1; + itemC = 123; + + Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode()); + + itemA = new Null(); + itemB = new Null(); + + Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); + + itemA = new VM.Types.Array(); + + Assert.ThrowsException(() => itemA.GetHashCode()); + + itemA = new Struct(); + + Assert.ThrowsException(() => itemA.GetHashCode()); + + itemA = new Map(); + + Assert.ThrowsException(() => itemA.GetHashCode()); + + itemA = new InteropInterface(123); + itemB = new InteropInterface(123); + + Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); + + var script = new Script(System.Array.Empty()); + itemA = new Pointer(script, 123); + itemB = new Pointer(script, 123); + itemC = new Pointer(script, 1234); + + Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode()); + } + + [TestMethod] + public void TestNull() + { + StackItem nullItem = System.Array.Empty(); + Assert.AreNotEqual(StackItem.Null, nullItem); + + nullItem = new Null(); + Assert.AreEqual(StackItem.Null, nullItem); + } + + [TestMethod] + public void TestEqual() + { + StackItem itemA = "NEO"; + StackItem itemB = "NEO"; + StackItem itemC = "SmartEconomy"; + StackItem itemD = "Smarteconomy"; + StackItem itemE = "smarteconomy"; + + Assert.IsTrue(itemA.Equals(itemB)); + Assert.IsFalse(itemA.Equals(itemC)); + Assert.IsFalse(itemC.Equals(itemD)); + Assert.IsFalse(itemD.Equals(itemE)); + Assert.IsFalse(itemA.Equals(new object())); + } + + [TestMethod] + public void TestCast() + { + // Signed byte + + StackItem item = sbyte.MaxValue; + + Assert.IsInstanceOfType(item, typeof(Integer)); + Assert.AreEqual(new BigInteger(sbyte.MaxValue), ((Integer)item).GetInteger()); + + // Unsigned byte + + item = byte.MaxValue; + + Assert.IsInstanceOfType(item, typeof(Integer)); + Assert.AreEqual(new BigInteger(byte.MaxValue), ((Integer)item).GetInteger()); + + // Signed short + + item = short.MaxValue; + + Assert.IsInstanceOfType(item, typeof(Integer)); + Assert.AreEqual(new BigInteger(short.MaxValue), ((Integer)item).GetInteger()); + + // Unsigned short + + item = ushort.MaxValue; + + Assert.IsInstanceOfType(item, typeof(Integer)); + Assert.AreEqual(new BigInteger(ushort.MaxValue), ((Integer)item).GetInteger()); + + // Signed integer + + item = int.MaxValue; + + Assert.IsInstanceOfType(item, typeof(Integer)); + Assert.AreEqual(new BigInteger(int.MaxValue), ((Integer)item).GetInteger()); + + // Unsigned integer + + item = uint.MaxValue; + + Assert.IsInstanceOfType(item, typeof(Integer)); + Assert.AreEqual(new BigInteger(uint.MaxValue), ((Integer)item).GetInteger()); + + // Signed long + + item = long.MaxValue; + + Assert.IsInstanceOfType(item, typeof(Integer)); + Assert.AreEqual(new BigInteger(long.MaxValue), ((Integer)item).GetInteger()); + + // Unsigned long + + item = ulong.MaxValue; + + Assert.IsInstanceOfType(item, typeof(Integer)); + Assert.AreEqual(new BigInteger(ulong.MaxValue), ((Integer)item).GetInteger()); + + // BigInteger + + item = BigInteger.MinusOne; + + Assert.IsInstanceOfType(item, typeof(Integer)); + Assert.AreEqual(new BigInteger(-1), ((Integer)item).GetInteger()); + + // Boolean + + item = true; + + Assert.IsInstanceOfType(item, typeof(VM.Types.Boolean)); + Assert.IsTrue(item.GetBoolean()); + + // ByteString + + item = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 }; + + Assert.IsInstanceOfType(item, typeof(ByteString)); + CollectionAssert.AreEqual(new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 }, item.GetSpan().ToArray()); + } + + [TestMethod] + public void TestDeepCopy() + { + Array a = new() + { + true, + 1, + new byte[] { 1 }, + StackItem.Null, + new Buffer(new byte[] { 1 }), + new Map { [0] = 1, [2] = 3 }, + new Struct { 1, 2, 3 } + }; + a.Add(a); + Array aa = (Array)a.DeepCopy(); + Assert.AreNotEqual(a, aa); + Assert.AreSame(aa, aa[^1]); + Assert.IsTrue(a[^2].Equals(aa[^2], ExecutionEngineLimits.Default)); + Assert.AreNotSame(a[^2], aa[^2]); + } + } +} diff --git a/tests/Neo.VM.Tests/UT_Struct.cs b/tests/Neo.VM.Tests/UT_Struct.cs new file mode 100644 index 0000000000..2e62650fc1 --- /dev/null +++ b/tests/Neo.VM.Tests/UT_Struct.cs @@ -0,0 +1,75 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Struct.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.VisualStudio.TestTools.UnitTesting; +using Neo.VM; +using Neo.VM.Types; +using System; + +namespace Neo.Test +{ + [TestClass] + public class UT_Struct + { + private readonly Struct @struct; + + public UT_Struct() + { + @struct = new Struct { 1 }; + for (int i = 0; i < 20000; i++) + @struct = new Struct { @struct }; + } + + [TestMethod] + public void TestClone() + { + Struct s1 = new() { 1, new Struct { 2 } }; + Struct s2 = s1.Clone(ExecutionEngineLimits.Default); + s1[0] = 3; + Assert.AreEqual(1, s2[0]); + ((Struct)s1[1])[0] = 3; + Assert.AreEqual(2, ((Struct)s2[1])[0]); + Assert.ThrowsException(() => @struct.Clone(ExecutionEngineLimits.Default)); + } + + [TestMethod] + public void TestEquals() + { + Struct s1 = new() { 1, new Struct { 2 } }; + Struct s2 = new() { 1, new Struct { 2 } }; + Assert.IsTrue(s1.Equals(s2, ExecutionEngineLimits.Default)); + Struct s3 = new() { 1, new Struct { 3 } }; + Assert.IsFalse(s1.Equals(s3, ExecutionEngineLimits.Default)); + Assert.ThrowsException(() => @struct.Equals(@struct.Clone(ExecutionEngineLimits.Default), ExecutionEngineLimits.Default)); + } + + [TestMethod] + public void TestEqualsDos() + { + string payloadStr = new string('h', 65535); + Struct s1 = new(); + Struct s2 = new(); + for (int i = 0; i < 2; i++) + { + s1.Add(payloadStr); + s2.Add(payloadStr); + } + Assert.ThrowsException(() => s1.Equals(s2, ExecutionEngineLimits.Default)); + + for (int i = 0; i < 1000; i++) + { + s1.Add(payloadStr); + s2.Add(payloadStr); + } + Assert.ThrowsException(() => s1.Equals(s2, ExecutionEngineLimits.Default)); + } + } +} diff --git a/tests/Neo.VM.Tests/UT_Unsafe.cs b/tests/Neo.VM.Tests/UT_Unsafe.cs new file mode 100644 index 0000000000..e3b4b8708f --- /dev/null +++ b/tests/Neo.VM.Tests/UT_Unsafe.cs @@ -0,0 +1,33 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Unsafe.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.VisualStudio.TestTools.UnitTesting; +using Neo.VM; + +namespace Neo.Test +{ + [TestClass] + public class UT_Unsafe + { + [TestMethod] + public void TestNotZero() + { + Assert.IsFalse(Unsafe.NotZero(System.Array.Empty())); + Assert.IsFalse(Unsafe.NotZero(new byte[4])); + Assert.IsFalse(Unsafe.NotZero(new byte[8])); + Assert.IsFalse(Unsafe.NotZero(new byte[11])); + + Assert.IsTrue(Unsafe.NotZero(new byte[4] { 0x00, 0x00, 0x00, 0x01 })); + Assert.IsTrue(Unsafe.NotZero(new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 })); + Assert.IsTrue(Unsafe.NotZero(new byte[11] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 })); + } + } +} diff --git a/tests/Neo.VM.Tests/UT_Utility.cs b/tests/Neo.VM.Tests/UT_Utility.cs new file mode 100644 index 0000000000..e87bcbcda7 --- /dev/null +++ b/tests/Neo.VM.Tests/UT_Utility.cs @@ -0,0 +1,105 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Utility.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.VisualStudio.TestTools.UnitTesting; +using Neo.VM; +using System; +using System.Numerics; + +namespace Neo.Test +{ + [TestClass] + public class UT_Utility + { + [TestMethod] + public void TestSqrtTest() + { + Assert.ThrowsException(() => BigInteger.MinusOne.Sqrt()); + + Assert.AreEqual(BigInteger.Zero, BigInteger.Zero.Sqrt()); + Assert.AreEqual(new BigInteger(1), new BigInteger(1).Sqrt()); + Assert.AreEqual(new BigInteger(1), new BigInteger(2).Sqrt()); + Assert.AreEqual(new BigInteger(1), new BigInteger(3).Sqrt()); + Assert.AreEqual(new BigInteger(2), new BigInteger(4).Sqrt()); + Assert.AreEqual(new BigInteger(9), new BigInteger(81).Sqrt()); + } + + private static byte[] GetRandomByteArray(Random random) + { + var byteValue = random.Next(0, 32); + var value = new byte[byteValue]; + + random.NextBytes(value); + return value; + } + + private void VerifyGetBitLength(BigInteger value, long expected) + { + var result = value.GetBitLength(); + Assert.AreEqual(expected, value.GetBitLength(), "Native method has not the expected result"); + Assert.AreEqual(result, Utility.GetBitLength(value), "Result doesn't match"); + } + + [TestMethod] + public void TestGetBitLength() + { + var random = new Random(); + + // Big Number (net standard didn't work) + VerifyGetBitLength(BigInteger.One << 32 << int.MaxValue, 2147483680); + + // Trivial cases + // sign bit|shortest two's complement + // string w/o sign bit + VerifyGetBitLength(0, 0); // 0| + VerifyGetBitLength(1, 1); // 0|1 + VerifyGetBitLength(-1, 0); // 1| + VerifyGetBitLength(2, 2); // 0|10 + VerifyGetBitLength(-2, 1); // 1|0 + VerifyGetBitLength(3, 2); // 0|11 + VerifyGetBitLength(-3, 2); // 1|01 + VerifyGetBitLength(4, 3); // 0|100 + VerifyGetBitLength(-4, 2); // 1|00 + VerifyGetBitLength(5, 3); // 0|101 + VerifyGetBitLength(-5, 3); // 1|011 + VerifyGetBitLength(6, 3); // 0|110 + VerifyGetBitLength(-6, 3); // 1|010 + VerifyGetBitLength(7, 3); // 0|111 + VerifyGetBitLength(-7, 3); // 1|001 + VerifyGetBitLength(8, 4); // 0|1000 + VerifyGetBitLength(-8, 3); // 1|000 + + // Random cases + for (uint i = 0; i < 1000; i++) + { + var bi = new BigInteger(GetRandomByteArray(random)); + Assert.AreEqual(bi.GetBitLength(), Utility.GetBitLength(bi), message: $"Error comparing: {bi}"); + } + + foreach (var bi in new[] { BigInteger.Zero, BigInteger.One, BigInteger.MinusOne, new BigInteger(ulong.MaxValue), new BigInteger(long.MinValue) }) + { + Assert.AreEqual(bi.GetBitLength(), Utility.GetBitLength(bi), message: $"Error comparing: {bi}"); + } + } + + [TestMethod] + public void TestModInverseTest() + { + Assert.ThrowsException(() => BigInteger.One.ModInverse(BigInteger.Zero)); + Assert.ThrowsException(() => BigInteger.One.ModInverse(BigInteger.One)); + Assert.ThrowsException(() => BigInteger.Zero.ModInverse(BigInteger.Zero)); + Assert.ThrowsException(() => BigInteger.Zero.ModInverse(BigInteger.One)); + Assert.ThrowsException(() => new BigInteger(ushort.MaxValue).ModInverse(byte.MaxValue)); + + Assert.AreEqual(new BigInteger(52), new BigInteger(19).ModInverse(141)); + } + } +} diff --git a/tests/Neo.VM.Tests/UT_VMJson.cs b/tests/Neo.VM.Tests/UT_VMJson.cs new file mode 100644 index 0000000000..d91c4b3cad --- /dev/null +++ b/tests/Neo.VM.Tests/UT_VMJson.cs @@ -0,0 +1,85 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_VMJson.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.VisualStudio.TestTools.UnitTesting; +using Neo.Test.Extensions; +using Neo.Test.Types; +using System; +using System.IO; +using System.Text; + +namespace Neo.Test +{ + [TestClass] + public class UT_VMJson : VMJsonTestBase + { + [TestMethod] + public void TestOthers() => TestJson("./Tests/Others"); + + [TestMethod] + public void TestOpCodesArrays() => TestJson("./Tests/OpCodes/Arrays"); + + [TestMethod] + public void TestOpCodesStack() => TestJson("./Tests/OpCodes/Stack"); + + [TestMethod] + public void TestOpCodesSlot() => TestJson("./Tests/OpCodes/Slot"); + + [TestMethod] + public void TestOpCodesSplice() => TestJson("./Tests/OpCodes/Splice"); + + [TestMethod] + public void TestOpCodesControl() => TestJson("./Tests/OpCodes/Control"); + + [TestMethod] + public void TestOpCodesPush() => TestJson("./Tests/OpCodes/Push"); + + [TestMethod] + public void TestOpCodesArithmetic() => TestJson("./Tests/OpCodes/Arithmetic"); + + [TestMethod] + public void TestOpCodesBitwiseLogic() => TestJson("./Tests/OpCodes/BitwiseLogic"); + + [TestMethod] + public void TestOpCodesTypes() => TestJson("./Tests/OpCodes/Types"); + + private void TestJson(string path) + { + foreach (var file in Directory.GetFiles(path, "*.json", SearchOption.AllDirectories)) + { + Console.WriteLine($"Processing file '{file}'"); + + var realFile = Path.GetFullPath(file); + var json = File.ReadAllText(realFile, Encoding.UTF8); + var ut = json.DeserializeJson(); + + Assert.IsFalse(string.IsNullOrEmpty(ut.Name), "Name is required"); + + if (json != ut.ToJson().Replace("\r\n", "\n")) + { + // Format json + + Console.WriteLine($"The file '{realFile}' was optimized"); + //File.WriteAllText(realFile, ut.ToJson().Replace("\r\n", "\n"), Encoding.UTF8); + } + + try + { + ExecuteTest(ut); + } + catch (Exception ex) + { + throw new AggregateException("Error in file: " + realFile, ex); + } + } + } + } +} diff --git a/tests/Neo.VM.Tests/VMJsonTestBase.cs b/tests/Neo.VM.Tests/VMJsonTestBase.cs new file mode 100644 index 0000000000..85882b7a8e --- /dev/null +++ b/tests/Neo.VM.Tests/VMJsonTestBase.cs @@ -0,0 +1,323 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// VMJsonTestBase.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.VisualStudio.TestTools.UnitTesting; +using Neo.Test.Extensions; +using Neo.Test.Types; +using Neo.VM; +using Neo.VM.Types; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Neo.Test +{ + public abstract class VMJsonTestBase + { + /// + /// Execute this test + /// + /// Test + public void ExecuteTest(VMUT ut) + { + foreach (var test in ut.Tests) + { + Assert.IsFalse(string.IsNullOrEmpty(test.Name), "Name is required"); + + using TestEngine engine = new(); + Debugger debugger = new(engine); + + if (test.Script.Length > 0) + { + engine.LoadScript(test.Script); + } + + // Execute Steps + + if (test.Steps != null) + { + foreach (var step in test.Steps) + { + // Actions + + if (step.Actions != null) foreach (var run in step.Actions) + { + switch (run) + { + case VMUTActionType.Execute: debugger.Execute(); break; + case VMUTActionType.StepInto: debugger.StepInto(); break; + case VMUTActionType.StepOut: debugger.StepOut(); break; + case VMUTActionType.StepOver: debugger.StepOver(); break; + } + } + + // Review results + + var add = string.IsNullOrEmpty(step.Name) ? "" : "-" + step.Name; + + AssertResult(step.Result, engine, $"{ut.Category}-{ut.Name}-{test.Name}{add}: "); + } + } + } + } + + /// + /// Assert result + /// + /// Engine + /// Result + /// Message + private void AssertResult(VMUTExecutionEngineState result, TestEngine engine, string message) + { + AssertAreEqual(result.State.ToString().ToLowerInvariant(), engine.State.ToString().ToLowerInvariant(), message + "State is different"); + if (engine.State == VMState.FAULT) + { + if (result.ExceptionMessage != null) + { + AssertAreEqual(result.ExceptionMessage, engine.FaultException.Message, message + " [Exception]"); + } + return; + } + AssertResult(result.InvocationStack, engine.InvocationStack, message + " [Invocation stack]"); + AssertResult(result.ResultStack, engine.ResultStack, message + " [Result stack] "); + } + + /// + /// Assert invocation stack + /// + /// Stack + /// Result + /// Message + private void AssertResult(VMUTExecutionContextState[] result, Stack stack, string message) + { + AssertAreEqual(result == null ? 0 : result.Length, stack.Count, message + "Stack is different"); + + int x = 0; + foreach (var context in stack) + { + var opcode = context.InstructionPointer >= context.Script.Length ? OpCode.RET : context.Script[context.InstructionPointer]; + + AssertAreEqual(result[x].NextInstruction, opcode, message + "Next instruction is different"); + AssertAreEqual(result[x].InstructionPointer, context.InstructionPointer, message + "Instruction pointer is different"); + + // Check stack + + AssertResult(result[x].EvaluationStack, context.EvaluationStack, message + " [EvaluationStack]"); + + // Check slots + + AssertResult(result[x].Arguments, context.Arguments, message + " [Arguments]"); + AssertResult(result[x].LocalVariables, context.LocalVariables, message + " [LocalVariables]"); + AssertResult(result[x].StaticFields, context.StaticFields, message + " [StaticFields]"); + + x++; + } + } + + /// + /// Assert result stack + /// + /// Stack + /// Result + /// Message + private void AssertResult(VMUTStackItem[] result, EvaluationStack stack, string message) + { + AssertAreEqual(stack.Count, result == null ? 0 : result.Length, message + "Stack is different"); + + for (int x = 0, max = stack.Count; x < max; x++) + { + AssertAreEqual(ItemToJson(stack.Peek(x)).ToString(Formatting.None), PrepareJsonItem(result[x]).ToString(Formatting.None), message + "Stack item is different"); + } + } + + /// + /// Assert result slot + /// + /// Slot + /// Result + /// Message + private void AssertResult(VMUTStackItem[] result, Slot slot, string message) + { + AssertAreEqual(slot == null ? 0 : slot.Count, result == null ? 0 : result.Length, message + "Slot is different"); + + for (int x = 0, max = slot == null ? 0 : slot.Count; x < max; x++) + { + AssertAreEqual(ItemToJson(slot[x]).ToString(Formatting.None), PrepareJsonItem(result[x]).ToString(Formatting.None), message + "Stack item is different"); + } + } + + private JObject PrepareJsonItem(VMUTStackItem item) + { + var ret = new JObject + { + ["type"] = item.Type.ToString(), + ["value"] = item.Value + }; + + switch (item.Type) + { + case VMUTStackItemType.Null: + { + ret["type"] = VMUTStackItemType.Null.ToString(); + ret.Remove("value"); + break; + } + case VMUTStackItemType.Pointer: + { + ret["type"] = VMUTStackItemType.Pointer.ToString(); + ret["value"] = item.Value.Value(); + break; + } + case VMUTStackItemType.String: + { + // Easy access + + ret["type"] = VMUTStackItemType.ByteString.ToString(); + ret["value"] = Encoding.UTF8.GetBytes(item.Value.Value()); + break; + } + case VMUTStackItemType.ByteString: + case VMUTStackItemType.Buffer: + { + var value = ret["value"].Value(); + Assert.IsTrue(string.IsNullOrEmpty(value) || value.StartsWith("0x"), $"'0x' prefix required for value: '{value}'"); + ret["value"] = value.FromHexString(); + break; + } + case VMUTStackItemType.Integer: + { + // Ensure format + + ret["value"] = ret["value"].Value(); + break; + } + case VMUTStackItemType.Struct: + case VMUTStackItemType.Array: + { + var array = (JArray)ret["value"]; + + for (int x = 0, m = array.Count; x < m; x++) + { + array[x] = PrepareJsonItem(JsonConvert.DeserializeObject(array[x].ToString())); + } + + ret["value"] = array; + break; + } + case VMUTStackItemType.Map: + { + var obj = (JObject)ret["value"]; + + foreach (var prop in obj.Properties()) + { + obj[prop.Name] = PrepareJsonItem(JsonConvert.DeserializeObject(prop.Value.ToString())); + } + + ret["value"] = obj; + break; + } + } + + return ret; + } + + private JToken ItemToJson(StackItem item) + { + if (item == null) return null; + + JToken value; + string type = item.GetType().Name; + + switch (item) + { + case VM.Types.Null _: + { + return new JObject + { + ["type"] = type, + }; + } + case Pointer p: + { + return new JObject + { + ["type"] = type, + ["value"] = p.Position + }; + } + case VM.Types.Boolean v: value = new JValue(v.GetBoolean()); break; + case VM.Types.Integer v: value = new JValue(v.GetInteger().ToString()); break; + case VM.Types.ByteString v: value = new JValue(v.GetSpan().ToArray()); break; + case VM.Types.Buffer v: value = new JValue(v.InnerBuffer.ToArray()); break; + //case VM.Types.Struct v: + case VM.Types.Array v: + { + var jarray = new JArray(); + + foreach (var entry in v) + { + jarray.Add(ItemToJson(entry)); + } + + value = jarray; + break; + } + case VM.Types.Map v: + { + var jdic = new JObject(); + + foreach (var entry in v) + { + jdic.Add(entry.Key.GetSpan().ToArray().ToHexString(), ItemToJson(entry.Value)); + } + + value = jdic; + break; + } + case VM.Types.InteropInterface v: + { + type = "Interop"; + var obj = v.GetInterface(); + + value = obj.GetType().Name.ToString(); + break; + } + default: throw new NotImplementedException(); + } + + return new JObject + { + ["type"] = type, + ["value"] = value + }; + } + + /// + /// Assert with message + /// + /// A + /// B + /// Message + private static void AssertAreEqual(object expected, object actual, string message) + { + if (expected is byte[] ba) expected = ba.ToHexString().ToUpperInvariant(); + if (actual is byte[] bb) actual = bb.ToHexString().ToUpperInvariant(); + + if (expected.ToJson() != actual.ToJson()) + { + throw new Exception(message + + $"{Environment.NewLine}Expected:{Environment.NewLine + expected.ToString() + Environment.NewLine}Actual:{Environment.NewLine + actual.ToString()}"); + } + } + } +} diff --git a/tests/neo.UnitTests/IO/Caching/UT_CloneCache.cs b/tests/neo.UnitTests/IO/Caching/UT_CloneCache.cs deleted file mode 100644 index bc0f15034f..0000000000 --- a/tests/neo.UnitTests/IO/Caching/UT_CloneCache.cs +++ /dev/null @@ -1,158 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; -using Neo.Persistence; -using Neo.SmartContract; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Neo.UnitTests.IO.Caching -{ - [TestClass] - public class UT_CloneCache - { - ClonedCache clonedCache; - MyDataCache myDataCache; - - [TestInitialize] - public void Init() - { - myDataCache = new MyDataCache(); - clonedCache = new ClonedCache(myDataCache); - } - - [TestMethod] - public void TestCloneCache() - { - clonedCache.Should().NotBeNull(); - } - - [TestMethod] - public void TestAddInternal() - { - clonedCache.Add(new MyKey("key1"), new MyValue("value1")); - clonedCache[new MyKey("key1")].Should().Be(new MyValue("value1")); - - clonedCache.Commit(); - Assert.IsTrue(myDataCache[new MyKey("key1")].Value.SequenceEqual(new MyValue("value1").Value)); - } - - [TestMethod] - public void TestDeleteInternal() - { - myDataCache.Add(new MyKey("key1"), new MyValue("value1")); - clonedCache.Delete(new MyKey("key1")); // trackable.State = TrackState.Deleted - clonedCache.Commit(); - - clonedCache.TryGet(new MyKey("key1")).Should().BeNull(); - myDataCache.TryGet(new MyKey("key1")).Should().BeNull(); - } - - [TestMethod] - public void TestFindInternal() - { - clonedCache.Add(new MyKey("key1"), new MyValue("value1")); - myDataCache.Add(new MyKey("key2"), new MyValue("value2")); - myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - - var items = clonedCache.Find(new MyKey("key1").ToArray()); - items.ElementAt(0).Key.Should().Be(new MyKey("key1")); - items.ElementAt(0).Value.Should().Be(new MyValue("value1")); - items.Count().Should().Be(1); - - items = clonedCache.Find(new MyKey("key2").ToArray()); - items.ElementAt(0).Key.Should().Be(new MyKey("key2")); - new MyValue("value2").Should().Be(items.ElementAt(0).Value); - items.Count().Should().Be(1); - - items = clonedCache.Find(new MyKey("key3").ToArray()); - items.ElementAt(0).Key.Should().Be(new MyKey("key3")); - new MyValue("value3").Should().Be(items.ElementAt(0).Value); - items.Count().Should().Be(1); - - items = clonedCache.Find(new MyKey("key4").ToArray()); - items.Count().Should().Be(0); - } - - [TestMethod] - public void TestGetInternal() - { - clonedCache.Add(new MyKey("key1"), new MyValue("value1")); - myDataCache.Add(new MyKey("key2"), new MyValue("value2")); - myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - - new MyValue("value1").Should().Be(clonedCache[new MyKey("key1")]); - new MyValue("value2").Should().Be(clonedCache[new MyKey("key2")]); - new MyValue("value3").Should().Be(clonedCache[new MyKey("key3")]); - - Action action = () => - { - var item = clonedCache[new MyKey("key4")]; - }; - action.Should().Throw(); - } - - [TestMethod] - public void TestTryGetInternal() - { - clonedCache.Add(new MyKey("key1"), new MyValue("value1")); - myDataCache.Add(new MyKey("key2"), new MyValue("value2")); - myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - - new MyValue("value1").Should().Be(clonedCache.TryGet(new MyKey("key1"))); - new MyValue("value2").Should().Be(clonedCache.TryGet(new MyKey("key2"))); - new MyValue("value3").Should().Be(clonedCache.TryGet(new MyKey("key3"))); - clonedCache.TryGet(new MyKey("key4")).Should().BeNull(); - } - - [TestMethod] - public void TestUpdateInternal() - { - clonedCache.Add(new MyKey("key1"), new MyValue("value1")); - myDataCache.Add(new MyKey("key2"), new MyValue("value2")); - myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - - clonedCache.GetAndChange(new MyKey("key1")).Value = Encoding.Default.GetBytes("value_new_1"); - clonedCache.GetAndChange(new MyKey("key2")).Value = Encoding.Default.GetBytes("value_new_2"); - clonedCache.GetAndChange(new MyKey("key3")).Value = Encoding.Default.GetBytes("value_new_3"); - - clonedCache.Commit(); - - new MyValue("value_new_1").Should().Be(clonedCache[new MyKey("key1")]); - new MyValue("value_new_2").Should().Be(clonedCache[new MyKey("key2")]); - new MyValue("value_new_3").Should().Be(clonedCache[new MyKey("key3")]); - new MyValue("value_new_2").Should().Be(clonedCache[new MyKey("key2")]); - } - - [TestMethod] - public void TestCacheOverrideIssue2572() - { - var snapshot = TestBlockchain.GetTestSnapshot(); - var storages = snapshot.CreateSnapshot(); - - storages.Add - ( - new StorageKey() { Key = new byte[] { 0x00, 0x01 }, Id = 0 }, - new StorageItem() { Value = Array.Empty() } - ); - storages.Add - ( - new StorageKey() { Key = new byte[] { 0x01, 0x01 }, Id = 0 }, - new StorageItem() { Value = new byte[] { 0x05 } } - ); - - storages.Commit(); - - var item = storages.GetAndChange(new StorageKey() { Key = new byte[] { 0x01, 0x01 }, Id = 0 }); - item.Value = new byte[] { 0x06 }; - - var res = snapshot.TryGet(new StorageKey() { Key = new byte[] { 0x01, 0x01 }, Id = 0 }); - Assert.AreEqual("05", res.Value.ToHexString()); - storages.Commit(); - res = snapshot.TryGet(new StorageKey() { Key = new byte[] { 0x01, 0x01 }, Id = 0 }); - Assert.AreEqual("06", res.Value.ToHexString()); - } - } -} diff --git a/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs b/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs deleted file mode 100644 index 7cb4a19115..0000000000 --- a/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs +++ /dev/null @@ -1,459 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; -using Neo.Persistence; -using Neo.SmartContract; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace Neo.UnitTests.IO.Caching -{ - class MyKey : StorageKey, IEquatable, IComparable, IComparable, IEquatable, IComparable - { - public int Size => Key.Length; - - public MyKey(UInt256 hash) - { - Key = hash.ToArray(); - } - - public MyKey(StorageKey key) - { - Id = key.Id; - Key = key.Key; - } - - public MyKey(string val) - { - Key = Encoding.UTF8.GetBytes(val); - } - - public void Deserialize(BinaryReader reader) - { - Key = Encoding.UTF8.GetBytes(reader.ReadString()); - } - - public void Serialize(BinaryWriter writer) - { - writer.Write(Key); - } - public bool Equals(MyKey other) - { - if (other == null) return false; - return Id == other.Id && Key.SequenceEqual(other.Key); - } - - public override bool Equals(object obj) - { - if (obj is not MyKey other) return false; - return Id == other.Id && Key.SequenceEqual(other.Key); - } - - public override int GetHashCode() - { - return HashCode.Combine(Id, Key.Length); - } - - public int CompareTo(MyKey obj) - { - return CompareTo((StorageKey)obj); - } - - public int CompareTo(StorageKey obj) - { - if (obj is null) throw new Exception(); - int ret = Id.CompareTo(obj.Id); - if (ret != 0) return ret; - return Encoding.UTF8.GetString(Key).CompareTo(Encoding.UTF8.GetString(obj.Key)); - } - - public int CompareTo(object obj) - { - if (obj is not StorageKey key) throw new Exception(); - return CompareTo(key); - } - } - - public class MyValue : StorageItem, ISerializable, IEquatable, IEquatable - { - public MyValue(UInt256 hash) - { - Value = hash.ToArray(); - } - - public MyValue(string val) - { - Value = Encoding.Default.GetBytes(val); - } - - public MyValue(byte[] val) - { - Value = val; - } - - public void FromReplica(MyValue replica) - { - Value = replica.Value; - } - - public bool Equals(StorageItem other) - { - if (other == null) return false; - return (Value == null && other.Value == null) || Value.SequenceEqual(other.Value); - } - - public bool Equals(MyValue other) - { - if (other == null) return false; - return (Value == null && other.Value == null) || Value.SequenceEqual(other.Value); - } - - public override bool Equals(object obj) - { - if (obj is not StorageItem other) return false; - return (Value == null && other.Value == null) || Value.SequenceEqual(other.Value); - } - - public override int GetHashCode() - { - return Value.Length; - } - } - - class MyDataCache : DataCache - { - public Dictionary InnerDict = new(); - - protected override void DeleteInternal(StorageKey key) - { - InnerDict.Remove(key); - } - - protected override void AddInternal(StorageKey key, StorageItem value) - { - InnerDict.Add(key, new MyValue(value.Value)); - } - - protected override IEnumerable<(StorageKey, StorageItem)> SeekInternal(byte[] keyOrPrefix, SeekDirection direction = SeekDirection.Forward) - { - if (direction == SeekDirection.Forward) - return InnerDict.OrderBy(kvp => kvp.Key) - .Where(kvp => ByteArrayComparer.Default.Compare(kvp.Key.ToArray(), keyOrPrefix) >= 0) - .Select(p => (p.Key, (StorageItem)new MyValue(p.Value.Value))); - else - return InnerDict.OrderByDescending(kvp => kvp.Key) - .Where(kvp => ByteArrayComparer.Reverse.Compare(kvp.Key.ToArray(), keyOrPrefix) >= 0) - .Select(p => (p.Key, (StorageItem)new MyValue(p.Value.Value))); - } - - protected override StorageItem GetInternal(StorageKey key) - { - if (InnerDict.TryGetValue(key, out var value)) - { - return new MyValue(value.Value); - } - throw new KeyNotFoundException(); - } - - protected override StorageItem TryGetInternal(StorageKey key) - { - if (InnerDict.TryGetValue(key, out var value)) - { - return new MyValue(value.Value); - } - return null; - } - - protected override bool ContainsInternal(StorageKey key) - { - return InnerDict.ContainsKey(key); - } - - protected override void UpdateInternal(StorageKey key, StorageItem value) - { - InnerDict[key] = new MyValue(value.Value); - } - } - - [TestClass] - public class UT_DataCache - { - MyDataCache myDataCache; - - [TestInitialize] - public void Initialize() - { - myDataCache = new MyDataCache(); - } - - [TestMethod] - public void TestAccessByKey() - { - myDataCache.Add(new MyKey("key1"), new MyValue("value1")); - myDataCache.Add(new MyKey("key2"), new MyValue("value2")); - - myDataCache[new MyKey("key1")].Should().Be(new MyValue("value1")); - - // case 2 read from inner - myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - myDataCache[new MyKey("key3")].Should().Be(new MyValue("value3")); - } - - [TestMethod] - public void TestAccessByNotFoundKey() - { - Action action = () => - { - var item = myDataCache[new MyKey("key1")]; - }; - action.Should().Throw(); - } - - [TestMethod] - public void TestAccessByDeletedKey() - { - myDataCache.InnerDict.Add(new MyKey("key1"), new MyValue("value1")); - myDataCache.Delete(new MyKey("key1")); - - Action action = () => - { - var item = myDataCache[new MyKey("key1")]; - }; - action.Should().Throw(); - } - - [TestMethod] - public void TestAdd() - { - myDataCache.Add(new MyKey("key1"), new MyValue("value1")); - myDataCache[new MyKey("key1")].Should().Be(new MyValue("value1")); - - Action action = () => myDataCache.Add(new MyKey("key1"), new MyValue("value1")); - action.Should().Throw(); - - myDataCache.InnerDict.Add(new MyKey("key2"), new MyValue("value2")); - myDataCache.Delete(new MyKey("key2")); // trackable.State = TrackState.Deleted - myDataCache.Add(new MyKey("key2"), new MyValue("value2")); // trackable.State = TrackState.Changed - - action = () => myDataCache.Add(new MyKey("key2"), new MyValue("value2")); - action.Should().Throw(); - } - - [TestMethod] - public void TestCommit() - { - myDataCache.Add(new MyKey("key1"), new MyValue("value1")); // trackable.State = TrackState.Added - - myDataCache.InnerDict.Add(new MyKey("key2"), new MyValue("value2")); - myDataCache.Delete(new MyKey("key2")); // trackable.State = TrackState.Deleted - - myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - myDataCache.Delete(new MyKey("key3")); // trackable.State = TrackState.Deleted - myDataCache.Add(new MyKey("key3"), new MyValue("value4")); // trackable.State = TrackState.Changed - - myDataCache.Commit(); - - myDataCache.InnerDict[new MyKey("key1")].Should().Be(new MyValue("value1")); - myDataCache.InnerDict.ContainsKey(new MyKey("key2")).Should().BeFalse(); - myDataCache.InnerDict[new MyKey("key3")].Should().Be(new MyValue("value4")); - } - - [TestMethod] - public void TestCreateSnapshot() - { - myDataCache.CreateSnapshot().Should().NotBeNull(); - } - - [TestMethod] - public void TestDelete() - { - myDataCache.Add(new MyKey("key1"), new MyValue("value1")); - myDataCache.Delete(new MyKey("key1")); - myDataCache.InnerDict.ContainsKey(new MyKey("key1")).Should().BeFalse(); - - myDataCache.InnerDict.Add(new MyKey("key2"), new MyValue("value2")); - myDataCache.Delete(new MyKey("key2")); - myDataCache.Commit(); - myDataCache.InnerDict.ContainsKey(new MyKey("key2")).Should().BeFalse(); - } - - [TestMethod] - public void TestFind() - { - myDataCache.Add(new MyKey("key1"), new MyValue("value1")); - myDataCache.Add(new MyKey("key2"), new MyValue("value2")); - - myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - myDataCache.InnerDict.Add(new MyKey("key4"), new MyValue("value4")); - - var items = myDataCache.Find(new MyKey("key1").ToArray()); - new MyKey("key1").Should().Be(items.ElementAt(0).Key); - new MyValue("value1").Should().Be(items.ElementAt(0).Value); - items.Count().Should().Be(1); - - items = myDataCache.Find(new MyKey("key5").ToArray()); - items.Count().Should().Be(0); - } - - [TestMethod] - public void TestSeek() - { - myDataCache.Add(new MyKey("key1"), new MyValue("value1")); - myDataCache.Add(new MyKey("key2"), new MyValue("value2")); - - myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - myDataCache.InnerDict.Add(new MyKey("key4"), new MyValue("value4")); - - var items = myDataCache.Seek(new MyKey("key3").ToArray(), SeekDirection.Backward).ToArray(); - new MyKey("key3").Should().Be(items[0].Key); - new MyValue("value3").Should().Be(items[0].Value); - new MyKey("key2").Should().Be(items[1].Key); - new MyValue("value2").Should().Be(items[1].Value); - items.Length.Should().Be(3); - - items = myDataCache.Seek(new MyKey("key5").ToArray(), SeekDirection.Forward).ToArray(); - items.Length.Should().Be(0); - } - - [TestMethod] - public void TestFindRange() - { - myDataCache.Add(new MyKey("key1"), new MyValue("value1")); - myDataCache.Add(new MyKey("key2"), new MyValue("value2")); - - myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - myDataCache.InnerDict.Add(new MyKey("key4"), new MyValue("value4")); - - var items = myDataCache.FindRange(new MyKey("key3").ToArray(), new MyKey("key5").ToArray()).ToArray(); - new MyKey("key3").Should().Be(items[0].Key); - new MyValue("value3").Should().Be(items[0].Value); - new MyKey("key4").Should().Be(items[1].Key); - new MyValue("value4").Should().Be(items[1].Value); - items.Length.Should().Be(2); - - // case 2 Need to sort the cache of myDataCache - - myDataCache = new MyDataCache(); - myDataCache.Add(new MyKey("key1"), new MyValue("value1")); - myDataCache.Add(new MyKey("key2"), new MyValue("value2")); - - myDataCache.InnerDict.Add(new MyKey("key4"), new MyValue("value4")); - myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - - items = myDataCache.FindRange(new MyKey("key3").ToArray(), new MyKey("key5").ToArray()).ToArray(); - new MyKey("key3").Should().Be(items[0].Key); - new MyValue("value3").Should().Be(items[0].Value); - new MyKey("key4").Should().Be(items[1].Key); - new MyValue("value4").Should().Be(items[1].Value); - items.Length.Should().Be(2); - - // case 3 FindRange by Backward - - myDataCache = new MyDataCache(); - myDataCache.Add(new MyKey("key1"), new MyValue("value1")); - myDataCache.Add(new MyKey("key2"), new MyValue("value2")); - - myDataCache.InnerDict.Add(new MyKey("key4"), new MyValue("value4")); - myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - myDataCache.InnerDict.Add(new MyKey("key5"), new MyValue("value5")); - - items = myDataCache.FindRange(new MyKey("key5").ToArray(), new MyKey("key3").ToArray(), SeekDirection.Backward).ToArray(); - new MyKey("key5").Should().Be(items[0].Key); - new MyValue("value5").Should().Be(items[0].Value); - new MyKey("key4").Should().Be(items[1].Key); - new MyValue("value4").Should().Be(items[1].Value); - items.Length.Should().Be(2); - } - - [TestMethod] - public void TestGetChangeSet() - { - myDataCache.Add(new MyKey("key1"), new MyValue("value1")); // trackable.State = TrackState.Added - myDataCache.Add(new MyKey("key2"), new MyValue("value2")); // trackable.State = TrackState.Added - - myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - myDataCache.InnerDict.Add(new MyKey("key4"), new MyValue("value4")); - myDataCache.Delete(new MyKey("key3")); // trackable.State = TrackState.Deleted - myDataCache.Delete(new MyKey("key4")); // trackable.State = TrackState.Deleted - - var items = myDataCache.GetChangeSet(); - int i = 0; - foreach (var item in items) - { - i++; - new MyKey("key" + i).Should().Be(item.Key); - new MyValue("value" + i).Should().Be(item.Item); - } - i.Should().Be(4); - } - - [TestMethod] - public void TestGetAndChange() - { - myDataCache.Add(new MyKey("key1"), new MyValue("value1")); // trackable.State = TrackState.Added - myDataCache.InnerDict.Add(new MyKey("key2"), new MyValue("value2")); - myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - myDataCache.Delete(new MyKey("key3")); // trackable.State = TrackState.Deleted - - myDataCache.GetAndChange(new MyKey("key1"), () => new MyValue("value_bk_1")).Should().Be(new MyValue("value1")); - myDataCache.GetAndChange(new MyKey("key2"), () => new MyValue("value_bk_2")).Should().Be(new MyValue("value2")); - myDataCache.GetAndChange(new MyKey("key3"), () => new MyValue("value_bk_3")).Should().Be(new MyValue("value_bk_3")); - myDataCache.GetAndChange(new MyKey("key4"), () => new MyValue("value_bk_4")).Should().Be(new MyValue("value_bk_4")); - } - - [TestMethod] - public void TestGetOrAdd() - { - myDataCache.Add(new MyKey("key1"), new MyValue("value1")); // trackable.State = TrackState.Added - myDataCache.InnerDict.Add(new MyKey("key2"), new MyValue("value2")); - myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - myDataCache.Delete(new MyKey("key3")); // trackable.State = TrackState.Deleted - - myDataCache.GetOrAdd(new MyKey("key1"), () => new MyValue("value_bk_1")).Should().Be(new MyValue("value1")); - myDataCache.GetOrAdd(new MyKey("key2"), () => new MyValue("value_bk_2")).Should().Be(new MyValue("value2")); - myDataCache.GetOrAdd(new MyKey("key3"), () => new MyValue("value_bk_3")).Should().Be(new MyValue("value_bk_3")); - myDataCache.GetOrAdd(new MyKey("key4"), () => new MyValue("value_bk_4")).Should().Be(new MyValue("value_bk_4")); - } - - [TestMethod] - public void TestTryGet() - { - myDataCache.Add(new MyKey("key1"), new MyValue("value1")); // trackable.State = TrackState.Added - myDataCache.InnerDict.Add(new MyKey("key2"), new MyValue("value2")); - myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - myDataCache.Delete(new MyKey("key3")); // trackable.State = TrackState.Deleted - - myDataCache.TryGet(new MyKey("key1")).Should().Be(new MyValue("value1")); - myDataCache.TryGet(new MyKey("key2")).Should().Be(new MyValue("value2")); - myDataCache.TryGet(new MyKey("key3")).Should().BeNull(); - } - - [TestMethod] - public void TestFindInvalid() - { - var myDataCache = new MyDataCache(); - myDataCache.Add(new MyKey("key1"), new MyValue("value1")); - - myDataCache.InnerDict.Add(new MyKey("key2"), new MyValue("value2")); - myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - myDataCache.InnerDict.Add(new MyKey("key4"), new MyValue("value3")); - - var items = myDataCache.Find().GetEnumerator(); - items.MoveNext().Should().Be(true); - items.Current.Key.Should().Be(new MyKey("key1")); - - myDataCache.TryGet(new MyKey("key3")); // GETLINE - - items.MoveNext().Should().Be(true); - items.Current.Key.Should().Be(new MyKey("key2")); - items.MoveNext().Should().Be(true); - items.Current.Key.Should().Be(new MyKey("key3")); - items.MoveNext().Should().Be(true); - items.Current.Key.Should().Be(new MyKey("key4")); - items.MoveNext().Should().Be(false); - } - } -} diff --git a/tests/neo.UnitTests/IO/Json/UT_JBoolean.cs b/tests/neo.UnitTests/IO/Json/UT_JBoolean.cs deleted file mode 100644 index ea080cd423..0000000000 --- a/tests/neo.UnitTests/IO/Json/UT_JBoolean.cs +++ /dev/null @@ -1,27 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; - -namespace Neo.UnitTests.IO.Json -{ - [TestClass] - public class UT_JBoolean - { - private JBoolean jFalse; - private JBoolean jTrue; - - [TestInitialize] - public void SetUp() - { - jFalse = new JBoolean(); - jTrue = new JBoolean(true); - } - - [TestMethod] - public void TestAsNumber() - { - jFalse.AsNumber().Should().Be(0); - jTrue.AsNumber().Should().Be(1); - } - } -} diff --git a/tests/neo.UnitTests/IO/Json/UT_JNumber.cs b/tests/neo.UnitTests/IO/Json/UT_JNumber.cs deleted file mode 100644 index 677157a1f0..0000000000 --- a/tests/neo.UnitTests/IO/Json/UT_JNumber.cs +++ /dev/null @@ -1,59 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using System; - -namespace Neo.UnitTests.IO.Json -{ - enum Woo - { - Tom, - Jerry, - James - } - - [TestClass] - public class UT_JNumber - { - private JNumber maxInt; - private JNumber minInt; - private JNumber zero; - - [TestInitialize] - public void SetUp() - { - maxInt = new JNumber(JNumber.MAX_SAFE_INTEGER); - minInt = new JNumber(JNumber.MIN_SAFE_INTEGER); - zero = new JNumber(); - } - - [TestMethod] - public void TestAsBoolean() - { - maxInt.AsBoolean().Should().BeTrue(); - zero.AsBoolean().Should().BeFalse(); - } - - [TestMethod] - public void TestAsString() - { - Action action1 = () => new JNumber(double.PositiveInfinity).AsString(); - action1.Should().Throw(); - - Action action2 = () => new JNumber(double.NegativeInfinity).AsString(); - action2.Should().Throw(); - - Action action3 = () => new JNumber(double.NaN).AsString(); - action3.Should().Throw(); - } - - [TestMethod] - public void TestTryGetEnum() - { - zero.TryGetEnum().Should().Be(Woo.Tom); - new JNumber(1).TryGetEnum().Should().Be(Woo.Jerry); - new JNumber(2).TryGetEnum().Should().Be(Woo.James); - new JNumber(3).TryGetEnum().Should().Be(Woo.Tom); - } - } -} diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs deleted file mode 100644 index e415d2528e..0000000000 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ /dev/null @@ -1,103 +0,0 @@ -using Akka.TestKit; -using Akka.TestKit.Xunit2; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Ledger; -using Neo.Network.P2P.Payloads; -using Neo.Persistence; -using Neo.SmartContract; -using Neo.SmartContract.Native; -using Neo.Wallets; -using Neo.Wallets.NEP6; -using System; -using System.Linq; -using System.Numerics; - -namespace Neo.UnitTests.Ledger -{ - [TestClass] - public class UT_Blockchain : TestKit - { - private NeoSystem system; - private Transaction txSample; - private TestProbe senderProbe; - - [TestInitialize] - public void Initialize() - { - system = TestBlockchain.TheNeoSystem; - senderProbe = CreateTestProbe(); - txSample = new Transaction() - { - Attributes = Array.Empty(), - Script = Array.Empty(), - Signers = new Signer[] { new Signer() { Account = UInt160.Zero } }, - Witnesses = Array.Empty() - }; - system.MemPool.TryAdd(txSample, TestBlockchain.GetTestSnapshot()); - } - - [TestMethod] - public void TestValidTransaction() - { - var snapshot = TestBlockchain.TheNeoSystem.GetSnapshot(); - var walletA = TestUtils.GenerateTestWallet(); - - using var unlockA = walletA.Unlock("123"); - var acc = walletA.CreateAccount(); - - // Fake balance - - var key = new KeyBuilder(NativeContract.GAS.Id, 20).Add(acc.ScriptHash); - var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); - entry.GetInteroperable().Balance = 100_000_000 * NativeContract.GAS.Factor; - snapshot.Commit(); - - // Make transaction - - var tx = CreateValidTx(snapshot, walletA, acc.ScriptHash, 0); - - senderProbe.Send(system.Blockchain, tx); - senderProbe.ExpectMsg(p => p.Result == VerifyResult.Succeed); - - senderProbe.Send(system.Blockchain, tx); - senderProbe.ExpectMsg(p => p.Result == VerifyResult.AlreadyExists); - } - - internal static StorageKey CreateStorageKey(byte prefix, byte[] key = null) - { - StorageKey storageKey = new() - { - Id = NativeContract.NEO.Id, - Key = new byte[sizeof(byte) + (key?.Length ?? 0)] - }; - storageKey.Key[0] = prefix; - key?.CopyTo(storageKey.Key.AsSpan(1)); - return storageKey; - } - - private static Transaction CreateValidTx(DataCache snapshot, NEP6Wallet wallet, UInt160 account, uint nonce) - { - var tx = wallet.MakeTransaction(snapshot, new TransferOutput[] - { - new TransferOutput() - { - AssetId = NativeContract.GAS.Hash, - ScriptHash = account, - Value = new BigDecimal(BigInteger.One,8) - } - }, - account); - - tx.Nonce = nonce; - - var data = new ContractParametersContext(snapshot, tx, ProtocolSettings.Default.Network); - Assert.IsNull(data.GetSignatures(tx.Sender)); - Assert.IsTrue(wallet.Sign(data)); - Assert.IsTrue(data.Completed); - Assert.AreEqual(1, data.GetSignatures(tx.Sender).Count()); - - tx.Witnesses = data.GetWitnesses(); - return tx; - } - } -} diff --git a/tests/neo.UnitTests/Ledger/UT_TransactionState.cs b/tests/neo.UnitTests/Ledger/UT_TransactionState.cs deleted file mode 100644 index b6ff40ebe5..0000000000 --- a/tests/neo.UnitTests/Ledger/UT_TransactionState.cs +++ /dev/null @@ -1,53 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Network.P2P.Payloads; -using Neo.SmartContract; -using Neo.SmartContract.Native; -using Neo.VM; -using System; -using System.IO; - -namespace Neo.UnitTests.Ledger -{ - [TestClass] - public class UT_TransactionState - { - TransactionState origin; - - [TestInitialize] - public void Initialize() - { - origin = new TransactionState - { - BlockIndex = 1, - Transaction = new Transaction() - { - Attributes = Array.Empty(), - Script = new byte[] { (byte)OpCode.PUSH1 }, - Signers = new Signer[] { new Signer() { Account = UInt160.Zero } }, - Witnesses = new Witness[] { new Witness() { - InvocationScript=Array.Empty(), - VerificationScript=Array.Empty() - } } - } - }; - } - - [TestMethod] - public void TestDeserialize() - { - using MemoryStream ms = new MemoryStream(1024); - using BinaryReader reader = new BinaryReader(ms); - - var data = BinarySerializer.Serialize(((IInteroperable)origin).ToStackItem(null), 1024); - ms.Write(data); - ms.Seek(0, SeekOrigin.Begin); - - TransactionState dest = new TransactionState(); - ((IInteroperable)dest).FromStackItem(BinarySerializer.Deserialize(reader, ExecutionEngineLimits.Default, null)); - - dest.BlockIndex.Should().Be(origin.BlockIndex); - dest.Transaction.Hash.Should().Be(origin.Transaction.Hash); - } - } -} diff --git a/tests/neo.UnitTests/Network/P2P/Capabilities/UT_ServerCapability.cs b/tests/neo.UnitTests/Network/P2P/Capabilities/UT_ServerCapability.cs deleted file mode 100644 index f0769d31be..0000000000 --- a/tests/neo.UnitTests/Network/P2P/Capabilities/UT_ServerCapability.cs +++ /dev/null @@ -1,55 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; -using Neo.Network.P2P.Capabilities; -using System; -using System.IO; - -namespace Neo.UnitTests.Network.P2P.Capabilities -{ - [TestClass] - public class UT_ServerCapability - { - [TestMethod] - public void Size_Get() - { - var test = new ServerCapability(NodeCapabilityType.TcpServer) { Port = 1 }; - test.Size.Should().Be(3); - - test = new ServerCapability(NodeCapabilityType.WsServer) { Port = 2 }; - test.Size.Should().Be(3); - } - - [TestMethod] - public void DeserializeAndSerialize() - { - var test = new ServerCapability(NodeCapabilityType.WsServer) { Port = 2 }; - var buffer = test.ToArray(); - - using var br = new BinaryReader(new MemoryStream(buffer)); - var clone = (ServerCapability)ServerCapability.DeserializeFrom(br); - - Assert.AreEqual(test.Port, clone.Port); - Assert.AreEqual(test.Type, clone.Type); - - clone = new ServerCapability(NodeCapabilityType.WsServer, 123); - br.BaseStream.Seek(0, SeekOrigin.Begin); - ((ISerializable)clone).Deserialize(br); - - Assert.AreEqual(test.Port, clone.Port); - Assert.AreEqual(test.Type, clone.Type); - - clone = new ServerCapability(NodeCapabilityType.TcpServer, 123); - - br.BaseStream.Seek(0, SeekOrigin.Begin); - Assert.ThrowsException(() => ((ISerializable)clone).Deserialize(br)); - Assert.ThrowsException(() => new ServerCapability(NodeCapabilityType.FullNode)); - - // Wrog type - br.BaseStream.Seek(0, SeekOrigin.Begin); - br.BaseStream.WriteByte(0xFF); - br.BaseStream.Seek(0, SeekOrigin.Begin); - Assert.ThrowsException(() => ServerCapability.DeserializeFrom(br)); - } - } -} diff --git a/tests/neo.UnitTests/Plugins/UT_Plugin.cs b/tests/neo.UnitTests/Plugins/UT_Plugin.cs deleted file mode 100644 index f194f0cff3..0000000000 --- a/tests/neo.UnitTests/Plugins/UT_Plugin.cs +++ /dev/null @@ -1,91 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Network.P2P.Payloads; -using Neo.Plugins; -using System; - -namespace Neo.UnitTests.Plugins -{ - [TestClass] - public class UT_Plugin - { - private static readonly object locker = new(); - - private class DummyP2PPlugin : IP2PPlugin - { - public void OnVerifiedInventory(IInventory inventory) { } - } - private class DummyPersistencePlugin : IPersistencePlugin { } - - [TestMethod] - public void TestIP2PPlugin() - { - var pp = new DummyP2PPlugin() as IP2PPlugin; - Assert.IsTrue(pp.OnP2PMessage(null, null)); - } - - [TestMethod] - public void TestIPersistencePlugin() - { - var pp = new DummyPersistencePlugin() as IPersistencePlugin; - - Assert.IsFalse(pp.ShouldThrowExceptionFromCommit(null)); - - // With empty default implementation - - pp.OnCommit(null, null, null); - pp.OnPersist(null, null, null, null); - } - - [TestMethod] - public void TestGetConfigFile() - { - var pp = new TestLogPlugin(); - var file = pp.ConfigFile; - file.EndsWith("config.json").Should().BeTrue(); - } - - [TestMethod] - public void TestGetName() - { - var pp = new TestLogPlugin(); - pp.Name.Should().Be("TestLogPlugin"); - } - - [TestMethod] - public void TestGetVersion() - { - var pp = new TestLogPlugin(); - Action action = () => pp.Version.ToString(); - action.Should().NotThrow(); - } - - [TestMethod] - public void TestLog() - { - var lp = new TestLogPlugin(); - lp.LogMessage("Hello"); - lp.Output.Should().Be("Plugin:TestLogPlugin_Info_Hello"); - } - - [TestMethod] - public void TestSendMessage() - { - lock (locker) - { - Plugin.Plugins.Clear(); - Plugin.SendMessage("hey1").Should().BeFalse(); - - var lp = new TestLogPlugin(); - Plugin.SendMessage("hey2").Should().BeTrue(); - } - } - - [TestMethod] - public void TestGetConfiguration() - { - var pp = new TestLogPlugin(); - pp.TestGetConfiguration().Key.Should().Be("PluginConfiguration"); - } - } -} diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs deleted file mode 100644 index 2141cc03b0..0000000000 --- a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs +++ /dev/null @@ -1,113 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Cryptography.ECC; -using Neo.IO.Json; -using Neo.SmartContract; -using Neo.SmartContract.Manifest; - -namespace Neo.UnitTests.SmartContract.Manifest -{ - [TestClass] - public class UT_ContractManifest - { - [TestMethod] - public void ParseFromJson_Default() - { - var json = @"{""name"":""testManifest"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":null}"; - var manifest = ContractManifest.Parse(json); - - Assert.AreEqual(manifest.ToJson().ToString(), json); - Assert.AreEqual(manifest.ToJson().ToString(), TestUtils.CreateDefaultManifest().ToJson().ToString()); - Assert.IsTrue(manifest.IsValid(UInt160.Zero)); - } - - [TestMethod] - public void ParseFromJson_Permissions() - { - var json = @"{""name"":""testManifest"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""0x0000000000000000000000000000000000000000"",""methods"":[""method1"",""method2""]}],""trusts"":[],""extra"":null}"; - var manifest = ContractManifest.Parse(json); - Assert.AreEqual(manifest.ToJson().ToString(), json); - - var check = TestUtils.CreateDefaultManifest(); - check.Permissions = new[] - { - new ContractPermission() - { - Contract = ContractPermissionDescriptor.Create(UInt160.Zero), - Methods = WildcardContainer.Create("method1", "method2") - } - }; - Assert.AreEqual(manifest.ToJson().ToString(), check.ToJson().ToString()); - } - - [TestMethod] - public void ParseFromJson_SafeMethods() - { - var json = @"{""name"":""testManifest"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":null}"; - var manifest = ContractManifest.Parse(json); - Assert.AreEqual(manifest.ToJson().ToString(), json); - - var check = TestUtils.CreateDefaultManifest(); - Assert.AreEqual(manifest.ToJson().ToString(), check.ToJson().ToString()); - } - - [TestMethod] - public void ParseFromJson_Trust() - { - var json = @"{""name"":""testManifest"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[""0x0000000000000000000000000000000000000001""],""extra"":null}"; - var manifest = ContractManifest.Parse(json); - Assert.AreEqual(manifest.ToJson().ToString(), json); - - var check = TestUtils.CreateDefaultManifest(); - check.Trusts = WildcardContainer.Create(ContractPermissionDescriptor.Create(UInt160.Parse("0x0000000000000000000000000000000000000001"))); - Assert.AreEqual(manifest.ToJson().ToString(), check.ToJson().ToString()); - } - - [TestMethod] - public void ParseFromJson_Groups() - { - var json = @"{""name"":""testManifest"",""groups"":[{""pubkey"":""03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"",""signature"":""QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ==""}],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":null}"; - var manifest = ContractManifest.Parse(json); - Assert.AreEqual(manifest.ToJson().ToString(), json); - - var check = TestUtils.CreateDefaultManifest(); - check.Groups = new ContractGroup[] { new ContractGroup() { PubKey = ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", ECCurve.Secp256r1), Signature = "41414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141".HexToBytes() } }; - Assert.AreEqual(manifest.ToJson().ToString(), check.ToJson().ToString()); - } - - [TestMethod] - public void ParseFromJson_Extra() - { - var json = @"{""name"":""testManifest"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":{""key"":""value""}}"; - var manifest = ContractManifest.Parse(json); - Assert.AreEqual(json, json); - Assert.AreEqual("value", manifest.Extra["key"].AsString(), false); - } - - [TestMethod] - public void TestDeserializeAndSerialize() - { - var expected = TestUtils.CreateDefaultManifest(); - expected.Extra = JObject.Parse(@"{""a"":123}"); - - var clone = new ContractManifest(); - ((IInteroperable)clone).FromStackItem(expected.ToStackItem(null)); - - Assert.AreEqual(expected.Extra.ToString(), @"{""a"":123}"); - Assert.AreEqual(expected.ToString(), clone.ToString()); - - expected.Extra = null; - clone = new ContractManifest(); - ((IInteroperable)clone).FromStackItem(expected.ToStackItem(null)); - - Assert.AreEqual(expected.Extra, clone.Extra); - Assert.AreEqual(expected.ToString(), clone.ToString()); - } - - [TestMethod] - public void TestGenerator() - { - ContractManifest contractManifest = new(); - Assert.IsNotNull(contractManifest); - } - } -} diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs deleted file mode 100644 index 710502bef5..0000000000 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.SmartContract.Native; - -namespace Neo.UnitTests.SmartContract.Native -{ - [TestClass] - public class UT_NativeContract - { - [TestMethod] - public void TestGetContract() - { - Assert.IsTrue(NativeContract.NEO == NativeContract.GetContract(NativeContract.NEO.Hash)); - } - } -} diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs deleted file mode 100644 index 4d0a65fb12..0000000000 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs +++ /dev/null @@ -1,95 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Network.P2P.Payloads; -using Neo.SmartContract; -using System; -using System.Numerics; - -namespace Neo.UnitTests.SmartContract -{ - public partial class UT_ApplicationEngine - { - [TestMethod] - public void TestGetRandomSameBlock() - { - var tx = TestUtils.GetTransaction(UInt160.Zero); - // Even if persisting the same block, in different ApplicationEngine instance, the random number should be different - using var engine_1 = ApplicationEngine.Create(TriggerType.Application, tx, null, TestBlockchain.TheNeoSystem.GenesisBlock, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1100_00000000); - using var engine_2 = ApplicationEngine.Create(TriggerType.Application, tx, null, TestBlockchain.TheNeoSystem.GenesisBlock, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1100_00000000); - - engine_1.LoadScript(new byte[] { 0x01 }); - engine_2.LoadScript(new byte[] { 0x01 }); - - var rand_1 = engine_1.GetRandom(); - var rand_2 = engine_1.GetRandom(); - var rand_3 = engine_1.GetRandom(); - var rand_4 = engine_1.GetRandom(); - var rand_5 = engine_1.GetRandom(); - - var rand_6 = engine_2.GetRandom(); - var rand_7 = engine_2.GetRandom(); - var rand_8 = engine_2.GetRandom(); - var rand_9 = engine_2.GetRandom(); - var rand_10 = engine_2.GetRandom(); - - rand_1.Should().Be(BigInteger.Parse("271339657438512451304577787170704246350")); - rand_2.Should().Be(BigInteger.Parse("3519468259280385525954723453894821326")); - rand_3.Should().Be(BigInteger.Parse("109167038153789065876532298231776118857")); - rand_4.Should().Be(BigInteger.Parse("278188388582393629262399165075733096984")); - rand_5.Should().Be(BigInteger.Parse("252973537848551880583287107760169066816")); - - rand_1.Should().Be(rand_6); - rand_2.Should().Be(rand_7); - rand_3.Should().Be(rand_8); - rand_4.Should().Be(rand_9); - rand_5.Should().Be(rand_10); - } - - [TestMethod] - public void TestGetRandomDifferentBlock() - { - var tx_1 = TestUtils.GetTransaction(UInt160.Zero); - - var tx_2 = new Transaction - { - Version = 0, - Nonce = 2083236893, - ValidUntilBlock = 0, - Signers = Array.Empty(), - Attributes = Array.Empty(), - Script = Array.Empty(), - SystemFee = 0, - NetworkFee = 0, - Witnesses = Array.Empty() - }; - - using var engine_1 = ApplicationEngine.Create(TriggerType.Application, tx_1, null, TestBlockchain.TheNeoSystem.GenesisBlock, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1100_00000000); - // The next_nonce shuld be reinitialized when a new block is persisting - using var engine_2 = ApplicationEngine.Create(TriggerType.Application, tx_2, null, TestBlockchain.TheNeoSystem.GenesisBlock, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1100_00000000); - - var rand_1 = engine_1.GetRandom(); - var rand_2 = engine_1.GetRandom(); - var rand_3 = engine_1.GetRandom(); - var rand_4 = engine_1.GetRandom(); - var rand_5 = engine_1.GetRandom(); - - var rand_6 = engine_2.GetRandom(); - var rand_7 = engine_2.GetRandom(); - var rand_8 = engine_2.GetRandom(); - var rand_9 = engine_2.GetRandom(); - var rand_10 = engine_2.GetRandom(); - - rand_1.Should().Be(BigInteger.Parse("271339657438512451304577787170704246350")); - rand_2.Should().Be(BigInteger.Parse("3519468259280385525954723453894821326")); - rand_3.Should().Be(BigInteger.Parse("109167038153789065876532298231776118857")); - rand_4.Should().Be(BigInteger.Parse("278188388582393629262399165075733096984")); - rand_5.Should().Be(BigInteger.Parse("252973537848551880583287107760169066816")); - - rand_1.Should().NotBe(rand_6); - rand_2.Should().NotBe(rand_7); - rand_3.Should().NotBe(rand_8); - rand_4.Should().NotBe(rand_9); - rand_5.Should().NotBe(rand_10); - } - } -} diff --git a/tests/neo.UnitTests/SmartContract/UT_OpCodePrices.cs b/tests/neo.UnitTests/SmartContract/UT_OpCodePrices.cs deleted file mode 100644 index fa82f194d2..0000000000 --- a/tests/neo.UnitTests/SmartContract/UT_OpCodePrices.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.SmartContract; -using Neo.VM; -using System; - -namespace Neo.UnitTests.SmartContract -{ - [TestClass] - public class UT_OpCodePrices - { - [TestMethod] - public void AllOpcodePriceAreSet() - { - foreach (OpCode opcode in Enum.GetValues(typeof(OpCode))) - Assert.IsTrue(ApplicationEngine.OpCodePrices.ContainsKey(opcode), opcode.ToString()); - } - } -} diff --git a/tests/neo.UnitTests/TestBlockchain.cs b/tests/neo.UnitTests/TestBlockchain.cs deleted file mode 100644 index f1ea5eb975..0000000000 --- a/tests/neo.UnitTests/TestBlockchain.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Neo.Persistence; -using System; - -namespace Neo.UnitTests -{ - public static class TestBlockchain - { - public static readonly NeoSystem TheNeoSystem; - public static readonly UInt160[] DefaultExtensibleWitnessWhiteList; - - static TestBlockchain() - { - Console.WriteLine("initialize NeoSystem"); - TheNeoSystem = new NeoSystem(ProtocolSettings.Default, null, null); - } - - internal static DataCache GetTestSnapshot() - { - return TheNeoSystem.GetSnapshot().CreateSnapshot(); - } - } -} diff --git a/tests/neo.UnitTests/UT_ProtocolSettings.cs b/tests/neo.UnitTests/UT_ProtocolSettings.cs deleted file mode 100644 index 0af209e10f..0000000000 --- a/tests/neo.UnitTests/UT_ProtocolSettings.cs +++ /dev/null @@ -1,51 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.SmartContract.Native; -using Neo.Wallets; - -namespace Neo.UnitTests -{ - [TestClass] - public class UT_ProtocolSettings - { - [TestMethod] - public void CheckFirstLetterOfAddresses() - { - UInt160 min = UInt160.Parse("0x0000000000000000000000000000000000000000"); - min.ToAddress(ProtocolSettings.Default.AddressVersion)[0].Should().Be('N'); - UInt160 max = UInt160.Parse("0xffffffffffffffffffffffffffffffffffffffff"); - max.ToAddress(ProtocolSettings.Default.AddressVersion)[0].Should().Be('N'); - } - - [TestMethod] - public void Default_Network_should_be_mainnet_Network_value() - { - var mainNetNetwork = 0x334F454Eu; - ProtocolSettings.Default.Network.Should().Be(mainNetNetwork); - } - - [TestMethod] - public void TestGetMemoryPoolMaxTransactions() - { - ProtocolSettings.Default.MemoryPoolMaxTransactions.Should().Be(50000); - } - - [TestMethod] - public void TestGetMillisecondsPerBlock() - { - ProtocolSettings.Default.MillisecondsPerBlock.Should().Be(15000); - } - - [TestMethod] - public void TestGetSeedList() - { - ProtocolSettings.Default.SeedList.Should().BeEquivalentTo(new string[] { "seed1.neo.org:10333", "seed2.neo.org:10333", "seed3.neo.org:10333", "seed4.neo.org:10333", "seed5.neo.org:10333", }); - } - - [TestMethod] - public void TestNativeUpdateHistory() - { - ProtocolSettings.Default.NativeUpdateHistory.Count.Should().Be(NativeContract.Contracts.Count); - } - } -} diff --git a/tests/neo.UnitTests/Wallets/SQLite/UT_Account.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_Account.cs deleted file mode 100644 index d04a31e7e1..0000000000 --- a/tests/neo.UnitTests/Wallets/SQLite/UT_Account.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Wallets.SQLite; -using System.Text; - -namespace Neo.UnitTests.Wallets.SQLite -{ - [TestClass] - public class UT_Account - { - [TestMethod] - public void TestGenerator() - { - Account account = new Account(); - Assert.IsNotNull(account); - } - - [TestMethod] - public void TestSetAndGetNep2key() - { - Account account = new Account - { - Nep2key = "123" - }; - Assert.AreEqual("123", account.Nep2key); - } - - [TestMethod] - public void TestSetAndGetPublicKeyHash() - { - Account account = new Account - { - PublicKeyHash = new byte[] { 0x01 } - }; - Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01 }), Encoding.Default.GetString(account.PublicKeyHash)); - } - } -} diff --git a/tests/neo.UnitTests/Wallets/SQLite/UT_Address.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_Address.cs deleted file mode 100644 index 6157d26fe7..0000000000 --- a/tests/neo.UnitTests/Wallets/SQLite/UT_Address.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Wallets.SQLite; -using System.Text; - -namespace Neo.UnitTests.Wallets.SQLite -{ - [TestClass] - public class UT_Address - { - [TestMethod] - public void TestGenerator() - { - Address address = new Address(); - Assert.IsNotNull(address); - } - - [TestMethod] - public void TestSetAndGetScriptHash() - { - Address address = new Address - { - ScriptHash = new byte[] { 0x01 } - }; - Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01 }), Encoding.Default.GetString(address.ScriptHash)); - } - } -} diff --git a/tests/neo.UnitTests/Wallets/SQLite/UT_Contract.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_Contract.cs deleted file mode 100644 index 262c74ce08..0000000000 --- a/tests/neo.UnitTests/Wallets/SQLite/UT_Contract.cs +++ /dev/null @@ -1,65 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Wallets.SQLite; -using System.Text; - -namespace Neo.UnitTests.Wallets.SQLite -{ - [TestClass] - public class UT_Contract - { - [TestMethod] - public void TestGenerator() - { - Contract contract = new Contract(); - Assert.IsNotNull(contract); - } - - [TestMethod] - public void TestSetAndGetRawData() - { - Contract contract = new Contract - { - RawData = new byte[] { 0x01 } - }; - Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01 }), Encoding.Default.GetString(contract.RawData)); - } - - [TestMethod] - public void TestSetAndGetScriptHash() - { - Contract contract = new Contract - { - ScriptHash = new byte[] { 0x01 } - }; - Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01 }), Encoding.Default.GetString(contract.ScriptHash)); - } - - [TestMethod] - public void TestSetAndGetPublicKeyHash() - { - Contract contract = new Contract - { - PublicKeyHash = new byte[] { 0x01 } - }; - Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01 }), Encoding.Default.GetString(contract.PublicKeyHash)); - } - - [TestMethod] - public void TestSetAndGetAccount() - { - Contract contract = new Contract(); - Account account = new Account(); - contract.Account = account; - Assert.AreEqual(account, contract.Account); - } - - [TestMethod] - public void TestSetAndGetAddress() - { - Contract contract = new Contract(); - Address address = new Address(); - contract.Address = address; - Assert.AreEqual(address, contract.Address); - } - } -} diff --git a/tests/neo.UnitTests/Wallets/SQLite/UT_Key.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_Key.cs deleted file mode 100644 index f4876cee11..0000000000 --- a/tests/neo.UnitTests/Wallets/SQLite/UT_Key.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Wallets.SQLite; -using System.Text; - -namespace Neo.UnitTests.Wallets.SQLite -{ - [TestClass] - public class UT_Key - { - [TestMethod] - public void TestGenerator() - { - Key key = new Key(); - Assert.IsNotNull(key); - } - - [TestMethod] - public void TestSetAndGetName() - { - Key key = new Key - { - Name = "AAA" - }; - Assert.AreEqual("AAA", key.Name); - } - - [TestMethod] - public void TestSetAndGetValue() - { - Key key = new Key - { - Value = new byte[] { 0x01 } - }; - Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01 }), Encoding.Default.GetString(key.Value)); - } - } -} diff --git a/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs deleted file mode 100644 index f2ee51da68..0000000000 --- a/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs +++ /dev/null @@ -1,197 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.SmartContract; -using Neo.Wallets; -using Neo.Wallets.NEP6; -using Neo.Wallets.SQLite; -using System; -using System.IO; -using System.Security; -using System.Security.Cryptography; -using System.Threading; -using Contract = Neo.SmartContract.Contract; - -namespace Neo.UnitTests.Wallets.SQLite -{ - [TestClass] - public class UT_UserWallet - { - private static string path; - private static UserWallet wallet; - private static WalletAccount account; - public static string GetRandomPath() - { - string threadName = Thread.CurrentThread.ManagedThreadId.ToString(); - return Path.GetFullPath(string.Format("Wallet_{0}", new Random().Next(1, 1000000).ToString("X8")) + threadName); - } - - [ClassInitialize] - public static void Setup(TestContext ctx) - { - path = GetRandomPath(); - wallet = UserWallet.Create(path, "123456", ProtocolSettings.Default, new ScryptParameters(2, 1, 1)); - byte[] privateKey = new byte[32]; - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) - { - rng.GetBytes(privateKey); - } - account = wallet.CreateAccount(privateKey); - } - - [ClassCleanup] - public static void Cleanup() - { - wallet.Delete(); - } - - [TestMethod] - public void TestChangePassword() - { - wallet.ChangePassword("123455", "654321").Should().BeFalse(); - wallet.ChangePassword("123456", "654321").Should().BeTrue(); - wallet.ChangePassword("654321", "123456").Should().BeTrue(); - } - - [TestMethod] - public void TestContains() - { - wallet.Contains(account.ScriptHash).Should().BeTrue(); - } - - [TestMethod] - public void TestCreateAccountAndGetByPrivateKey() - { - byte[] privateKey = new byte[32]; - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) - { - rng.GetBytes(privateKey); - } - var account = wallet.CreateAccount(privateKey); - var dbAccount = wallet.GetAccount(account.ScriptHash); - account.Should().Be(dbAccount); - - var account1 = wallet.CreateAccount(privateKey); - var dbAccount1 = wallet.GetAccount(account1.ScriptHash); - account1.Should().Be(dbAccount1); - wallet.DeleteAccount(account.ScriptHash); - wallet.DeleteAccount(account1.ScriptHash); - } - - [TestMethod] - public void TestCreateAccountByScriptHash() - { - var account = wallet.CreateAccount(UInt160.Parse("0xa6ee944042f3c7ea900481a95d65e4a887320cf0")); - var dbAccount = wallet.GetAccount(account.ScriptHash); - account.Should().Be(dbAccount); - wallet.DeleteAccount(account.ScriptHash); - } - - [TestMethod] - public void TestCreateAccountBySmartContract() - { - byte[] privateKey = new byte[32]; - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) - { - rng.GetBytes(privateKey); - } - KeyPair key = new KeyPair(privateKey); - VerificationContract contract = new VerificationContract - { - Script = Contract.CreateSignatureRedeemScript(key.PublicKey), - ParameterList = new[] { ContractParameterType.Signature } - }; - var account = wallet.CreateAccount(contract, key); - var dbAccount = wallet.GetAccount(account.ScriptHash); - account.Should().Be(dbAccount); - - byte[] privateKey2 = new byte[32]; - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) - { - rng.GetBytes(privateKey2); - } - KeyPair key2 = new KeyPair(privateKey2); - Contract contract2 = new Contract - { - Script = Contract.CreateSignatureRedeemScript(key2.PublicKey), - ParameterList = new[] { ContractParameterType.Signature } - }; - var account2 = wallet.CreateAccount(contract2, key2); - var dbAccount2 = wallet.GetAccount(account2.ScriptHash); - account2.Should().Be(dbAccount2); - wallet.DeleteAccount(account.ScriptHash); - wallet.DeleteAccount(account2.ScriptHash); - } - - [TestMethod] - public void TestCreateAndOpenSecureString() - { - string myPath = GetRandomPath(); - var ss = new SecureString(); - ss.AppendChar('a'); - ss.AppendChar('b'); - ss.AppendChar('c'); - - var w1 = UserWallet.Create(myPath, ss, ProtocolSettings.Default, new ScryptParameters(0, 0, 0)); - w1.Should().NotBeNull(); - - var w2 = UserWallet.Open(myPath, ss, ProtocolSettings.Default); - w2.Should().NotBeNull(); - - ss.AppendChar('d'); - Action action = () => UserWallet.Open(myPath, ss, ProtocolSettings.Default); - action.Should().Throw(); - - w1.Delete(); - } - - [TestMethod] - public void TestGetAccounts() - { - var ret = wallet.GetAccounts(); - ret.Should().NotBeEmpty(); - foreach (var dbAccount in ret) - { - dbAccount.Should().Be(account); - } - } - - [TestMethod] - public void TestGetName() - { - wallet.Name.Should().Be(Path.GetFileNameWithoutExtension(path)); - } - - [TestMethod] - public void TestGetVersion() - { - Action action = () => wallet.Version.ToString(); - action.Should().NotThrow(); - } - - [TestMethod] - public void TestOpen() - { - var w1 = UserWallet.Open(path, "123456", ProtocolSettings.Default); - w1.Should().NotBeNull(); - - Action action = () => UserWallet.Open(path, "123", ProtocolSettings.Default); - action.Should().Throw(); - } - - [TestMethod] - public void TestToDeleteAccount() - { - bool ret = wallet.DeleteAccount(UInt160.Parse("0xa6ee944042f3c7ea900481a95d65e4a887320cf0")); - ret.Should().BeFalse(); - bool ret2 = wallet.DeleteAccount(account.ScriptHash); - ret2.Should().BeTrue(); - } - - [TestMethod] - public void TestToVerifyPassword() - { - wallet.VerifyPassword("123456").Should().BeTrue(); - wallet.VerifyPassword("123").Should().BeFalse(); - } - } -} diff --git a/tests/neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs deleted file mode 100644 index be3d756b19..0000000000 --- a/tests/neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Wallets.SQLite; - -namespace Neo.UnitTests -{ - [TestClass] - public class UT_UserWalletAccount - { - [TestMethod] - public void TestGenerator() - { - UserWalletAccount account = new UserWalletAccount(UInt160.Zero, ProtocolSettings.Default); - Assert.IsNotNull(account); - } - - [TestMethod] - public void TestGetHasKey() - { - UserWalletAccount account = new UserWalletAccount(UInt160.Zero, ProtocolSettings.Default); - Assert.AreEqual(false, account.HasKey); - } - - [TestMethod] - public void TestGetKey() - { - UserWalletAccount account = new UserWalletAccount(UInt160.Zero, ProtocolSettings.Default); - Assert.AreEqual(null, account.GetKey()); - } - } -} diff --git a/tests/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs deleted file mode 100644 index 879eda73b0..0000000000 --- a/tests/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs +++ /dev/null @@ -1,144 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; -using Neo.SmartContract; -using Neo.Wallets; -using Neo.Wallets.SQLite; -using System; -using System.IO; -using System.Security.Cryptography; -using System.Text; - -namespace Neo.UnitTests -{ - [TestClass] - public class UT_VerificationContract - { - [TestMethod] - public void TestGenerator() - { - byte[] privateKey = new byte[32]; - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) - { - rng.GetBytes(privateKey); - } - KeyPair key = new KeyPair(privateKey); - VerificationContract contract = new VerificationContract - { - Script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), - ParameterList = new[] { ContractParameterType.Signature } - }; - Assert.IsNotNull(contract); - } - - [TestMethod] - public void TestDeserialize() - { - byte[] privateKey = new byte[32]; - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) - { - rng.GetBytes(privateKey); - } - KeyPair key = new KeyPair(privateKey); - VerificationContract contract1 = new VerificationContract - { - Script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), - ParameterList = new[] { ContractParameterType.Signature } - }; - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); - BinaryReader reader = new BinaryReader(stream); - contract1.Serialize(writer); - stream.Seek(0, SeekOrigin.Begin); - VerificationContract contract2 = new VerificationContract(); - contract2.Deserialize(reader); - Assert.AreEqual(Encoding.Default.GetString(contract2.Script), Encoding.Default.GetString(contract1.Script)); - Assert.AreEqual(1, contract2.ParameterList.Length); - Assert.AreEqual(ContractParameterType.Signature, contract2.ParameterList[0]); - } - - [TestMethod] - public void TestEquals() - { - byte[] privateKey = new byte[32]; - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) - { - rng.GetBytes(privateKey); - } - KeyPair key = new KeyPair(privateKey); - VerificationContract contract1 = new VerificationContract - { - Script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), - ParameterList = new[] { ContractParameterType.Signature } - }; - object tempObject = contract1; - VerificationContract contract2 = new VerificationContract - { - Script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), - ParameterList = new[] { ContractParameterType.Signature } - }; - Assert.AreEqual(true, contract1.Equals(tempObject)); - Assert.AreEqual(true, contract1.Equals(contract1)); - Assert.AreEqual(false, contract1.Equals(null)); - Assert.AreEqual(true, contract1.Equals(contract2)); - } - - [TestMethod] - public void TestGetHashCode() - { - byte[] privateKey = new byte[32]; - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) - { - rng.GetBytes(privateKey); - } - KeyPair key = new KeyPair(privateKey); - VerificationContract contract1 = new VerificationContract - { - Script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), - ParameterList = new[] { ContractParameterType.Signature } - }; - byte[] script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey); - Assert.AreEqual(script.ToScriptHash().GetHashCode(), contract1.GetHashCode()); - } - - [TestMethod] - public void TestSerialize() - { - byte[] privateKey = new byte[32]; - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) - { - rng.GetBytes(privateKey); - } - KeyPair key = new KeyPair(privateKey); - VerificationContract contract1 = new VerificationContract - { - Script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), - ParameterList = new[] { ContractParameterType.Signature } - }; - byte[] byteArray = contract1.ToArray(); - byte[] script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey); - byte[] result = new byte[43]; - result[0] = 0x01; - result[1] = (byte)ContractParameterType.Signature; - result[2] = 0x28; - Array.Copy(script, 0, result, 3, 40); - CollectionAssert.AreEqual(result, byteArray); - } - - [TestMethod] - public void TestGetSize() - { - byte[] privateKey = new byte[32]; - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) - { - rng.GetBytes(privateKey); - } - KeyPair key = new KeyPair(privateKey); - VerificationContract contract1 = new VerificationContract - { - Script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), - ParameterList = new[] { ContractParameterType.Signature } - }; - Assert.AreEqual(43, contract1.Size); - } - } -} diff --git a/tests/neo.UnitTests/neo.UnitTests.csproj b/tests/neo.UnitTests/neo.UnitTests.csproj deleted file mode 100644 index f31f2b45a7..0000000000 --- a/tests/neo.UnitTests/neo.UnitTests.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - Exe - net6.0 - Neo.UnitTests - Neo.UnitTests - true - - - - - - - - - - - - - - - - - - - -