Compare commits

..

1 commit

Author SHA1 Message Date
Evgeniy Stratonikov
e4ec4052d8 m
Signed-off-by: Evgeniy Stratonikov <evgeniy@nspcc.ru>
2022-02-26 16:01:44 +03:00
952 changed files with 33966 additions and 87620 deletions

121
.circleci/config.yml Normal file
View file

@ -0,0 +1,121 @@
version: 2.1
executors:
go1_15:
docker:
- image: circleci/golang:1.15
environment:
GO111MODULE: "on"
go1_16:
docker:
- image: circleci/golang:1.16
environment:
GO111MODULE: "on"
go1_17:
docker:
- image: circleci/golang:1.17
commands:
gomod:
steps:
- restore_cache:
keys: [deps-]
- run:
name: Download go module dependencies
command: go mod download
- save_cache:
key: deps-{{ checksum "go.sum" }}-{{ checksum "go.sum" }}
paths: [/go/pkg/mod]
jobs:
lint:
working_directory: /go/src/github.com/nspcc-dev/neo-go
executor: go1_17
steps:
- checkout
- gomod
- run:
name: go-lint
command: |
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.42.1
make lint
test_1_15:
working_directory: /go/src/github.com/nspcc-dev/neo-go
executor: go1_15
steps:
- checkout
- run: git submodule sync
- run: git submodule update --init
- gomod
- run: go test -v -race ./...
test_1_16:
working_directory: /go/src/github.com/nspcc-dev/neo-go
executor: go1_16
steps:
- checkout
- run: git submodule sync
- run: git submodule update --init
- gomod
- run: go test -v -race ./...
build_cli:
working_directory: /go/src/github.com/nspcc-dev/neo-go
executor: go1_17
steps:
- checkout
- gomod
- run: make build
- store_artifacts:
path: bin
destination: /
build_image:
working_directory: /go/src/github.com/nspcc-dev/neo-go
executor: go1_17
docker:
- image: golang:1-alpine
steps:
- run: apk update && apk add git make curl tar
- checkout
- gomod
- setup_remote_docker:
version: 20.10.6
- run:
name: Install Docker client
command: |
set -x
VER="20.10.6"
curl -L -o /tmp/docker-$VER.tgz https://download.docker.com/linux/static/stable/x86_64/docker-$VER.tgz
tar -xz -C /tmp -f /tmp/docker-$VER.tgz
mv /tmp/docker/* /usr/bin
- run: make image
workflows:
version: 2
workflow:
jobs:
- lint:
filters:
tags:
only: v/[0-9]+\.[0-9]+\.[0-9]+/
- test_1_15:
filters:
tags:
only: v/[0-9]+\.[0-9]+\.[0-9]+/
- test_1_16:
filters:
tags:
only: v/[0-9]+\.[0-9]+\.[0-9]+/
- build_cli:
filters:
tags:
only: v/[0-9]+\.[0-9]+\.[0-9]+/
- build_image:
requires:
- build_cli
filters:
tags:
only: v/[0-9]+\.[0-9]+\.[0-9]+/

View file

@ -1,5 +1,5 @@
{ {
"version": "1.0", "version": "3.0",
"accounts": [ "accounts": [
{ {
"address": "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn", "address": "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn",
@ -16,7 +16,7 @@
"deployed": false "deployed": false
}, },
"lock": false, "lock": false,
"isDefault": false "isdefault": false
}, },
{ {
"address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq", "address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq",
@ -41,7 +41,7 @@
"deployed": false "deployed": false
}, },
"lock": false, "lock": false,
"isDefault": false "isdefault": false
} }
], ],
"scrypt": { "scrypt": {
@ -52,4 +52,4 @@
"extra": { "extra": {
"Tokens": null "Tokens": null
} }
} }

View file

@ -1,5 +1,5 @@
{ {
"version": "1.0", "version": "3.0",
"accounts": [ "accounts": [
{ {
"address": "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn", "address": "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn",
@ -16,7 +16,7 @@
"deployed": false "deployed": false
}, },
"lock": false, "lock": false,
"isDefault": false "isdefault": false
}, },
{ {
"address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq", "address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq",
@ -41,7 +41,7 @@
"deployed": false "deployed": false
}, },
"lock": false, "lock": false,
"isDefault": false "isdefault": false
}, },
{ {
"address": "NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP", "address": "NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP",
@ -58,7 +58,7 @@
"deployed": false "deployed": false
}, },
"lock": false, "lock": false,
"isDefault": false "isdefault": false
} }
], ],
"scrypt": { "scrypt": {
@ -69,4 +69,4 @@
"extra": { "extra": {
"Tokens": null "Tokens": null
} }
} }

View file

@ -1,5 +1,5 @@
{ {
"version": "1.0", "version": "3.0",
"accounts": [ "accounts": [
{ {
"address": "NMUedC8TSV2rE17wGguSvPk9XcmHSaT275", "address": "NMUedC8TSV2rE17wGguSvPk9XcmHSaT275",
@ -16,7 +16,7 @@
"deployed": false "deployed": false
}, },
"lock": false, "lock": false,
"isDefault": false "isdefault": false
}, },
{ {
"address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq", "address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq",
@ -41,7 +41,7 @@
"deployed": false "deployed": false
}, },
"lock": false, "lock": false,
"isDefault": false "isdefault": false
} }
], ],
"scrypt": { "scrypt": {
@ -52,4 +52,4 @@
"extra": { "extra": {
"Tokens": null "Tokens": null
} }
} }

View file

@ -1,5 +1,5 @@
{ {
"version": "1.0", "version": "3.0",
"accounts": [ "accounts": [
{ {
"address": "NdypBhqkz2CMMnwxBgvoC9X2XjKF5axgKo", "address": "NdypBhqkz2CMMnwxBgvoC9X2XjKF5axgKo",
@ -16,7 +16,7 @@
"deployed": false "deployed": false
}, },
"lock": false, "lock": false,
"isDefault": false "isdefault": false
}, },
{ {
"address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq", "address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq",
@ -41,7 +41,7 @@
"deployed": false "deployed": false
}, },
"lock": false, "lock": false,
"isDefault": false "isdefault": false
} }
], ],
"scrypt": { "scrypt": {
@ -52,4 +52,4 @@
"extra": { "extra": {
"Tokens": null "Tokens": null
} }
} }

View file

@ -1,5 +1,5 @@
{ {
"version": "1.0", "version": "3.0",
"accounts": [ "accounts": [
{ {
"address": "NPrB7BmTMYxf9UVroJp4RQExM9tqKmsHTz", "address": "NPrB7BmTMYxf9UVroJp4RQExM9tqKmsHTz",
@ -16,7 +16,7 @@
"deployed": false "deployed": false
}, },
"lock": false, "lock": false,
"isDefault": false "isdefault": false
}, },
{ {
"address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq", "address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq",
@ -41,7 +41,7 @@
"deployed": false "deployed": false
}, },
"lock": false, "lock": false,
"isDefault": false "isdefault": false
} }
], ],
"scrypt": { "scrypt": {
@ -52,4 +52,4 @@
"extra": { "extra": {
"Tokens": null "Tokens": null
} }
} }

1
.github/CODEOWNERS vendored
View file

@ -1 +0,0 @@
* @AnnaShaleva @roman-khimov

BIN
.github/logo_dark.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

View file

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View file

@ -8,145 +8,110 @@ on:
paths-ignore: paths-ignore:
- 'scripts/**' - 'scripts/**'
- '**/*.md' - '**/*.md'
push:
# Build for the master branch.
branches:
- master
release:
# Publish released commit as Docker `latest` and `git_revision` images.
types:
- published
workflow_dispatch: workflow_dispatch:
inputs: inputs:
ref: ref:
description: 'Ref to build CLI for Ubuntu and Windows Server Core [default: latest master; examples: v0.92.0, 0a4ff9d3e4a9ab432fd5812eb18c98e03b5a7432]' description: 'Ref to build CLI for Ubuntu and Windows Server Core [default: latest master; examples: v0.92.0, 0a4ff9d3e4a9ab432fd5812eb18c98e03b5a7432]'
required: false required: false
default: '' default: ''
push_image:
description: 'Push images to DockerHub [default: false; examples: true, false]' env:
required: false GO111MODULE: "on"
default: 'false'
use_latest_tag:
description: 'Use `latest` tag while pushing images to DockerHub (applied to Ubuntu image only) [default: false; examples: true, false]'
required: false
default: 'false'
jobs: jobs:
build_cli: build_cli_ubuntu:
name: Build CLI name: Build CLI (Ubuntu)
runs-on: ${{matrix.os.name}} runs-on: ubuntu-20.04
strategy:
matrix:
os: [{ name: ubuntu-22.04, bin-name: linux }, { name: windows-2022, bin-name: windows }, { name: macos-12, bin-name: darwin }]
arch: [amd64, arm64]
exclude:
- os: { name: windows-2022, bin-name: windows }
arch: 'arm64'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
with: with:
ref: ${{ github.event.inputs.ref }} ref: ${{ github.event.inputs.ref }}
# Allows to fetch all history for all branches and tags. Need this for proper versioning. # Allows to fetch all history for all branches and tags. Need this for proper versioning.
fetch-depth: 0 fetch-depth: 0
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v2
with: with:
go-version: '1.22' go-version: 1.17
- name: Restore Go modules from cache
uses: actions/cache@v2
with:
path: /home/runner/go/pkg/mod
key: deps-${{ hashFiles('go.sum') }}
- name: Update Go modules
run: go mod download -json
- name: Build CLI - name: Build CLI
run: make build run: make build
env:
GOARCH: ${{ matrix.arch }}
- name: Rename CLI binary
run: mv ./bin/neo-go* ./bin/neo-go-${{ matrix.os.bin-name }}-${{ matrix.arch }}${{ (matrix.os.bin-name == 'windows' && '.exe') || '' }}
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v2
with: with:
name: neo-go-${{ matrix.os.bin-name }}-${{ matrix.arch }} name: neo-go-binaries-ubuntu
path: ./bin/neo-go* path: ./bin/neo-go
if-no-files-found: error if-no-files-found: error
- name: Attach binary to the release as an asset build_cli_wsc:
if: ${{ github.event_name == 'release' }} name: Build CLI (Windows Server Core)
run: gh release upload ${{ github.event.release.tag_name }} ./bin/neo-go-${{ matrix.os.bin-name }}-${{ matrix.arch }}${{ (matrix.os.bin-name == 'windows' && '.exe') || '' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build_image:
needs: build_cli
name: Build and push docker image
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.ref }}
fetch-depth: 0
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
if: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_image == 'true') }}
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Set vars
id: setvars
run: make gh-docker-vars >> $GITHUB_OUTPUT
- name: Set latest tag
id: setlatest
if: ${{ (github.event_name == 'release' && github.event.release.target_commitish == 'master') || (github.event_name == 'workflow_dispatch' && github.event.inputs.use_latest_tag == 'true') }}
run: echo "latest=,${{ steps.setvars.outputs.repo }}:latest" >> $GITHUB_OUTPUT
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_image == 'true') }}
platforms: linux/amd64,linux/arm64
build-args: |
REPO=github.com/${{ github.repository }}
VERSION=${{ steps.setvars.outputs.version }}
tags: ${{ steps.setvars.outputs.repo }}:${{ steps.setvars.outputs.version }}${{ steps.setvars.outputs.suffix }}${{ steps.setlatest.outputs.latest }}
build_image_wsc:
needs: build_cli
name: Build and push docker image (Windows Server Core)
runs-on: windows-2022 runs-on: windows-2022
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
with: with:
ref: ${{ github.event.inputs.ref }} ref: ${{ github.event.inputs.ref }}
fetch-depth: 0 fetch-depth: 0
# For proper `deps` make target execution.
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v2
with: with:
go-version: '1.22' go-version: 1.17
- name: Login to DockerHub - name: Restore Go modules from cache
if: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_image == 'true') }} uses: actions/cache@v2
uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} path: /home/runner/go/pkg/mod
password: ${{ secrets.DOCKERHUB_PASSWORD }} key: deps-${{ hashFiles('go.sum') }}
- name: Update Go modules
run: go mod download -json
- name: Build CLI
run: make build
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: neo-go-binaries-wsc
path: ./bin/neo-go.exe
if-no-files-found: error
build_image_ubuntu:
needs: build_cli_ubuntu
name: Build Docker image (Ubuntu)
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.inputs.ref }}
fetch-depth: 0
- name: Build Docker image - name: Build Docker image
run: make image run: make image
- name: Push image to registry build_image_wsc:
if: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_image == 'true') }} needs: build_cli_wsc
run: make image-push name: Build Docker image (Windows Server Core)
runs-on: windows-2022
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.inputs.ref }}
fetch-depth: 0
- name: Build Docker image
run: make image-wsc

67
.github/workflows/codeql-analysis.yml vendored Normal file
View file

@ -0,0 +1,67 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master, master-2.x ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '35 8 * * 1'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View file

@ -1,11 +0,0 @@
name: Contribution guidelines
on:
pull_request:
branches:
- master
jobs:
commits_check_job:
name: DCO check
uses: nspcc-dev/.github/.github/workflows/dco.yml@master

View file

@ -0,0 +1,155 @@
name: Push images to DockerHub
# Controls when the action will run.
on:
push:
# Publish `master` as Docker `latest` and `git_revision` images.
branches:
- master
release:
# Publish released commit as Docker `latest` and `git_revision` images.
types:
- published
# Allows to run this workflow manually from the Actions tab.
workflow_dispatch:
inputs:
ref:
description: 'Ref to build Docker images for Ubuntu and Windows Server Core [default: latest master; examples: v0.92.0, 0a4ff9d3e4a9ab432fd5812eb18c98e03b5a7432]'
required: false
default: ''
push_image:
description: 'Push images to DockerHub [default: false; examples: true, false]'
required: false
default: 'false'
# Environment variables.
env:
GO111MODULE: "on"
# A workflow run.
jobs:
tests_ubuntu:
name: Run Ubuntu-based tests before publishing
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.inputs.ref }}
# Allows to fetch all history for all branches and tags. Need this for proper versioning.
fetch-depth: 0
- name: Sync VM submodule
run: |
git submodule sync
git submodule update --init
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.17
- name: Restore go modules from cache
uses: actions/cache@v2
with:
path: /home/runner/go/pkg/mod
key: deps-${{ hashFiles('go.sum') }}
- name: Update Go modules
run: go mod download -json
- name: Run tests
run: make test
publish_ubuntu:
# Ensure test job passes before pushing image.
needs: tests_ubuntu
name: Publish Ubuntu-based image to DockerHub
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.inputs.ref }}
# Allows to fetch all history for all branches and tags. Need this for proper versioning.
fetch-depth: 0
- name: Build image
run: make image
- name: Build image with 'latest' tag
if: ${{ github.event_name == 'release' && github.event.release.target_commitish == 'master' }}
run: make image-latest
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Push image to registry
if: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_image == 'true') }}
run: make image-push
- name: Push image with 'latest' tag to registry
if: ${{ github.event_name == 'release' && github.event.release.target_commitish == 'master' }}
run: make image-push-latest
tests_wsc:
name: Run WindowsServerCore-based tests before publishing
runs-on: windows-2022
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.inputs.ref }}
# Allows to fetch all history for all branches and tags. Need this for proper versioning.
fetch-depth: 0
- name: Sync VM submodule
run: |
git submodule sync
git submodule update --init
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.17
- name: Restore go modules from cache
uses: actions/cache@v2
with:
path: /home/runner/go/pkg/mod
key: deps-${{ hashFiles('go.sum') }}
- name: Update Go modules
run: go mod download -json
- name: Run tests
run: make test
publish_wsc:
# Ensure test job passes before pushing image.
needs: tests_wsc
name: Publish WindowsServerCore-based image to DockerHub
runs-on: windows-2022
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.inputs.ref }}
# Allows to fetch all history for all branches and tags. Need this for proper versioning.
fetch-depth: 0
- name: Show docker images
run: docker images
- name: Build image
run: make image-wsc
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Push image to registry
if: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_image == 'true') }}
run: make image-wsc-push

120
.github/workflows/run_tests.yml vendored Normal file
View file

@ -0,0 +1,120 @@
name: Tests
on:
pull_request:
branches:
- master
types: [opened, synchronize]
paths-ignore:
- 'scripts/**'
- '**/*.md'
workflow_dispatch:
env:
GO111MODULE: "on"
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: latest
gomodcheck:
name: Check internal dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Check dependencies
run: |
find -name go.mod | xargs -n1 grep -o 'pkg/interop v\S*' | uniq | wc -l | xargs -I{} -n1 [ 1 -eq {} ] || { echo "Different versions for dependencies in go.mod" && exit 1; }
test_cover:
name: Coverage
runs-on: ubuntu-20.04
env:
CGO_ENABLED: 0
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Sync VM submodule
run: |
git submodule sync
git submodule update --init
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.17
- name: Restore Go modules from cache
uses: actions/cache@v2
with:
path: /home/runner/go/pkg/mod
key: deps-${{ hashFiles('go.sum') }}
- name: Update Go modules
run: go mod download -json
- name: Write coverage profile
run: go test -v ./... -coverprofile=./coverage.txt -covermode=atomic -coverpkg=./pkg...,./cli/...
- name: Upload coverage results to Codecov
uses: codecov/codecov-action@v2
with:
fail_ci_if_error: true # if something is wrong on uploading codecov results, then this job will fail
files: ./coverage.txt
verbose: true
tests:
name: Run tests
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, windows-2022]
go_versions: [ '1.15', '1.16', '1.17' ]
exclude:
- os: windows-2022
go_versions: '1.15'
- os: windows-2022
go_versions: '1.16'
- os: ubuntu-20.04
go_versions: '1.17'
fail-fast: false
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: '${{ matrix.go_versions }}'
- name: Restore Go modules from cache
uses: actions/cache@v2
with:
path: /home/runner/go/pkg/mod
key: deps-${{ hashFiles('go.sum') }}
- name: Update Go modules
run: go mod download -json
- name: Sync VM submodule
run: |
git submodule sync
git submodule update --init
- name: Run tests
run: go test -v -race ./...

View file

@ -1,190 +0,0 @@
name: Tests
on:
push:
branches: [ master ]
pull_request:
branches:
- master
types: [opened, synchronize]
paths-ignore:
- 'scripts/*.sh'
- '**/*.md'
workflow_dispatch:
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- name: golangci-lint
uses: golangci/golangci-lint-action@v4
with:
version: latest
skip-pkg-cache: true # golangci-lint can't work with this cache enabled, ref. https://github.com/golangci/golangci-lint-action/issues/135.
gomodcheck:
name: Check internal dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check dependencies
run: |
./scripts/check_deps.sh
- name: Check go.mod is tidy
run: |
go mod tidy
if [[ $(git diff --name-only go.* | grep '' -c) != 0 ]]; then
echo "go mod tidy should be executed before the merge, following packages are unused or out of date:";
git diff go.*;
exit 1;
fi
codegencheck:
name: Check code generated with 'go generate' is up-to-date
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- name: Install stringer
run: go install golang.org/x/tools/cmd/stringer@latest
- name: Run go generate
run: go generate ./...
- name: Check that autogenerated code is up-to-date
run: |
if [[ $(git diff --name-only | grep '' -c) != 0 ]]; then
echo "Fresh version of autogenerated code should be committed for the following files:";
git diff --name-only;
exit 1;
fi
codeql:
name: CodeQL
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
test_cover:
name: Coverage
runs-on: ubuntu-22.04
env:
CGO_ENABLED: 0
GOEXPERIMENT: nocoverageredesign
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: 'true'
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
cache: true
- name: Write coverage profile
run: go test -timeout 15m -v ./... -coverprofile=./coverage.txt -covermode=atomic -coverpkg=./pkg...,./cli/...
- name: Upload coverage results to Codecov
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: true # if something is wrong on uploading codecov results, then this job will fail
files: ./coverage.txt
slug: nspcc-dev/neo-go
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true
tests:
name: Run tests
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-22.04, windows-2022, macos-12, macos-14]
go_versions: [ '1.20', '1.21', '1.22' ]
exclude:
# Only latest Go version for Windows and MacOS.
- os: windows-2022
go_versions: '1.20'
- os: windows-2022
go_versions: '1.21'
- os: macos-12
go_versions: '1.20'
- os: macos-12
go_versions: '1.21'
- os: macos-14
go_versions: '1.20'
- os: macos-14
go_versions: '1.21'
# Exclude latest Go version for Ubuntu as Coverage uses it.
- os: ubuntu-22.04
go_versions: '1.22'
fail-fast: false
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: 'true'
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '${{ matrix.go_versions }}'
- name: Run tests
run: go test -timeout 15m -v -race ./...

19
.gitignore vendored
View file

@ -7,6 +7,9 @@
# Test binary, build with `go test -c` # Test binary, build with `go test -c`
*.test *.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Added by CoZ developers # Added by CoZ developers
vendor/ vendor/
bin/ bin/
@ -39,19 +42,3 @@ coverage.html
# Compiler output # Compiler output
examples/*/*.nef examples/*/*.nef
examples/*/*.json examples/*/*.json
# Fuzzing testdata.
testdata/
!cli/testdata
!internal/basicchain/testdata
!pkg/compiler/testdata
!pkg/config/testdata
!pkg/consensus/testdata
!pkg/services/rpcsrv/testdata
!pkg/services/notary/testdata
!pkg/services/oracle/testdata
!pkg/smartcontract/testdata
!cli/smartcontract/testdata
pkg/vm/testdata/fuzz
!pkg/vm/testdata
!pkg/wallet/testdata

View file

@ -32,32 +32,20 @@ linters:
- revive - revive
# some default golangci-lint linters # some default golangci-lint linters
- deadcode
- errcheck - errcheck
- gosimple - gosimple
- godot - godot
- ineffassign - ineffassign
- staticcheck - staticcheck
- structcheck
- typecheck - typecheck
- unused - unused
- varcheck
# extra linters # extra linters
# - exhaustive # - exhaustive
# - goconst
# - goerr113
# - gomnd
# - nonamedreturns
# - unparam
- bidichk
- bodyclose
- contextcheck
- decorder
- durationcheck
- errorlint
- exportloopref
- gofmt - gofmt
- misspell
- predeclared
- reassign
- whitespace - whitespace
- goimports - goimports
disable-all: true disable-all: true
@ -69,7 +57,3 @@ issues:
- EXC0003 # test/Test ... consider calling this - EXC0003 # test/Test ... consider calling this
- EXC0004 # govet - EXC0004 # govet
- EXC0005 # C-style breaks - EXC0005 # C-style breaks
exclude-rules:
- linters:
- revive
text: "unused-parameter"

16
.travis.yml Normal file
View file

@ -0,0 +1,16 @@
language: go
go:
- 1.15.x
env:
- GO111MODULE=on
install:
- go get -v golang.org/x/lint/golint
- go mod tidy -v
script:
- golint -set_exit_status ./...
- go test -v -race -coverprofile=coverage.txt -covermode=atomic -coverpkg=./pkg/...,./cli/... ./...
after_success:
- bash <(curl -s https://codecov.io/bash)
matrix:
allow_failures:
- go: tip

File diff suppressed because it is too large Load diff

View file

@ -5,9 +5,9 @@ follow the guidelines:
1. Check open [issues](https://github.com/nspcc-dev/neo-go/issues) and 1. Check open [issues](https://github.com/nspcc-dev/neo-go/issues) and
[pull requests](https://github.com/nspcc-dev/neo-go/pulls) for existing discussions. [pull requests](https://github.com/nspcc-dev/neo-go/pulls) for existing discussions.
1. Open an issue first to discuss a new feature or enhancement. 1. Open an issue first, to discuss a new feature or enhancement.
1. Write tests and make sure the test suite passes locally and on CI. 1. Write tests, and make sure the test suite passes locally and on CI.
1. When optimizing something, write benchmarks and attach the results: 1. When optimizing something, write benchmarks and attach results:
``` ```
go test -run - -bench BenchmarkYourFeature -count=10 ./... >old // on master go test -run - -bench BenchmarkYourFeature -count=10 ./... >old // on master
go test -run - -bench BenchmarkYourFeature -count=10 ./... >new // on your branch go test -run - -bench BenchmarkYourFeature -count=10 ./... >new // on your branch
@ -15,11 +15,11 @@ follow the guidelines:
``` ```
`benchstat` is described here https://godocs.io/golang.org/x/perf/cmd/benchstat. `benchstat` is described here https://godocs.io/golang.org/x/perf/cmd/benchstat.
1. Open a pull request and reference the relevant issue(s). 1. Open a pull request, and reference the relevant issue(s).
1. Make sure your commits are logically separated and have good comments 1. Make sure your commits are logically separated and have good comments
explaining the details of your change. Add a package/file prefix to your explaining the details of your change. Add a package/file prefix to your
commit if that's applicable (like 'vm: fix ADD miscalculation on full commit if that's applicable (like 'vm: fix ADD miscalculation on full
moon'). moon').
1. After receiving a feedback, amend your commits or add new ones as 1. After receiving feedback, amend your commits or add new ones as
appropriate. appropriate.
1. **Have fun!** 1. **Have fun!**

View file

@ -1,9 +1,5 @@
# Builder image # Builder image
# Keep go version in sync with Build GA job. FROM golang:1-alpine as builder
FROM golang:1.22-alpine as builder
# Display go version for information purposes.
RUN go version
RUN set -x \ RUN set -x \
&& apk add --no-cache git make \ && apk add --no-cache git make \
@ -16,12 +12,12 @@ WORKDIR /neo-go
ARG REPO=repository ARG REPO=repository
ARG VERSION=dev ARG VERSION=dev
RUN VERSION=$VERSION REPO=$REPO make build RUN make build
# Executable image # Executable image
FROM alpine FROM alpine
ARG VERSION=dev ARG VERSION
LABEL version=$VERSION LABEL version=$VERSION
WORKDIR / WORKDIR /

View file

@ -1,6 +1,25 @@
# Builder image # Builder image
# Keep go version in sync with Build GA job. FROM mcr.microsoft.com/windows/servercore:ltsc2022 as builder
FROM golang:1.22.0-windowsservercore-ltsc2022 as builder
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop';", "$ProgressPreference = 'SilentlyContinue';"]
ENV GIT_VERSION=2.23.0
ENV GIT_TAG=v2.23.0.windows.1
ENV GIT_DOWNLOAD_URL=https://github.com/git-for-windows/git/releases/download/v2.23.0.windows.1/MinGit-2.23.0-64-bit.zip
ENV GIT_DOWNLOAD_SHA256=8f65208f92c0b4c3ae4c0cf02d4b5f6791d539cd1a07b2df62b7116467724735
RUN Write-Host ('Downloading {0} ...' -f $env:GIT_DOWNLOAD_URL); [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Invoke-WebRequest -Uri $env:GIT_DOWNLOAD_URL -OutFile 'git.zip'; Write-Host ('Verifying sha256 ({0}) ...' -f $env:GIT_DOWNLOAD_SHA256); if ((Get-FileHash git.zip -Algorithm sha256).Hash -ne $env:GIT_DOWNLOAD_SHA256) { Write-Host 'FAILED!'; exit 1; }; Write-Host 'Expanding ...'; Expand-Archive -Path git.zip -DestinationPath C:\git\.; Write-Host 'Removing ...'; Remove-Item git.zip -Force; Write-Host 'Updating PATH ...'; $env:PATH = 'C:\git\cmd;C:\git\mingw64\bin;C:\git\usr\bin;' + $env:PATH; [Environment]::SetEnvironmentVariable('PATH', $env:PATH, [EnvironmentVariableTarget]::Machine); Write-Host 'Verifying install ("git version") ...'; git version; Write-Host 'Complete.';
ENV GOPATH=C:\\go
RUN $newPath = ('{0}\bin;C:\Program Files\Go\bin;{1}' -f $env:GOPATH, $env:PATH); Write-Host ('Updating PATH: {0}' -f $newPath); [Environment]::SetEnvironmentVariable('PATH', $newPath, [EnvironmentVariableTarget]::Machine);
ENV GOLANG_VERSION=1.17.6
RUN $url = 'https://dl.google.com/go/go1.17.6.windows-amd64.zip'; Write-Host ('Downloading {0} ...' -f $url); [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Invoke-WebRequest -Uri $url -OutFile 'go.zip'; $sha256 = '5bf8f87aec7edfc08e6bc845f1c30dba6de32b863f89ae46553ff4bbcc1d4954'; Write-Host ('Verifying sha256 ({0}) ...' -f $sha256); if ((Get-FileHash go.zip -Algorithm sha256).Hash -ne $sha256) { Write-Host 'FAILED!'; exit 1; }; Write-Host 'Expanding ...'; Expand-Archive go.zip -DestinationPath C:\; Write-Host 'Moving ...'; Move-Item -Path C:\go -Destination 'C:\Program Files\Go'; Write-Host 'Removing ...'; Remove-Item go.zip -Force; Write-Host 'Verifying install ("go version") ...'; go version; Write-Host 'Complete.';
COPY . /neo-go COPY . /neo-go

View file

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2018-2023 NeoSPCC (@nspcc-dev), Anthony De Meulemeester (@anthdm), City of Zion community (@CityOfZion) Copyright (c) 2018 Anthony De Meulemeester (@anthdm) & City of Zion community (@CityOfZion)
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.

View file

@ -1,22 +1,18 @@
BRANCH = "master" BRANCH = "master"
REPONAME = "neo-go" REPONAME = "neo-go"
NETMODE ?= "privnet" NETMODE ?= "privnet"
BINARY=neo-go BINARY=./bin/neo-go$(shell go env GOEXE)
BINARY_PATH=./bin/$(BINARY)$(shell go env GOEXE)
GO_VERSION ?= 1.20
DESTDIR = "" DESTDIR = ""
SYSCONFIGDIR = "/etc" SYSCONFIGDIR = "/etc"
BINDIR = "/usr/bin" BINDIR = "/usr/bin"
SYSTEMDUNIT_DIR = "/lib/systemd/system" SYSTEMDUNIT_DIR = "/lib/systemd/system"
UNITWORKDIR = "/var/lib/neo-go" UNITWORKDIR = "/var/lib/neo-go"
IMAGE_SUFFIX="$(shell if [ "$(OS)" = Windows_NT ]; then echo "_WindowsServerCore"; fi)" DC_FILE=.docker/docker-compose.yml
D_FILE ?= "$(shell if [ "$(OS)" = Windows_NT ]; then echo "Dockerfile.wsc"; else echo "Dockerfile"; fi)"
DC_FILE ?= ".docker/docker-compose.yml" # Single docker-compose for Ubuntu/WSC, should be kept in sync with ENV_IMAGE_TAG.
ENV_IMAGE_TAG="env_neo_go_image"
GOOS ?= $(shell go env GOOS)
REPO ?= "$(shell go list -m)" REPO ?= "$(shell go list -m)"
VERSION ?= "$(shell git describe --tags --match "v*" --abbrev=8 2>/dev/null | sed -r 's,^v([0-9]+\.[0-9]+)\.([0-9]+)(-.*)?$$,\1 \2 \3,' | while read mm patch suffix; do if [ -z "$$suffix" ]; then echo $$mm.$$patch; else patch=`expr $$patch + 1`; echo $$mm.$${patch}-pre$$suffix; fi; done)" VERSION ?= "$(shell git describe --tags 2>/dev/null | sed 's/^v//')$(shell if [ "$(GOOS)" = "windows" ]; then echo "_unsupported"; fi)"
MODVERSION ?= "$(shell cat go.mod | cat go.mod | sed -r -n -e 's|.*pkg/interop (.*)|\1|p')" MODVERSION ?= "$(shell cat go.mod | cat go.mod | sed -r -n -e 's|.*pkg/interop (.*)|\1|p')"
BUILD_FLAGS = "-X '$(REPO)/pkg/config.Version=$(VERSION)' -X '$(REPO)/cli/smartcontract.ModVersion=$(MODVERSION)'" BUILD_FLAGS = "-X '$(REPO)/pkg/config.Version=$(VERSION)' -X '$(REPO)/cli/smartcontract.ModVersion=$(MODVERSION)'"
@ -24,26 +20,15 @@ IMAGE_REPO=nspccdev/neo-go
# All of the targets are phony here because we don't really use make dependency # All of the targets are phony here because we don't really use make dependency
# tracking for files # tracking for files
.PHONY: build $(BINARY) deps image docker/$(BINARY) image-latest image-push image-push-latest clean-cluster \ .PHONY: build deps image image-wsc image-latest image-push image-wsc-push image-push-latest check-version clean-cluster push-tag \
test vet lint fmt cover version gh-docker-vars test vet lint fmt cover
build: deps build: deps
@echo "=> Building binary" @echo "=> Building binary"
@set -x \ @set -x \
&& export GOGC=off \ && export GOGC=off \
&& export CGO_ENABLED=0 \ && export CGO_ENABLED=0 \
&& go build -trimpath -v -ldflags $(BUILD_FLAGS) -o ${BINARY_PATH} ./cli/main.go && go build -trimpath -v -ldflags $(BUILD_FLAGS) -o ${BINARY} ./cli/main.go
$(BINARY): build
docker/$(BINARY):
@echo "=> Building binary using clean Docker environment"
@docker run --rm -t \
-v `pwd`:/src \
-w /src \
-u "$$(id -u):$$(id -g)" \
--env HOME=/src \
golang:$(GO_VERSION) make $(BINARY)
neo-go.service: neo-go.service.template neo-go.service: neo-go.service.template
@sed -r -e 's_BINDIR_$(BINDIR)_' -e 's_UNITWORKDIR_$(UNITWORKDIR)_' -e 's_SYSCONFIGDIR_$(SYSCONFIGDIR)_' $< >$@ @sed -r -e 's_BINDIR_$(BINDIR)_' -e 's_UNITWORKDIR_$(UNITWORKDIR)_' -e 's_SYSCONFIGDIR_$(SYSCONFIGDIR)_' $< >$@
@ -56,7 +41,7 @@ install: build neo-go.service
&& cp ./config/protocol.mainnet.yml $(DESTDIR)$(SYSCONFIGDIR)/neo-go \ && cp ./config/protocol.mainnet.yml $(DESTDIR)$(SYSCONFIGDIR)/neo-go \
&& cp ./config/protocol.privnet.yml $(DESTDIR)$(SYSCONFIGDIR)/neo-go \ && cp ./config/protocol.privnet.yml $(DESTDIR)$(SYSCONFIGDIR)/neo-go \
&& cp ./config/protocol.testnet.yml $(DESTDIR)$(SYSCONFIGDIR)/neo-go \ && cp ./config/protocol.testnet.yml $(DESTDIR)$(SYSCONFIGDIR)/neo-go \
&& install -m 0755 -t $(BINDIR) $(BINARY_PATH) \ && install -m 0755 -t $(BINDIR) $(BINARY) \
postinst: install postinst: install
@echo "=> Preparing directories and configs" @echo "=> Preparing directories and configs"
@ -66,40 +51,43 @@ postinst: install
&& systemctl enable neo-go.service && systemctl enable neo-go.service
image: deps image: deps
@echo "=> Building image" @echo "=> Building image for Ubuntu"
@echo " Dockerfile: $(D_FILE)" @docker build -t $(IMAGE_REPO):$(VERSION) --build-arg REPO=$(REPO) --build-arg VERSION=$(VERSION) .
@echo " Tag: $(IMAGE_REPO):$(VERSION)$(IMAGE_SUFFIX)"
@docker build -f $(D_FILE) -t $(IMAGE_REPO):$(VERSION)$(IMAGE_SUFFIX) --build-arg REPO=$(REPO) --build-arg VERSION=$(VERSION) . image-wsc: deps
@echo "=> Building image for Windows Server Core"
@docker build -f Dockerfile.wsc -t $(IMAGE_REPO):$(VERSION)_WindowsServerCore --build-arg REPO=$(REPO) --build-arg VERSION=$(VERSION) .
image-latest: deps image-latest: deps
@echo "=> Building image with 'latest' tag" @echo "=> Building image with 'latest' tag"
@echo " Dockerfile: Dockerfile" # Always use default Dockerfile for Ubuntu as `latest`.
@echo " Tag: $(IMAGE_REPO):latest"
@docker build -t $(IMAGE_REPO):latest --build-arg REPO=$(REPO) --build-arg VERSION=$(VERSION) . @docker build -t $(IMAGE_REPO):latest --build-arg REPO=$(REPO) --build-arg VERSION=$(VERSION) .
image-push: image-push:
@echo "=> Publish image" @echo "=> Publish image for Ubuntu"
@echo " Tag: $(IMAGE_REPO):$(VERSION)$(IMAGE_SUFFIX)" @docker push $(IMAGE_REPO):$(VERSION)
@docker push $(IMAGE_REPO):$(VERSION)$(IMAGE_SUFFIX)
image-wsc-push:
@echo "=> Publish image for Windows Server Core"
@docker push $(IMAGE_REPO):$(VERSION)_WindowsServerCore
image-push-latest: image-push-latest:
@echo "=> Publish image for Ubuntu with 'latest' tag" @echo "=> Publish image for Ubuntu with 'latest' tag"
@docker push $(IMAGE_REPO):latest @docker push $(IMAGE_REPO):latest
check-version:
git fetch && (! git rev-list ${VERSION})
deps: deps:
@CGO_ENABLED=0 \ @CGO_ENABLED=0 \
go mod download go mod download
@CGO_ENABLED=0 \ @CGO_ENABLED=0 \
go mod tidy -v go mod tidy -v
version: push-tag:
@echo $(VERSION) git checkout ${BRANCH}
git pull origin ${BRANCH}
gh-docker-vars: git tag ${VERSION}
@echo "file=$(D_FILE)" git push origin ${VERSION}
@echo "version=$(VERSION)"
@echo "repo=$(IMAGE_REPO)"
@echo "suffix=$(IMAGE_SUFFIX)"
test: test:
@go test ./... -cover @go test ./... -cover
@ -117,20 +105,16 @@ cover:
@go test -v -race ./... -coverprofile=coverage.txt -covermode=atomic -coverpkg=./pkg/...,./cli/... @go test -v -race ./... -coverprofile=coverage.txt -covermode=atomic -coverpkg=./pkg/...,./cli/...
@go tool cover -html=coverage.txt -o coverage.html @go tool cover -html=coverage.txt -o coverage.html
# --- Ubuntu/Windows environment --- # --- Environment ---
env_image: env_image:
@echo "=> Building env image" @echo "=> Building env image"
@echo " Dockerfile: $(D_FILE)"
@echo " Tag: $(ENV_IMAGE_TAG)"
@docker build \ @docker build \
-f $(D_FILE) \ -t env_neo_go_image \
-t $(ENV_IMAGE_TAG) \
--build-arg REPO=$(REPO) \ --build-arg REPO=$(REPO) \
--build-arg VERSION=$(VERSION) . --build-arg VERSION=$(VERSION) .
env_up: env_up:
@echo "=> Bootup environment" @echo "=> Bootup environment"
@echo " Docker-compose file: $(DC_FILE)"
@docker-compose -f $(DC_FILE) up -d node_one node_two node_three node_four @docker-compose -f $(DC_FILE) up -d node_one node_two node_three node_four
env_single: env_single:

View file

@ -1,9 +1,5 @@
<p align="center"> <p align="center">
<picture> <img src="./.github/neo_color_dark_gopher.png" width="300px" alt="logo">
<source media="(prefers-color-scheme: dark)" srcset="./.github/logo_dark.png">
<source media="(prefers-color-scheme: light)" srcset="./.github/logo_light.png">
<img src="./.github/logo_light.png" width="300px" alt="NeoGo logo">
</picture>
</p> </p>
<p align="center"> <p align="center">
<b>Go</b> Node and SDK for the <a href="https://neo.org">Neo</a> blockchain. <b>Go</b> Node and SDK for the <a href="https://neo.org">Neo</a> blockchain.
@ -12,7 +8,9 @@
<hr /> <hr />
[![codecov](https://codecov.io/gh/nspcc-dev/neo-go/branch/master/graph/badge.svg)](https://codecov.io/gh/nspcc-dev/neo-go) [![codecov](https://codecov.io/gh/nspcc-dev/neo-go/branch/master/graph/badge.svg)](https://codecov.io/gh/nspcc-dev/neo-go)
[![GithubWorkflows Tests](https://github.com/nspcc-dev/neo-go/actions/workflows/tests.yml/badge.svg)](https://github.com/nspcc-dev/neo-go/actions/workflows/tests.yml) [![CircleCI](https://circleci.com/gh/nspcc-dev/neo-go/tree/master.svg?style=shield)](https://circleci.com/gh/nspcc-dev/neo-go/tree/master)
[![GithubWorkflows Tests](https://github.com/nspcc-dev/neo-go/actions/workflows/run_tests.yml/badge.svg)](https://github.com/nspcc-dev/neo-go/actions/workflows/run_tests.yml)
[![GithubWorkflows CodeQL](https://github.com/nspcc-dev/neo-go/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/nspcc-dev/neo-go/actions/workflows/codeql-analysis.yml)
[![Report](https://goreportcard.com/badge/github.com/nspcc-dev/neo-go)](https://goreportcard.com/report/github.com/nspcc-dev/neo-go) [![Report](https://goreportcard.com/badge/github.com/nspcc-dev/neo-go)](https://goreportcard.com/report/github.com/nspcc-dev/neo-go)
[![GoDoc](https://godoc.org/github.com/nspcc-dev/neo-go?status.svg)](https://godoc.org/github.com/nspcc-dev/neo-go) [![GoDoc](https://godoc.org/github.com/nspcc-dev/neo-go?status.svg)](https://godoc.org/github.com/nspcc-dev/neo-go)
![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/nspcc-dev/neo-go?sort=semver) ![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/nspcc-dev/neo-go?sort=semver)
@ -20,21 +18,20 @@
# Overview # Overview
NeoGo is a complete platform for distributed application development built on This project aims to be a full port of the original C# [Neo project](https://github.com/neo-project).
top of and compatible with the [Neo project](https://github.com/neo-project). A complete toolkit for the NEO blockchain, including:
This includes, but not limited to (see documentation for more details):
- [Consensus node](docs/consensus.md) - [Consensus node](docs/consensus.md)
- [RPC node & client](docs/rpc.md) - [RPC node & client](docs/rpc.md)
- [CLI tool](docs/cli.md) - [CLI tool](docs/cli.md)
- [Smart contract compiler](docs/compiler.md) - [Smart contract compiler](docs/compiler.md)
- [Neo virtual machine](docs/vm.md) - [NEO virtual machine](docs/vm.md)
- [Smart contract examples](examples/README.md) - [Smart contract examples](examples/README.md)
- [Oracle service](docs/oracle.md) - [Oracle service](docs/oracle.md)
- [State validation service](docs/stateroots.md) - [State validation service](docs/stateroots.md)
The protocol implemented here is Neo N3-compatible, however you can also find This branch (**master**) is Neo N3-compatible. For the current
an implementation of the Neo Legacy protocol in the [**master-2.x** Legacy-compatible version please refer to the [**master-2.x**
branch](https://github.com/nspcc-dev/neo-go/tree/master-2.x) and releases branch](https://github.com/nspcc-dev/neo-go/tree/master-2.x) and releases
before 0.80.0 (**0.7X.Y** track). before 0.80.0 (**0.7X.Y** track).
@ -51,28 +48,27 @@ NeoGo, `:latest` points to the latest release) or build yourself.
### Building ### Building
Building NeoGo requires Go 1.20+ and `make`: To build NeoGo you need Go 1.15+ and `make`:
``` ```
make make build
``` ```
The resulting binary is `bin/neo-go`. Notice that using some random revision The resulting binary is `bin/neo-go`.
from the `master` branch is not recommended (it can have any number of
incompatibilities and bugs depending on the development stage), please use
tagged released versions.
#### Building on Windows #### Building on Windows
To build NeoGo on Windows platform we recommend you to install `make` from [MinGW To build NeoGo on Windows platform we recommend you to install `make` from [MinGW
package](https://osdn.net/projects/mingw/). Then, you can build NeoGo with: package](https://osdn.net/projects/mingw/). Then you can build NeoGo with:
``` ```
make make build
``` ```
The resulting binary is `bin/neo-go.exe`. The resulting binary is `bin/neo-go.exe`.
We also recommend you to switch the Windows Firewall off for further NeoGo node run.
## Running a node ## Running a node
A node needs to connect to some network, either local one (usually referred to A node needs to connect to some network, either local one (usually referred to
@ -81,13 +77,13 @@ is stored in a file and NeoGo allows you to store multiple files in one
directory (`./config` by default) and easily switch between them using network directory (`./config` by default) and easily switch between them using network
flags. flags.
To start Neo node on a private network, use: To start Neo node on private network use:
``` ```
./bin/neo-go node ./bin/neo-go node
``` ```
Or specify a different network with an appropriate flag like this: Or specify a different network with appropriate flag like this:
``` ```
./bin/neo-go node --mainnet ./bin/neo-go node --mainnet
@ -98,15 +94,12 @@ Available network flags:
- `--privnet, -p` - `--privnet, -p`
- `--testnet, -t` - `--testnet, -t`
To run a consensus/committee node, refer to [consensus To run a consensus/committee node refer to [consensus
documentation](docs/consensus.md). documentation](docs/consensus.md).
If you're running a node on Windows, please turn off or configure Windows
Firewall appropriately (allowing inbound connections to the P2P port).
### Docker ### Docker
By default, the `CMD` is set to run a node on `privnet`. So, to do this, simply run: By default the `CMD` is set to run a node on `privnet`, so to do this simply run:
```bash ```bash
docker run -d --name neo-go -p 20332:20332 -p 20331:20331 nspccdev/neo-go docker run -d --name neo-go -p 20332:20332 -p 20331:20331 nspccdev/neo-go
@ -118,7 +111,8 @@ protocol) and `20331` (JSON-RPC server).
### Importing mainnet/testnet dump files ### Importing mainnet/testnet dump files
If you want to jump-start your mainnet or testnet node with [chain archives If you want to jump-start your mainnet or testnet node with [chain archives
provided by NGD](https://sync.ngd.network/), follow these instructions: provided by NGD](https://sync.ngd.network/) follow these instructions (when
they'd be available for 3.0 networks):
``` ```
$ wget .../chain.acc.zip # chain dump file $ wget .../chain.acc.zip # chain dump file
$ unzip chain.acc.zip $ unzip chain.acc.zip
@ -126,7 +120,7 @@ $ ./bin/neo-go db restore -m -i chain.acc # for testnet use '-t' flag instead of
``` ```
The process differs from the C# node in that block importing is a separate The process differs from the C# node in that block importing is a separate
mode. After it ends, the node can be started normally. mode, after it ends the node can be started normally.
## Running a private network ## Running a private network
@ -134,39 +128,37 @@ Refer to [consensus node documentation](docs/consensus.md).
## Smart contract development ## Smart contract development
Please refer to [NeoGo smart contract development Please refer to [neo-go smart contract development
workshop](https://github.com/nspcc-dev/neo-go-sc-wrkshp) that shows some workshop](https://github.com/nspcc-dev/neo-go-sc-wrkshp) that shows some
simple contracts that can be compiled/deployed/run using NeoGo compiler, SDK simple contracts that can be compiled/deployed/run using neo-go compiler, SDK
and a private network. For details on how Go code is translated to Neo VM and private network. For details on how Go code is translated to Neo VM
bytecode and what you can and can not do in a smart contract, please refer to the bytecode and what you can and can not do in smart contract please refer to the
[compiler documentation](docs/compiler.md). [compiler documentation](docs/compiler.md).
Refer to [examples](examples/README.md) for more Neo smart contract examples Refer to [examples](examples/README.md) for more NEO smart contract examples
written in Go. written in Go.
## Wallets ## Wallets
NeoGo wallet is just a NeoGo differs substantially from C# implementation in its approach to
wallets. NeoGo wallet is just a
[NEP-6](https://github.com/neo-project/proposals/blob/68398d28b6932b8dd2b377d5d51bca7b0442f532/nep-6.mediawiki) [NEP-6](https://github.com/neo-project/proposals/blob/68398d28b6932b8dd2b377d5d51bca7b0442f532/nep-6.mediawiki)
file that is used by CLI commands to sign various things. CLI commands are not file that is used by CLI commands to sign various things. There is no database
a direct part of the node, but rather a part of the NeoGo binary, their behind it, the blockchain is the database and CLI commands use RPC to query
implementations use RPC to query data from the blockchain and perform any data from it. At the same time it's not required to open the wallet on RPC
required actions. It's not required to open a wallet on an RPC node (unless node to perform various actions (unless your node is providing some service
your node provides some service for the network like consensus or oracle nodes for the network like consensus or oracle nodes).
do).
## Monitoring # Developer notes
NeoGo provides [Prometheus](https://prometheus.io/docs/guides/go-application) and Nodes have such features as [Prometheus](https://prometheus.io/docs/guides/go-application) and
[Pprof](https://golang.org/pkg/net/http/pprof/) services that can be enabled [Pprof](https://golang.org/pkg/net/http/pprof/) in order to have additional information about them for debugging.
in the node in order to provide additional monitoring and debugging data.
Configuring any of the two services is easy, add the following section (`Pprof` How to configure Prometheus or Pprof:
instead of `Prometheus` if you need that) to the respective `config/protocol.*.yml`: In `config/protocol.*.yml` there is
``` ```
Prometheus: Prometheus:
Enabled: true Enabled: true
Addresses: Port: 2112
- ":2112"
``` ```
where you can switch on/off and define port. Prometheus is enabled and Pprof is disabled by default. where you can switch on/off and define port. Prometheus is enabled and Pprof is disabled by default.
@ -175,14 +167,15 @@ where you can switch on/off and define port. Prometheus is enabled and Pprof is
Feel free to contribute to this project after reading the Feel free to contribute to this project after reading the
[contributing guidelines](CONTRIBUTING.md). [contributing guidelines](CONTRIBUTING.md).
Before starting to work on a certain topic, create a new issue first Before starting to work on a certain topic, create an new issue first,
describing the feature/topic you are going to implement. describing the feature/topic you are going to implement.
# Contact # Contact
- [@AnnaShaleva](https://github.com/AnnaShaleva) on GitHub
- [@roman-khimov](https://github.com/roman-khimov) on GitHub - [@roman-khimov](https://github.com/roman-khimov) on GitHub
- Reach out to us on the [Neo Discord](https://discordapp.com/invite/R8v48YA) channel - [@AnnaShaleva](https://github.com/AnnaShaleva) on GitHub
- [@fyrchik](https://github.com/fyrchik) on GitHub
- Reach out to us on the [NEO Discord](https://discordapp.com/invite/R8v48YA) channel
# License # License

View file

@ -1,71 +1,11 @@
# Roadmap for neo-go # Roadmap for neo-go
This defines approximate plan of neo-go releases and key features planned for This defines approximate plan of neo-go releases and key features planned for
them. Things can change if there is a need to push a bugfix or some critical them. Things can change if there a need to push a bugfix or some critical
functionality. functionality.
## Versions 0.7X.Y (as needed) ## Versions 0.7X.Y (as needed)
* Neo 2.0 support (bug fixes, minor functionality additions) * Neo 2.0 support (bug fixes, minor functionality additions)
## Version 0.107.0 (~Jun-Jul 2024) ## Version 1.0 (2022, TBD)
* protocol updates * full NEO N3 support and useful extensions
* bug fixes
* node resynchronisation from local DB
* CLI library upgrade
## Version 1.0 (2024, TBD)
* stable version
# Deprecated functionality
As the node and the protocol evolve some external APIs can change. Usually we
try keeping backwards compatibility for some time (like half a year) unless
it's impossible to do for some reason. But eventually old
APIs/commands/configurations will be removed and here is a list of scheduled
breaking changes. Consider changing your code/scripts/configurations if you're
using anything mentioned here.
## GetPeers RPC server response type changes and RPC client support
GetPeers RPC command returns a list of Peers where the port type has changed from
string to uint16 to match C#. The RPC client currently supports unmarshalling both
formats.
Removal of Peer unmarshalling with string based ports is scheduled for Jun-Jul 2024
(~0.107.0 release).
## `NEOBalance` from stack item
We check struct items count before convert LastGasPerVote to let RPC client be compatible with
old versions.
Removal of this compatiblility code is scheduled for Jun-Jul 2024.
## `serv_node_version` Prometheus gauge metric
This metric is replaced by the new `neogo_version` and `server_id` Prometheus gauge
metrics with proper version formatting. `neogo_version` contains NeoGo version
hidden under `version` label and `server_id` contains network server ID hidden
under `server_id` label.
Removal of `serv_node_version` is scheduled for Jun-Jul 2024 (~0.107.0 release).
## RPC error codes returned by old versions and C#-nodes
NeoGo retains certain deprecated error codes: `neorpc.ErrCompatGeneric`,
`neorpc.ErrCompatNoOpenedWallet`. They returned by nodes not compliant with the
neo-project/proposals#156 (NeoGo pre-0.102.0 and all known C# versions).
Removal of the deprecated RPC error codes is planned for Jun-Jul 2024 (~0.107.0
release).
## Block based web-socket waiter transaction awaiting
Web-socket RPC based `waiter.EventWaiter` uses `header_of_added_block` notifications
subscription to manage transaction awaiting. To support old NeoGo RPC servers
(older than 0.105.0) that do not have block headers subscription ability,
event-based waiter fallbacks to the old way of block monitoring with
`block_added` notifications subscription.
Removal of stale RPC server compatibility code from `waiter.EventWaiter` is
scheduled for Jun-Jul 2024 (~0.107.0 release).

View file

@ -1,41 +0,0 @@
package app
import (
"fmt"
"os"
"runtime"
"github.com/nspcc-dev/neo-go/cli/query"
"github.com/nspcc-dev/neo-go/cli/server"
"github.com/nspcc-dev/neo-go/cli/smartcontract"
"github.com/nspcc-dev/neo-go/cli/util"
"github.com/nspcc-dev/neo-go/cli/vm"
"github.com/nspcc-dev/neo-go/cli/wallet"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/urfave/cli"
)
func versionPrinter(c *cli.Context) {
_, _ = fmt.Fprintf(c.App.Writer, "NeoGo\nVersion: %s\nGoVersion: %s\n",
config.Version,
runtime.Version(),
)
}
// New creates a NeoGo instance of [cli.App] with all commands included.
func New() *cli.App {
cli.VersionPrinter = versionPrinter
ctl := cli.NewApp()
ctl.Name = "neo-go"
ctl.Version = config.Version
ctl.Usage = "Official Go client for Neo"
ctl.ErrWriter = os.Stdout
ctl.Commands = append(ctl.Commands, server.NewCommands()...)
ctl.Commands = append(ctl.Commands, smartcontract.NewCommands()...)
ctl.Commands = append(ctl.Commands, wallet.NewCommands()...)
ctl.Commands = append(ctl.Commands, vm.NewCommands()...)
ctl.Commands = append(ctl.Commands, util.NewCommands()...)
ctl.Commands = append(ctl.Commands, query.NewCommands()...)
return ctl
}

View file

@ -1,19 +0,0 @@
package app_test
import (
"testing"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/nspcc-dev/neo-go/internal/versionutil"
"github.com/nspcc-dev/neo-go/pkg/config"
)
func TestCLIVersion(t *testing.T) {
config.Version = versionutil.TestVersion // Zero-length version string disables '--version' completely.
e := testcli.NewExecutor(t, false)
e.Run(t, "neo-go", "--version")
e.CheckNextLine(t, "^NeoGo")
e.CheckNextLine(t, "^Version:")
e.CheckNextLine(t, "^GoVersion:")
e.CheckEOF(t)
}

131
cli/candidate_test.go Normal file
View file

@ -0,0 +1,131 @@
package main
import (
"encoding/hex"
"math/big"
"strconv"
"testing"
"github.com/stretchr/testify/require"
)
// Register standby validator and vote for it.
// We don't create a new account here, because chain will
// stop working after validator will change.
func TestRegisterCandidate(t *testing.T) {
e := newExecutor(t, true)
validatorHex := hex.EncodeToString(validatorPriv.PublicKey().Bytes())
e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "nep17", "multitransfer",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet,
"--from", validatorAddr,
"--force",
"NEO:"+validatorPriv.Address()+":10",
"GAS:"+validatorPriv.Address()+":10000")
e.checkTxPersisted(t)
e.Run(t, "neo-go", "query", "committee",
"--rpc-endpoint", "http://"+e.RPC.Addr)
e.checkNextLine(t, "^\\s*"+validatorHex)
e.Run(t, "neo-go", "query", "candidates",
"--rpc-endpoint", "http://"+e.RPC.Addr)
e.checkNextLine(t, "^\\s*Key.+$") // Header.
e.checkEOF(t)
// missing address
e.RunWithError(t, "neo-go", "wallet", "candidate", "register",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet)
e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "candidate", "register",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet,
"--address", validatorPriv.Address())
e.checkTxPersisted(t)
vs, err := e.Chain.GetEnrollments()
require.NoError(t, err)
require.Equal(t, 1, len(vs))
require.Equal(t, validatorPriv.PublicKey(), vs[0].Key)
require.Equal(t, big.NewInt(0), vs[0].Votes)
t.Run("VoteUnvote", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "candidate", "vote",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet,
"--address", validatorPriv.Address(),
"--candidate", validatorHex)
_, index := e.checkTxPersisted(t)
vs, err = e.Chain.GetEnrollments()
require.Equal(t, 1, len(vs))
require.Equal(t, validatorPriv.PublicKey(), vs[0].Key)
b, _ := e.Chain.GetGoverningTokenBalance(validatorPriv.GetScriptHash())
require.Equal(t, b, vs[0].Votes)
e.Run(t, "neo-go", "query", "committee",
"--rpc-endpoint", "http://"+e.RPC.Addr)
e.checkNextLine(t, "^\\s*"+validatorHex)
e.Run(t, "neo-go", "query", "candidates",
"--rpc-endpoint", "http://"+e.RPC.Addr)
e.checkNextLine(t, "^\\s*Key.+$") // Header.
e.checkNextLine(t, "^\\s*"+validatorHex+"\\s*"+b.String()+"\\s*true\\s*true$")
e.checkEOF(t)
// check state
e.Run(t, "neo-go", "query", "voter",
"--rpc-endpoint", "http://"+e.RPC.Addr,
validatorPriv.Address())
e.checkNextLine(t, "^\\s*Voted:\\s+"+validatorHex+"\\s+\\("+validatorPriv.Address()+"\\)$")
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+b.String()+"$")
e.checkNextLine(t, "^\\s*Block\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
e.checkEOF(t)
// unvote
e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "candidate", "vote",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet,
"--address", validatorPriv.Address())
_, index = e.checkTxPersisted(t)
vs, err = e.Chain.GetEnrollments()
require.Equal(t, 1, len(vs))
require.Equal(t, validatorPriv.PublicKey(), vs[0].Key)
require.Equal(t, big.NewInt(0), vs[0].Votes)
// check state
e.Run(t, "neo-go", "query", "voter",
"--rpc-endpoint", "http://"+e.RPC.Addr,
validatorPriv.Address())
e.checkNextLine(t, "^\\s*Voted:\\s+"+"null") // no vote.
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+b.String()+"$")
e.checkNextLine(t, "^\\s*Block\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
e.checkEOF(t)
})
// missing address
e.RunWithError(t, "neo-go", "wallet", "candidate", "unregister",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet)
e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "candidate", "unregister",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet,
"--address", validatorPriv.Address())
e.checkTxPersisted(t)
vs, err = e.Chain.GetEnrollments()
require.Equal(t, 0, len(vs))
// query voter: missing address
e.RunWithError(t, "neo-go", "query", "voter")
}

View file

@ -9,7 +9,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" "github.com/nspcc-dev/neo-go/pkg/rpc/client"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli" "github.com/urfave/cli"
@ -24,144 +24,19 @@ const (
ArrayEndSeparator = "]" ArrayEndSeparator = "]"
) )
const (
// ParamsParsingDoc is a documentation for parameters parsing.
ParamsParsingDoc = ` Arguments always do have regular Neo smart contract parameter types, either
specified explicitly or being inferred from the value. To specify the type
manually use "type:value" syntax where the type is one of the following:
'signature', 'bool', 'int', 'hash160', 'hash256', 'bytes', 'key' or 'string'.
Array types are also supported: use special space-separated '[' and ']'
symbols around array values to denote array bounds. Nested arrays are also
supported. Null parameter is supported via 'nil' keyword without additional
type specification.
There is ability to provide an argument of 'bytearray' type via file. Use a
special 'filebytes' argument type for this with a filepath specified after
the colon, e.g. 'filebytes:my_file.txt'.
Given values are type-checked against given types with the following
restrictions applied:
* 'signature' type values should be hex-encoded and have a (decoded)
length of 64 bytes.
* 'bool' type values are 'true' and 'false'.
* 'int' values are decimal integers that can be successfully converted
from the string.
* 'hash160' values are Neo addresses and hex-encoded 20-bytes long (after
decoding) strings.
* 'hash256' type values should be hex-encoded and have a (decoded)
length of 32 bytes.
* 'bytes' type values are any hex-encoded things.
* 'filebytes' type values are filenames with the argument value inside.
* 'key' type values are hex-encoded marshalled public keys.
* 'string' type values are any valid UTF-8 strings. In the value's part of
the string the colon looses it's special meaning as a separator between
type and value and is taken literally.
If no type is explicitly specified, it is inferred from the value using the
following logic:
- anything that can be interpreted as a decimal integer gets
an 'int' type
- 'nil' string gets 'Any' NEP-14 parameter type and nil value which corresponds
to Null stackitem
- 'true' and 'false' strings get 'bool' type
- valid Neo addresses and 20 bytes long hex-encoded strings get 'hash160'
type
- valid hex-encoded public keys get 'key' type
- 32 bytes long hex-encoded values get 'hash256' type
- 64 bytes long hex-encoded values get 'signature' type
- any other valid hex-encoded values get 'bytes' type
- anything else is a 'string'
Backslash character is used as an escape character and allows to use colon in
an implicitly typed string. For any other characters it has no special
meaning, to get a literal backslash in the string use the '\\' sequence.
Examples:
* 'int:42' is an integer with a value of 42
* '42' is an integer with a value of 42
* 'nil' is a parameter with Any NEP-14 type and nil value (corresponds to Null stackitem)
* 'bad' is a string with a value of 'bad'
* 'dead' is a byte array with a value of 'dead'
* 'string:dead' is a string with a value of 'dead'
* 'filebytes:my_data.txt' is bytes decoded from a content of my_data.txt
* 'NSiVJYZej4XsxG5CUpdwn7VRQk8iiiDMPM' is a hash160 with a value
of '682cca3ebdc66210e5847d7f8115846586079d4a'
* '\4\2' is an integer with a value of 42
* '\\4\2' is a string with a value of '\42'
* 'string:string' is a string with a value of 'string'
* 'string\:string' is a string with a value of 'string:string'
* '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c' is a
key with a value of '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c'
* '[ a b c ]' is an array with strings values 'a', 'b' and 'c'
* '[ a b [ c d ] e ]' is an array with 4 values: string 'a', string 'b',
array of two strings 'c' and 'd', string 'e'
* '[ ]' is an empty array`
// SignersParsingDoc is a documentation for signers parsing.
SignersParsingDoc = ` Signers represent a set of Uint160 hashes with witness scopes and are used
to verify hashes in System.Runtime.CheckWitness syscall. First signer is treated
as a sender. To specify signers use signer[:scope] syntax where
* 'signer' is a signer's address (as Neo address or hex-encoded 160 bit (20 byte)
LE value with or without '0x' prefix).
* 'scope' is a comma-separated set of cosigner's scopes, which could be:
- 'None' - default witness scope which may be used for the sender
to only pay fee for the transaction.
- 'Global' - allows this witness in all contexts. This cannot be combined
with other flags.
- 'CalledByEntry' - means that this condition must hold: EntryScriptHash
== CallingScriptHash. The witness/permission/signature
given on first invocation will automatically expire if
entering deeper internal invokes. This can be default
safe choice for native NEO/GAS.
- 'CustomContracts' - define valid custom contract hashes for witness check.
Hashes are be provided as hex-encoded LE value string.
At lest one hash must be provided. Multiple hashes
are separated by ':'.
- 'CustomGroups' - define custom public keys for group members. Public keys are
provided as short-form (1-byte prefix + 32 bytes) hex-encoded
values. At least one key must be provided. Multiple keys
are separated by ':'.
If no scopes were specified, 'CalledByEntry' used as default. If no signers were
specified, no array is passed. Note that scopes are properly handled by
neo-go RPC server only. C# implementation does not support scopes capability.
Examples:
* 'NNQk4QXsxvsrr3GSozoWBUxEmfag7B6hz5'
* 'NVquyZHoPirw6zAEPvY1ZezxM493zMWQqs:Global'
* '0x0000000009070e030d0f0e020d0c06050e030c02'
* '0000000009070e030d0f0e020d0c06050e030c02:CalledByEntry,` +
`CustomGroups:0206d7495ceb34c197093b5fc1cccf1996ada05e69ef67e765462a7f5d88ee14d0'
* '0000000009070e030d0f0e020d0c06050e030c02:CalledByEntry,` +
`CustomContracts:1011120009070e030d0f0e020d0c06050e030c02:0x1211100009070e030d0f0e020d0c06050e030c02'`
)
// GetSignersFromContext returns signers parsed from context args starting // GetSignersFromContext returns signers parsed from context args starting
// from the specified offset. // from the specified offset.
func GetSignersFromContext(ctx *cli.Context, offset int) ([]transaction.Signer, *cli.ExitError) { func GetSignersFromContext(ctx *cli.Context, offset int) ([]transaction.Signer, *cli.ExitError) {
args := ctx.Args() args := ctx.Args()
var (
signers []transaction.Signer
err error
)
if args.Present() && len(args) > offset {
signers, err = ParseSigners(args[offset:])
if err != nil {
return nil, cli.NewExitError(err, 1)
}
}
return signers, nil
}
// ParseSigners returns array of signers parsed from their string representation.
func ParseSigners(args []string) ([]transaction.Signer, error) {
var signers []transaction.Signer var signers []transaction.Signer
for i, c := range args { if args.Present() && len(args) > offset {
cosigner, err := parseCosigner(c) for i, c := range args[offset:] {
if err != nil { cosigner, err := parseCosigner(c)
return nil, fmt.Errorf("failed to parse signer #%d: %w", i, err) if err != nil {
return nil, cli.NewExitError(fmt.Errorf("failed to parse signer #%d: %w", i, err), 1)
}
signers = append(signers, cosigner)
} }
signers = append(signers, cosigner)
} }
return signers, nil return signers, nil
} }
@ -230,9 +105,9 @@ func parseCosigner(c string) (transaction.Signer, error) {
} }
// GetDataFromContext returns data parameter from context args. // GetDataFromContext returns data parameter from context args.
func GetDataFromContext(ctx *cli.Context) (int, any, *cli.ExitError) { func GetDataFromContext(ctx *cli.Context) (int, interface{}, *cli.ExitError) {
var ( var (
data any data interface{}
offset int offset int
params []smartcontract.Parameter params []smartcontract.Parameter
err error err error
@ -256,15 +131,6 @@ func GetDataFromContext(ctx *cli.Context) (int, any, *cli.ExitError) {
return offset, data, nil return offset, data, nil
} }
// EnsureNone returns an error if there are any positional arguments present.
// It can be used to check for them in commands that don't accept arguments.
func EnsureNone(ctx *cli.Context) *cli.ExitError {
if ctx.Args().Present() {
return cli.NewExitError("additional arguments given while this command expects none", 1)
}
return nil
}
// ParseParams extracts array of smartcontract.Parameter from the given args and // ParseParams extracts array of smartcontract.Parameter from the given args and
// returns the number of handled words, the array itself and an error. // returns the number of handled words, the array itself and an error.
// `calledFromMain` denotes whether the method was called from the outside or // `calledFromMain` denotes whether the method was called from the outside or
@ -322,29 +188,17 @@ func ParseParams(args []string, calledFromMain bool) (int, []smartcontract.Param
// GetSignersAccounts returns the list of signers combined with the corresponding // GetSignersAccounts returns the list of signers combined with the corresponding
// accounts from the provided wallet. // accounts from the provided wallet.
func GetSignersAccounts(senderAcc *wallet.Account, wall *wallet.Wallet, signers []transaction.Signer, accScope transaction.WitnessScope) ([]actor.SignerAccount, error) { func GetSignersAccounts(wall *wallet.Wallet, signers []transaction.Signer) ([]client.SignerAccount, error) {
signersAccounts := make([]actor.SignerAccount, 0, len(signers)+1) signersAccounts := make([]client.SignerAccount, len(signers))
sender := senderAcc.ScriptHash() for i := range signers {
signersAccounts = append(signersAccounts, actor.SignerAccount{ signerAcc := wall.GetAccount(signers[i].Account)
Signer: transaction.Signer{
Account: sender,
Scopes: accScope,
},
Account: senderAcc,
})
for i, s := range signers {
if s.Account == sender {
signersAccounts[0].Signer = s
continue
}
signerAcc := wall.GetAccount(s.Account)
if signerAcc == nil { if signerAcc == nil {
return nil, fmt.Errorf("no account was found in the wallet for signer #%d (%s)", i, address.Uint160ToString(s.Account)) return nil, fmt.Errorf("no account was found in the wallet for signer #%d (%s)", i, address.Uint160ToString(signers[i].Account))
} }
signersAccounts = append(signersAccounts, actor.SignerAccount{ signersAccounts[i] = client.SignerAccount{
Signer: s, Signer: signers[i],
Account: signerAcc, Account: signerAcc,
}) }
} }
return signersAccounts, nil return signersAccounts, nil
} }

View file

@ -1,6 +1,7 @@
package cmdargs package cmdargs
import ( import (
"encoding/hex"
"strings" "strings"
"testing" "testing"
@ -44,7 +45,7 @@ func TestParseCosigner(t *testing.T) {
Scopes: transaction.CalledByEntry | transaction.CustomContracts, Scopes: transaction.CalledByEntry | transaction.CustomContracts,
AllowedContracts: []util.Uint160{c1, c2}, AllowedContracts: []util.Uint160{c1, c2},
}, },
acc.StringLE() + ":CustomGroups:" + priv.PublicKey().StringCompressed(): { acc.StringLE() + ":CustomGroups:" + hex.EncodeToString(priv.PublicKey().Bytes()): {
Account: acc, Account: acc,
Scopes: transaction.CustomGroups, Scopes: transaction.CustomGroups,
AllowedGroups: keys.PublicKeys{priv.PublicKey()}, AllowedGroups: keys.PublicKeys{priv.PublicKey()},

File diff suppressed because it is too large Load diff

106
cli/dump_test.go Normal file
View file

@ -0,0 +1,106 @@
package main
import (
"io/ioutil"
"os"
"path/filepath"
"strconv"
"testing"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v2"
)
func TestDBRestoreDump(t *testing.T) {
tmpDir := t.TempDir()
loadConfig := func(t *testing.T) config.Config {
chainPath := filepath.Join(tmpDir, "neogotestchain")
cfg, err := config.LoadFile(filepath.Join("..", "config", "protocol.unit_testnet.yml"))
require.NoError(t, err, "could not load config")
cfg.ApplicationConfiguration.DBConfiguration.Type = "leveldb"
cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath = chainPath
return cfg
}
cfg := loadConfig(t)
out, err := yaml.Marshal(cfg)
require.NoError(t, err)
cfgPath := filepath.Join(tmpDir, "protocol.unit_testnet.yml")
require.NoError(t, ioutil.WriteFile(cfgPath, out, os.ModePerm))
// generated via `go run ./scripts/gendump/main.go --out ./cli/testdata/chain50x2.acc --blocks 50 --txs 2`
const inDump = "./testdata/chain50x2.acc"
e := newExecutor(t, false)
stateDump := filepath.Join(tmpDir, "neogo.teststate")
baseArgs := []string{"neo-go", "db", "restore", "--unittest",
"--config-path", tmpDir, "--in", inDump, "--dump", stateDump}
// First 15 blocks.
e.Run(t, append(baseArgs, "--count", "15")...)
// Big count.
e.RunWithError(t, append(baseArgs, "--count", "1000")...)
// Continue 15..25
e.Run(t, append(baseArgs, "--count", "10")...)
// Continue till end.
e.Run(t, baseArgs...)
// Dump and compare.
dumpPath := filepath.Join(tmpDir, "testdump.acc")
t.Run("missing config", func(t *testing.T) {
e.RunWithError(t, "neo-go", "db", "dump", "--privnet",
"--config-path", tmpDir, "--out", dumpPath)
})
t.Run("bad logger config", func(t *testing.T) {
badConfigDir := t.TempDir()
logfile := filepath.Join(badConfigDir, "logdir")
require.NoError(t, ioutil.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm))
cfg = loadConfig(t)
cfg.ApplicationConfiguration.LogPath = filepath.Join(logfile, "file.log")
out, err = yaml.Marshal(cfg)
require.NoError(t, err)
cfgPath = filepath.Join(badConfigDir, "protocol.unit_testnet.yml")
require.NoError(t, ioutil.WriteFile(cfgPath, out, os.ModePerm))
e.RunWithError(t, "neo-go", "db", "dump", "--unittest",
"--config-path", badConfigDir, "--out", dumpPath)
})
t.Run("bad storage config", func(t *testing.T) {
badConfigDir := t.TempDir()
logfile := filepath.Join(badConfigDir, "logdir")
require.NoError(t, ioutil.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm))
cfg = loadConfig(t)
cfg.ApplicationConfiguration.DBConfiguration.Type = ""
out, err = yaml.Marshal(cfg)
require.NoError(t, err)
cfgPath = filepath.Join(badConfigDir, "protocol.unit_testnet.yml")
require.NoError(t, ioutil.WriteFile(cfgPath, out, os.ModePerm))
e.RunWithError(t, "neo-go", "db", "dump", "--unittest",
"--config-path", badConfigDir, "--out", dumpPath)
})
baseCmd := []string{"neo-go", "db", "dump", "--unittest",
"--config-path", tmpDir, "--out", dumpPath}
t.Run("invalid start/count", func(t *testing.T) {
e.RunWithError(t, append(baseCmd, "--start", "5", "--count", strconv.Itoa(50-5+1+1))...)
})
e.Run(t, baseCmd...)
d1, err := ioutil.ReadFile(inDump)
require.NoError(t, err)
d2, err := ioutil.ReadFile(dumpPath)
require.NoError(t, err)
require.Equal(t, d1, d2, "dumps differ")
}

View file

@ -1,25 +1,16 @@
/* package main
Package testcli contains auxiliary code to test CLI commands.
All testdata assets for it are contained in the cli directory and paths here
use `../` prefix to reference them because the package itself is used from
cli/* subpackages.
*/
package testcli
import ( import (
"bytes" "bytes"
"errors" "errors"
"fmt"
"io" "io"
"io/ioutil"
"math" "math"
"path/filepath"
"strings" "strings"
"sync" "sync"
"testing" "testing"
"time" "time"
"github.com/nspcc-dev/neo-go/cli/app"
"github.com/nspcc-dev/neo-go/cli/input" "github.com/nspcc-dev/neo-go/cli/input"
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/consensus" "github.com/nspcc-dev/neo-go/pkg/consensus"
@ -29,10 +20,10 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/network" "github.com/nspcc-dev/neo-go/pkg/network"
"github.com/nspcc-dev/neo-go/pkg/services/rpcsrv" "github.com/nspcc-dev/neo-go/pkg/rpc/server"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli" "github.com/urfave/cli"
"go.uber.org/zap" "go.uber.org/zap"
@ -41,40 +32,31 @@ import (
) )
const ( const (
ValidatorWIF = "KxyjQ8eUa4FHt3Gvioyt1Wz29cTUrE4eTqX3yFSk1YFCsPL8uNsY" validatorWIF = "KxyjQ8eUa4FHt3Gvioyt1Wz29cTUrE4eTqX3yFSk1YFCsPL8uNsY"
ValidatorAddr = "NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP" validatorAddr = "NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP"
MultisigAddr = "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq" multisigAddr = "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq"
TestWalletPath = "../testdata/testwallet.json" testWalletPath = "testdata/testwallet.json"
TestWalletAccount = "Nfyz4KcsgYepRJw1W5C2uKCi6QWKf7v6gG" testWalletAccount = "Nfyz4KcsgYepRJw1W5C2uKCi6QWKf7v6gG"
TestWalletMultiPath = "../testdata/testwallet_multi.json" validatorWallet = "testdata/wallet1_solo.json"
TestWalletMultiAccount1 = "NgHcPxgEKZQV4QBedzyASJrgiANhJqBVLw" validatorPass = "one"
TestWalletMultiAccount2 = "NLvHRfKAifjio2z9HiwLo9ZnpRPHUbAHgH"
TestWalletMultiAccount3 = "NcDfG8foJx79XSihcDDrx1df7cHAoJBfXj"
ValidatorWallet = "../testdata/wallet1_solo.json"
ValidatorPass = "one"
) )
var ( var (
ValidatorHash, _ = address.StringToUint160(ValidatorAddr) validatorHash, _ = address.StringToUint160(validatorAddr)
ValidatorPriv, _ = keys.NewPrivateKeyFromWIF(ValidatorWIF) validatorPriv, _ = keys.NewPrivateKeyFromWIF(validatorWIF)
TestWalletMultiAccount1Hash, _ = address.StringToUint160(TestWalletMultiAccount1)
TestWalletMultiAccount2Hash, _ = address.StringToUint160(TestWalletMultiAccount2)
TestWalletMultiAccount3Hash, _ = address.StringToUint160(TestWalletMultiAccount3)
) )
// Executor represents context for a test instance. // executor represents context for a test instance.
// It can be safely used in multiple tests, but not in parallel. // It can be safely used in multiple tests, but not in parallel.
type Executor struct { type executor struct {
// CLI is a cli application to test. // CLI is a cli application to test.
CLI *cli.App CLI *cli.App
// Chain is a blockchain instance (can be empty). // Chain is a blockchain instance (can be empty).
Chain *core.Blockchain Chain *core.Blockchain
// RPC is an RPC server to query (can be empty). // RPC is an RPC server to query (can be empty).
RPC *rpcsrv.Server RPC *server.Server
// NetSrv is a network server (can be empty). // NetSrv is a network server (can be empty).
NetSrv *network.Server NetSrv *network.Server
// Out contains command output. // Out contains command output.
@ -138,8 +120,8 @@ func (w *ConcurrentBuffer) Reset() {
w.buf.Reset() w.buf.Reset()
} }
func NewTestChain(t *testing.T, f func(*config.Config), run bool) (*core.Blockchain, *rpcsrv.Server, *network.Server) { func newTestChain(t *testing.T, f func(*config.Config), run bool) (*core.Blockchain, *server.Server, *network.Server) {
configPath := "../../config/protocol.unit_testnet.single.yml" configPath := "../config/protocol.unit_testnet.single.yml"
cfg, err := config.LoadFile(configPath) cfg, err := config.LoadFile(configPath)
require.NoError(t, err, "could not load config") require.NoError(t, err, "could not load config")
if f != nil { if f != nil {
@ -148,50 +130,46 @@ func NewTestChain(t *testing.T, f func(*config.Config), run bool) (*core.Blockch
memoryStore := storage.NewMemoryStore() memoryStore := storage.NewMemoryStore()
logger := zaptest.NewLogger(t) logger := zaptest.NewLogger(t)
chain, err := core.NewBlockchain(memoryStore, cfg.Blockchain(), logger) chain, err := core.NewBlockchain(memoryStore, cfg.ProtocolConfiguration, logger)
require.NoError(t, err, "could not create chain") require.NoError(t, err, "could not create chain")
if run { if run {
go chain.Run() go chain.Run()
} }
serverConfig, err := network.NewServerConfig(cfg) serverConfig := network.NewServerConfig(cfg)
require.NoError(t, err)
serverConfig.UserAgent = fmt.Sprintf(config.UserAgentFormat, "0.98.3-test")
netSrv, err := network.NewServer(serverConfig, chain, chain.GetStateSyncModule(), zap.NewNop()) netSrv, err := network.NewServer(serverConfig, chain, chain.GetStateSyncModule(), zap.NewNop())
require.NoError(t, err) require.NoError(t, err)
cons, err := consensus.NewService(consensus.Config{ cons, err := consensus.NewService(consensus.Config{
Logger: zap.NewNop(), Logger: zap.NewNop(),
Broadcast: netSrv.BroadcastExtensible, Broadcast: netSrv.BroadcastExtensible,
Chain: chain, Chain: chain,
BlockQueue: netSrv.GetBlockQueue(), ProtocolConfiguration: chain.GetConfig(),
ProtocolConfiguration: cfg.ProtocolConfiguration,
RequestTx: netSrv.RequestTx, RequestTx: netSrv.RequestTx,
StopTxFlow: netSrv.StopTxFlow, Wallet: serverConfig.Wallet,
Wallet: cfg.ApplicationConfiguration.Consensus.UnlockWallet,
TimePerBlock: serverConfig.TimePerBlock, TimePerBlock: serverConfig.TimePerBlock,
}) })
require.NoError(t, err) require.NoError(t, err)
netSrv.AddConsensusService(cons, cons.OnPayload, cons.OnTransaction) netSrv.AddExtensibleHPService(cons, consensus.Category, cons.OnPayload, cons.OnTransaction)
netSrv.Start() go netSrv.Start(make(chan error, 1))
rpcServer := server.New(chain, cfg.ApplicationConfiguration.RPC, netSrv, nil, logger)
errCh := make(chan error, 2) errCh := make(chan error, 2)
rpcServer := rpcsrv.New(chain, cfg.ApplicationConfiguration.RPC, netSrv, nil, logger, errCh) rpcServer.Start(errCh)
rpcServer.Start()
return chain, &rpcServer, netSrv return chain, &rpcServer, netSrv
} }
func NewExecutor(t *testing.T, needChain bool) *Executor { func newExecutor(t *testing.T, needChain bool) *executor {
return NewExecutorWithConfig(t, needChain, true, nil) return newExecutorWithConfig(t, needChain, true, nil)
} }
func NewExecutorSuspended(t *testing.T) *Executor { func newExecutorSuspended(t *testing.T) *executor {
return NewExecutorWithConfig(t, true, false, nil) return newExecutorWithConfig(t, true, false, nil)
} }
func NewExecutorWithConfig(t *testing.T, needChain, runChain bool, f func(*config.Config)) *Executor { func newExecutorWithConfig(t *testing.T, needChain, runChain bool, f func(*config.Config)) *executor {
e := &Executor{ e := &executor{
CLI: app.New(), CLI: newApp(),
Out: NewConcurrentBuffer(), Out: NewConcurrentBuffer(),
Err: bytes.NewBuffer(nil), Err: bytes.NewBuffer(nil),
In: bytes.NewBuffer(nil), In: bytes.NewBuffer(nil),
@ -199,7 +177,7 @@ func NewExecutorWithConfig(t *testing.T, needChain, runChain bool, f func(*confi
e.CLI.Writer = e.Out e.CLI.Writer = e.Out
e.CLI.ErrWriter = e.Err e.CLI.ErrWriter = e.Err
if needChain { if needChain {
e.Chain, e.RPC, e.NetSrv = NewTestChain(t, f, runChain) e.Chain, e.RPC, e.NetSrv = newTestChain(t, f, runChain)
} }
t.Cleanup(func() { t.Cleanup(func() {
e.Close(t) e.Close(t)
@ -207,10 +185,10 @@ func NewExecutorWithConfig(t *testing.T, needChain, runChain bool, f func(*confi
return e return e
} }
func (e *Executor) Close(t *testing.T) { func (e *executor) Close(t *testing.T) {
input.Terminal = nil input.Terminal = nil
if e.RPC != nil { if e.RPC != nil {
e.RPC.Shutdown() require.NoError(t, e.RPC.Shutdown())
} }
if e.NetSrv != nil { if e.NetSrv != nil {
e.NetSrv.Shutdown() e.NetSrv.Shutdown()
@ -223,7 +201,7 @@ func (e *Executor) Close(t *testing.T) {
// GetTransaction returns tx with hash h after it has persisted. // GetTransaction returns tx with hash h after it has persisted.
// If it is in mempool, we can just wait for the next block, otherwise // If it is in mempool, we can just wait for the next block, otherwise
// it must be already in chain. 1 second is time per block in a unittest chain. // it must be already in chain. 1 second is time per block in a unittest chain.
func (e *Executor) GetTransaction(t *testing.T, h util.Uint256) (*transaction.Transaction, uint32) { func (e *executor) GetTransaction(t *testing.T, h util.Uint256) (*transaction.Transaction, uint32) {
var tx *transaction.Transaction var tx *transaction.Transaction
var height uint32 var height uint32
require.Eventually(t, func() bool { require.Eventually(t, func() bool {
@ -234,22 +212,22 @@ func (e *Executor) GetTransaction(t *testing.T, h util.Uint256) (*transaction.Tr
return tx, height return tx, height
} }
func (e *Executor) GetNextLine(t *testing.T) string { func (e *executor) getNextLine(t *testing.T) string {
line, err := e.Out.ReadString('\n') line, err := e.Out.ReadString('\n')
require.NoError(t, err) require.NoError(t, err)
return strings.TrimSuffix(line, "\n") return strings.TrimSuffix(line, "\n")
} }
func (e *Executor) CheckNextLine(t *testing.T, expected string) { func (e *executor) checkNextLine(t *testing.T, expected string) {
line := e.GetNextLine(t) line := e.getNextLine(t)
e.CheckLine(t, line, expected) e.checkLine(t, line, expected)
} }
func (e *Executor) CheckLine(t *testing.T, line, expected string) { func (e *executor) checkLine(t *testing.T, line, expected string) {
require.Regexp(t, expected, line) require.Regexp(t, expected, line)
} }
func (e *Executor) CheckEOF(t *testing.T) { func (e *executor) checkEOF(t *testing.T) {
_, err := e.Out.ReadString('\n') _, err := e.Out.ReadString('\n')
require.True(t, errors.Is(err, io.EOF)) require.True(t, errors.Is(err, io.EOF))
} }
@ -274,38 +252,24 @@ func checkExit(t *testing.T, ch <-chan int, code int) {
} }
// RunWithError runs command and checks that is exits with error. // RunWithError runs command and checks that is exits with error.
func (e *Executor) RunWithError(t *testing.T, args ...string) { func (e *executor) RunWithError(t *testing.T, args ...string) {
ch := setExitFunc() ch := setExitFunc()
require.Error(t, e.run(args...)) require.Error(t, e.run(args...))
checkExit(t, ch, 1) checkExit(t, ch, 1)
} }
// Run runs command and checks that there were no errors. // Run runs command and checks that there were no errors.
func (e *Executor) Run(t *testing.T, args ...string) { func (e *executor) Run(t *testing.T, args ...string) {
ch := setExitFunc() ch := setExitFunc()
require.NoError(t, e.run(args...)) require.NoError(t, e.run(args...))
checkExit(t, ch, 0) checkExit(t, ch, 0)
} }
func (e *executor) run(args ...string) error {
// RunUnchecked runs command and ensures that proper exit code is set (0 if no error is returned, 1 is an error is returned).
// The resulting error is returned (if so).
func (e *Executor) RunUnchecked(t *testing.T, args ...string) error {
ch := setExitFunc()
err := e.run(args...)
if err != nil {
checkExit(t, ch, 1)
} else {
checkExit(t, ch, 0)
}
return err
}
func (e *Executor) run(args ...string) error {
e.Out.Reset() e.Out.Reset()
e.Err.Reset() e.Err.Reset()
input.Terminal = term.NewTerminal(input.ReadWriter{ input.Terminal = term.NewTerminal(input.ReadWriter{
Reader: e.In, Reader: e.In,
Writer: io.Discard, Writer: ioutil.Discard,
}, "") }, "")
err := e.CLI.Run(args) err := e.CLI.Run(args)
input.Terminal = nil input.Terminal = nil
@ -313,7 +277,7 @@ func (e *Executor) run(args ...string) error {
return err return err
} }
func (e *Executor) CheckTxPersisted(t *testing.T, prefix ...string) (*transaction.Transaction, uint32) { func (e *executor) checkTxPersisted(t *testing.T, prefix ...string) (*transaction.Transaction, uint32) {
line, err := e.Out.ReadString('\n') line, err := e.Out.ReadString('\n')
require.NoError(t, err) require.NoError(t, err)
@ -328,18 +292,11 @@ func (e *Executor) CheckTxPersisted(t *testing.T, prefix ...string) (*transactio
aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application) aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(aer)) require.Equal(t, 1, len(aer))
require.Equal(t, vmstate.Halt, aer[0].VMState) require.Equal(t, vm.HaltState, aer[0].VMState)
return tx, height return tx, height
} }
func (e *Executor) CheckAwaitableTxPersisted(t *testing.T, prefix ...string) (*transaction.Transaction, uint32) { func generateKeys(t *testing.T, n int) ([]*keys.PrivateKey, keys.PublicKeys) {
tx, vub := e.CheckTxPersisted(t, prefix...)
e.CheckNextLine(t, "OnChain:\ttrue")
e.CheckNextLine(t, "VMState:\tHALT")
return tx, vub
}
func GenerateKeys(t *testing.T, n int) ([]*keys.PrivateKey, keys.PublicKeys) {
privs := make([]*keys.PrivateKey, n) privs := make([]*keys.PrivateKey, n)
pubs := make(keys.PublicKeys, n) pubs := make(keys.PublicKeys, n)
for i := range privs { for i := range privs {
@ -350,44 +307,3 @@ func GenerateKeys(t *testing.T, n int) ([]*keys.PrivateKey, keys.PublicKeys) {
} }
return privs, pubs return privs, pubs
} }
func (e *Executor) CheckTxTestInvokeOutput(t *testing.T, scriptSize int) {
e.CheckNextLine(t, `Hash:\s+`)
e.CheckNextLine(t, `OnChain:\s+false`)
e.CheckNextLine(t, `ValidUntil:\s+\d+`)
e.CheckNextLine(t, `Signer:\s+\w+`)
e.CheckNextLine(t, `SystemFee:\s+(\d|\.)+`)
e.CheckNextLine(t, `NetworkFee:\s+(\d|\.)+`)
e.CheckNextLine(t, `Script:\s+\w+`)
e.CheckScriptDump(t, scriptSize)
}
func (e *Executor) CheckScriptDump(t *testing.T, scriptSize int) {
e.CheckNextLine(t, `INDEX\s+`)
for i := 0; i < scriptSize; i++ {
e.CheckNextLine(t, `\d+\s+\w+`)
}
}
func DeployContract(t *testing.T, e *Executor, inPath, configPath, wallet, address, pass string) util.Uint160 {
tmpDir := t.TempDir()
nefName := filepath.Join(tmpDir, "contract.nef")
manifestName := filepath.Join(tmpDir, "contract.manifest.json")
e.Run(t, "neo-go", "contract", "compile",
"--in", inPath,
"--config", configPath,
"--out", nefName, "--manifest", manifestName)
e.In.WriteString(pass + "\r")
e.Run(t, "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", wallet, "--address", address,
"--force",
"--in", nefName, "--manifest", manifestName)
e.CheckTxPersisted(t, "Sent invocation transaction ")
line, err := e.Out.ReadString('\n')
require.NoError(t, err)
line = strings.TrimSpace(strings.TrimPrefix(line, "Contract: "))
h, err := util.Uint160DecodeStringLE(line)
require.NoError(t, err)
return h
}

View file

@ -10,7 +10,7 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
) )
// Address is a wrapper for a Uint160 with flag.Value methods. // Address is a wrapper for Uint160 with flag.Value methods.
type Address struct { type Address struct {
IsSet bool IsSet bool
Value util.Uint160 Value util.Uint160
@ -28,12 +28,12 @@ var (
_ cli.Flag = AddressFlag{} _ cli.Flag = AddressFlag{}
) )
// String implements the fmt.Stringer interface. // String implements fmt.Stringer interface.
func (a Address) String() string { func (a Address) String() string {
return address.Uint160ToString(a.Value) return address.Uint160ToString(a.Value)
} }
// Set implements the flag.Value interface. // Set implements flag.Value interface.
func (a *Address) Set(s string) error { func (a *Address) Set(s string) error {
addr, err := ParseAddress(s) addr, err := ParseAddress(s)
if err != nil { if err != nil {
@ -44,7 +44,7 @@ func (a *Address) Set(s string) error {
return nil return nil
} }
// Uint160 casts an address to Uint160. // Uint160 casts address to Uint160.
func (a *Address) Uint160() (u util.Uint160) { func (a *Address) Uint160() (u util.Uint160) {
if !a.IsSet { if !a.IsSet {
// It is a programmer error to call this method without // It is a programmer error to call this method without
@ -82,7 +82,7 @@ func (f AddressFlag) GetName() string {
return f.Name return f.Name
} }
// Apply populates the flag given the flag set and environment. // Apply populates the flag given the flag set and environment
// Ignores errors. // Ignores errors.
func (f AddressFlag) Apply(set *flag.FlagSet) { func (f AddressFlag) Apply(set *flag.FlagSet) {
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -90,7 +90,7 @@ func (f AddressFlag) Apply(set *flag.FlagSet) {
}) })
} }
// ParseAddress parses a Uint160 from either an LE string or an address. // ParseAddress parses Uint160 form either LE string or address.
func ParseAddress(s string) (util.Uint160, error) { func ParseAddress(s string) (util.Uint160, error) {
const uint160size = 2 * util.Uint160Size const uint160size = 2 * util.Uint160Size
switch len(s) { switch len(s) {

View file

@ -2,7 +2,7 @@ package flags
import ( import (
"flag" "flag"
"io" "io/ioutil"
"testing" "testing"
"github.com/nspcc-dev/neo-go/internal/random" "github.com/nspcc-dev/neo-go/internal/random"
@ -119,7 +119,7 @@ func TestAddressFlag_GetName(t *testing.T) {
func TestAddress(t *testing.T) { func TestAddress(t *testing.T) {
f := flag.NewFlagSet("", flag.ContinueOnError) f := flag.NewFlagSet("", flag.ContinueOnError)
f.SetOutput(io.Discard) // don't pollute test output f.SetOutput(ioutil.Discard) // don't pollute test output
addr := AddressFlag{Name: "addr, a"} addr := AddressFlag{Name: "addr, a"}
addr.Apply(f) addr.Apply(f)
require.NoError(t, f.Parse([]string{"--addr", "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR"})) require.NoError(t, f.Parse([]string{"--addr", "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR"}))

View file

@ -8,7 +8,7 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
) )
// Fixed8 is a wrapper for a Uint160 with flag.Value methods. // Fixed8 is a wrapper for Uint160 with flag.Value methods.
type Fixed8 struct { type Fixed8 struct {
Value fixedn.Fixed8 Value fixedn.Fixed8
} }
@ -25,12 +25,12 @@ var (
_ cli.Flag = Fixed8Flag{} _ cli.Flag = Fixed8Flag{}
) )
// String implements the fmt.Stringer interface. // String implements fmt.Stringer interface.
func (a Fixed8) String() string { func (a Fixed8) String() string {
return a.Value.String() return a.Value.String()
} }
// Set implements the flag.Value interface. // Set implements flag.Value interface.
func (a *Fixed8) Set(s string) error { func (a *Fixed8) Set(s string) error {
f, err := fixedn.Fixed8FromString(s) f, err := fixedn.Fixed8FromString(s)
if err != nil { if err != nil {
@ -40,7 +40,7 @@ func (a *Fixed8) Set(s string) error {
return nil return nil
} }
// Fixed8 casts the address to util.Fixed8. // Fixed8 casts address to util.Fixed8.
func (a *Fixed8) Fixed8() fixedn.Fixed8 { func (a *Fixed8) Fixed8() fixedn.Fixed8 {
return a.Value return a.Value
} }
@ -61,7 +61,7 @@ func (f Fixed8Flag) GetName() string {
return f.Name return f.Name
} }
// Apply populates the flag given the flag set and environment. // Apply populates the flag given the flag set and environment
// Ignores errors. // Ignores errors.
func (f Fixed8Flag) Apply(set *flag.FlagSet) { func (f Fixed8Flag) Apply(set *flag.FlagSet) {
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -69,7 +69,7 @@ func (f Fixed8Flag) Apply(set *flag.FlagSet) {
}) })
} }
// Fixed8FromContext returns a parsed util.Fixed8 value provided flag name. // Fixed8FromContext returns parsed util.Fixed8 value provided flag name.
func Fixed8FromContext(ctx *cli.Context, name string) fixedn.Fixed8 { func Fixed8FromContext(ctx *cli.Context, name string) fixedn.Fixed8 {
return ctx.Generic(name).(*Fixed8).Value return ctx.Generic(name).(*Fixed8).Value
} }

View file

@ -2,7 +2,7 @@ package flags
import ( import (
"flag" "flag"
"io" "io/ioutil"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
@ -55,7 +55,7 @@ func TestFixed8Flag_GetName(t *testing.T) {
func TestFixed8(t *testing.T) { func TestFixed8(t *testing.T) {
f := flag.NewFlagSet("", flag.ContinueOnError) f := flag.NewFlagSet("", flag.ContinueOnError)
f.SetOutput(io.Discard) // don't pollute test output f.SetOutput(ioutil.Discard) // don't pollute test output
gas := Fixed8Flag{Name: "gas, g"} gas := Fixed8Flag{Name: "gas, g"}
gas.Apply(f) gas.Apply(f)
require.NoError(t, f.Parse([]string{"--gas", "0.123"})) require.NoError(t, f.Parse([]string{"--gas", "0.123"}))

View file

@ -21,7 +21,7 @@ type ReadWriter struct {
io.Writer io.Writer
} }
// ReadLine reads a line from the input without trailing '\n'. // ReadLine reads line from the input without trailing '\n'.
func ReadLine(prompt string) (string, error) { func ReadLine(prompt string) (string, error) {
trm := Terminal trm := Terminal
if trm == nil { if trm == nil {
@ -46,16 +46,21 @@ func readLine(trm *term.Terminal, prompt string) (string, error) {
return trm.ReadLine() return trm.ReadLine()
} }
// ReadPassword reads the user's password with prompt. // ReadPassword reads user password with prompt.
func ReadPassword(prompt string) (string, error) { func ReadPassword(prompt string) (string, error) {
trm := Terminal trm := Terminal
if trm != nil { if trm == nil {
return trm.ReadPassword(prompt) s, err := term.MakeRaw(int(syscall.Stdin))
if err != nil {
return "", err
}
defer func() { _ = term.Restore(int(syscall.Stdin), s) }()
trm = term.NewTerminal(ReadWriter{os.Stdin, os.Stdout}, prompt)
} }
return readSecurePassword(prompt) return trm.ReadPassword(prompt)
} }
// ConfirmTx asks for a confirmation to send the tx. // ConfirmTx asks for a confirmation to send tx.
func ConfirmTx(w io.Writer, tx *transaction.Transaction) error { func ConfirmTx(w io.Writer, tx *transaction.Transaction) error {
fmt.Fprintf(w, "Network fee: %s\n", fixedn.Fixed8(tx.NetworkFee)) fmt.Fprintf(w, "Network fee: %s\n", fixedn.Fixed8(tx.NetworkFee))
fmt.Fprintf(w, "System fee: %s\n", fixedn.Fixed8(tx.SystemFee)) fmt.Fprintf(w, "System fee: %s\n", fixedn.Fixed8(tx.SystemFee))

View file

@ -1,29 +0,0 @@
//go:build !windows
package input
import (
"fmt"
"os"
"golang.org/x/term"
)
// readSecurePassword reads the user's password with prompt directly from /dev/tty.
func readSecurePassword(prompt string) (string, error) {
f, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
return "", err
}
defer f.Close()
_, err = f.WriteString(prompt)
if err != nil {
return "", err
}
pass, err := term.ReadPassword(int(f.Fd()))
if err != nil {
return "", fmt.Errorf("failed to read password: %w", err)
}
_, err = f.WriteString("\n")
return string(pass), err
}

View file

@ -1,21 +0,0 @@
//go:build windows
package input
import (
"os"
"syscall"
"golang.org/x/term"
)
// readSecurePassword reads the user's password with prompt.
func readSecurePassword(prompt string) (string, error) {
s, err := term.MakeRaw(int(syscall.Stdin))
if err != nil {
return "", err
}
defer func() { _ = term.Restore(int(syscall.Stdin), s) }()
trm := term.NewTerminal(ReadWriter{os.Stdin, os.Stdout}, prompt)
return trm.ReadPassword(prompt)
}

View file

@ -3,13 +3,36 @@ package main
import ( import (
"os" "os"
"github.com/nspcc-dev/neo-go/cli/app" "github.com/nspcc-dev/neo-go/cli/query"
"github.com/nspcc-dev/neo-go/cli/server"
"github.com/nspcc-dev/neo-go/cli/smartcontract"
"github.com/nspcc-dev/neo-go/cli/util"
"github.com/nspcc-dev/neo-go/cli/vm"
"github.com/nspcc-dev/neo-go/cli/wallet"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/urfave/cli"
) )
func main() { func main() {
ctl := app.New() ctl := newApp()
if err := ctl.Run(os.Args); err != nil { if err := ctl.Run(os.Args); err != nil {
panic(err) panic(err)
} }
} }
func newApp() *cli.App {
ctl := cli.NewApp()
ctl.Name = "neo-go"
ctl.Version = config.Version
ctl.Usage = "Official Go client for Neo"
ctl.ErrWriter = os.Stdout
ctl.Commands = append(ctl.Commands, server.NewCommands()...)
ctl.Commands = append(ctl.Commands, smartcontract.NewCommands()...)
ctl.Commands = append(ctl.Commands, wallet.NewCommands()...)
ctl.Commands = append(ctl.Commands, vm.NewCommands()...)
ctl.Commands = append(ctl.Commands, util.NewCommands()...)
ctl.Commands = append(ctl.Commands, query.NewCommands()...)
return ctl
}

12
cli/main_test.go Normal file
View file

@ -0,0 +1,12 @@
package main
import (
"testing"
)
func TestCLIVersion(t *testing.T) {
e := newExecutor(t, false)
e.Run(t, "neo-go", "--version")
e.checkNextLine(t, "^neo-go version")
e.checkEOF(t)
}

View file

@ -1,6 +1,7 @@
package wallet_test package main
import ( import (
"encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"math/big" "math/big"
@ -8,15 +9,13 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/context"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -24,9 +23,9 @@ import (
// 1. Transfer funds to a created multisig address. // 1. Transfer funds to a created multisig address.
// 2. Transfer from multisig to another account. // 2. Transfer from multisig to another account.
func TestSignMultisigTx(t *testing.T) { func TestSignMultisigTx(t *testing.T) {
e := testcli.NewExecutor(t, true) e := newExecutor(t, true)
privs, pubs := testcli.GenerateKeys(t, 3) privs, pubs := generateKeys(t, 3)
script, err := smartcontract.CreateMultiSigRedeemScript(2, pubs) script, err := smartcontract.CreateMultiSigRedeemScript(2, pubs)
require.NoError(t, err) require.NoError(t, err)
multisigHash := hash.Hash160(script) multisigHash := hash.Hash160(script)
@ -44,9 +43,9 @@ func TestSignMultisigTx(t *testing.T) {
"--wallet", w, "--wallet", w,
"--wif", wif, "--wif", wif,
"--min", "2", "--min", "2",
pubs[0].StringCompressed(), hex.EncodeToString(pubs[0].Bytes()),
pubs[1].StringCompressed(), hex.EncodeToString(pubs[1].Bytes()),
pubs[2].StringCompressed()) hex.EncodeToString(pubs[2].Bytes()))
} }
addAccount(wallet1Path, privs[0].WIF()) addAccount(wallet1Path, privs[0].WIF())
addAccount(wallet2Path, privs[1].WIF()) addAccount(wallet2Path, privs[1].WIF())
@ -54,13 +53,13 @@ func TestSignMultisigTx(t *testing.T) {
// Transfer funds to the multisig. // Transfer funds to the multisig.
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "nep17", "multitransfer", e.Run(t, "neo-go", "wallet", "nep17", "multitransfer",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", testcli.ValidatorWallet, "--wallet", validatorWallet,
"--from", testcli.ValidatorAddr, "--from", validatorAddr,
"--force", "--force",
"NEO:"+multisigAddr+":4", "NEO:"+multisigAddr+":4",
"GAS:"+multisigAddr+":1") "GAS:"+multisigAddr+":1")
e.CheckTxPersisted(t) e.checkTxPersisted(t)
// Sign and transfer funds to another account. // Sign and transfer funds to another account.
priv, err := keys.NewPrivateKey() priv, err := keys.NewPrivateKey()
@ -73,7 +72,7 @@ func TestSignMultisigTx(t *testing.T) {
}) })
e.In.WriteString("pass\r") e.In.WriteString("pass\r")
e.Run(t, "neo-go", "wallet", "nep17", "transfer", e.Run(t, "neo-go", "wallet", "nep17", "transfer",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wallet1Path, "--from", multisigAddr, "--wallet", wallet1Path, "--from", multisigAddr,
"--to", priv.Address(), "--token", "NEO", "--amount", "1", "--to", priv.Address(), "--token", "NEO", "--amount", "1",
"--out", txPath) "--out", txPath)
@ -98,7 +97,7 @@ func TestSignMultisigTx(t *testing.T) {
// invalid out // invalid out
e.In.WriteString("pass\r") e.In.WriteString("pass\r")
e.RunWithError(t, "neo-go", "wallet", "sign", e.RunWithError(t, "neo-go", "wallet", "sign",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wallet2Path, "--address", multisigAddr, "--wallet", wallet2Path, "--address", multisigAddr,
"--in", txPath, "--out", t.TempDir()) "--in", txPath, "--out", t.TempDir())
@ -117,7 +116,7 @@ func TestSignMultisigTx(t *testing.T) {
}) })
e.In.WriteString("pass\r") e.In.WriteString("pass\r")
e.Run(t, "neo-go", "wallet", "nep17", "transfer", e.Run(t, "neo-go", "wallet", "nep17", "transfer",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wallet1Path, "--from", multisigAddr, "--wallet", wallet1Path, "--from", multisigAddr,
"--to", priv.Address(), "--token", "NEO", "--amount", "1", "--to", priv.Address(), "--token", "NEO", "--amount", "1",
"--out", txPath) "--out", txPath)
@ -145,63 +144,30 @@ func TestSignMultisigTx(t *testing.T) {
t.Run("no invoke", func(t *testing.T) { t.Run("no invoke", func(t *testing.T) {
e.Run(t, "neo-go", "util", "txdump", txPath) e.Run(t, "neo-go", "util", "txdump", txPath)
e.CheckTxTestInvokeOutput(t, 11) e.checkTxTestInvokeOutput(t, 11)
e.CheckEOF(t) e.checkEOF(t)
}) })
t.Run("excessive parameters", func(t *testing.T) {
e.RunWithError(t, "neo-go", "util", "txdump",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
txPath, "garbage")
})
e.Run(t, "neo-go", "util", "txdump", e.Run(t, "neo-go", "util", "txdump",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
txPath) txPath)
e.CheckTxTestInvokeOutput(t, 11) e.checkTxTestInvokeOutput(t, 11)
res := new(result.Invoke) res := new(result.Invoke)
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res)) require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException) require.Equal(t, vm.HaltState.String(), res.State, res.FaultException)
}) })
t.Run("console output", func(t *testing.T) { e.In.WriteString("pass\r")
oldIn, err := os.ReadFile(txPath) e.Run(t, "neo-go", "wallet", "sign",
require.NoError(t, err) "--rpc-endpoint", "http://"+e.RPC.Addr,
e.In.WriteString("pass\r") "--wallet", wallet2Path, "--address", multisigAddr,
e.Run(t, "neo-go", "wallet", "sign", "--in", txPath, "--out", txPath)
"--wallet", wallet2Path, "--address", multisigAddr, e.checkTxPersisted(t)
"--in", txPath)
newIn, err := os.ReadFile(txPath)
require.NoError(t, err)
require.Equal(t, oldIn, newIn)
pcOld := new(context.ParameterContext)
require.NoError(t, json.Unmarshal(oldIn, pcOld))
jOut := e.Out.Bytes()
pcNew := new(context.ParameterContext)
require.NoError(t, json.Unmarshal(jOut, pcNew))
require.Equal(t, pcOld.Type, pcNew.Type)
require.Equal(t, pcOld.Network, pcNew.Network)
require.Equal(t, pcOld.Verifiable, pcNew.Verifiable)
require.Equal(t, pcOld.Items[multisigHash].Script, pcNew.Items[multisigHash].Script)
// It's completely signed after this, so parameters have signatures now as well.
require.NotEqual(t, pcOld.Items[multisigHash].Parameters, pcNew.Items[multisigHash].Parameters)
require.NotEqual(t, pcOld.Items[multisigHash].Signatures, pcNew.Items[multisigHash].Signatures)
})
t.Run("sign, save and send", func(t *testing.T) {
e.In.WriteString("pass\r")
e.Run(t, "neo-go", "wallet", "sign",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", wallet2Path, "--address", multisigAddr,
"--in", txPath, "--out", txPath)
e.CheckTxPersisted(t)
})
t.Run("double-sign", func(t *testing.T) { t.Run("double-sign", func(t *testing.T) {
e.In.WriteString("pass\r") e.In.WriteString("pass\r")
e.RunWithError(t, "neo-go", "wallet", "sign", e.RunWithError(t, "neo-go", "wallet", "sign",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wallet2Path, "--address", multisigAddr, "--wallet", wallet2Path, "--address", multisigAddr,
"--in", txPath, "--out", txPath) "--in", txPath, "--out", txPath)
}) })
@ -216,13 +182,13 @@ func TestSignMultisigTx(t *testing.T) {
e.In.WriteString("acc\rpass\rpass\r") e.In.WriteString("acc\rpass\rpass\r")
e.Run(t, "neo-go", "wallet", "import-deployed", e.Run(t, "neo-go", "wallet", "import-deployed",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wallet1Path, "--wif", simplePriv.WIF(), "--wallet", wallet1Path, "--wif", simplePriv.WIF(),
"--contract", h.StringLE()) "--contract", h.StringLE())
e.In.WriteString("pass\r") e.In.WriteString("pass\r")
e.Run(t, "neo-go", "contract", "invokefunction", e.Run(t, "neo-go", "contract", "invokefunction",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wallet1Path, "--address", multisigHash.StringLE(), // test with scripthash instead of address "--wallet", wallet1Path, "--address", multisigHash.StringLE(), // test with scripthash instead of address
"--out", txPath, "--out", txPath,
e.Chain.GoverningTokenHash().StringLE(), "transfer", e.Chain.GoverningTokenHash().StringLE(), "transfer",
@ -248,10 +214,10 @@ func TestSignMultisigTx(t *testing.T) {
// Contract. // Contract.
e.In.WriteString("pass\r") e.In.WriteString("pass\r")
e.Run(t, "neo-go", "wallet", "sign", e.Run(t, "neo-go", "wallet", "sign",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wallet1Path, "--address", address.Uint160ToString(h), "--wallet", wallet1Path, "--address", address.Uint160ToString(h),
"--in", txPath, "--out", txPath) "--in", txPath, "--out", txPath)
tx, _ := e.CheckTxPersisted(t) tx, _ := e.checkTxPersisted(t)
require.Equal(t, 3, len(tx.Signers)) require.Equal(t, 3, len(tx.Signers))
b, _ := e.Chain.GetGoverningTokenBalance(priv.GetScriptHash()) b, _ := e.Chain.GetGoverningTokenBalance(priv.GetScriptHash())
@ -261,6 +227,20 @@ func TestSignMultisigTx(t *testing.T) {
}) })
} }
func deployVerifyContract(t *testing.T, e *testcli.Executor) util.Uint160 { func (e *executor) checkTxTestInvokeOutput(t *testing.T, scriptSize int) {
return testcli.DeployContract(t, e, "../smartcontract/testdata/verify.go", "../smartcontract/testdata/verify.yml", testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass) e.checkNextLine(t, `Hash:\s+`)
e.checkNextLine(t, `OnChain:\s+false`)
e.checkNextLine(t, `ValidUntil:\s+\d+`)
e.checkNextLine(t, `Signer:\s+\w+`)
e.checkNextLine(t, `SystemFee:\s+(\d|\.)+`)
e.checkNextLine(t, `NetworkFee:\s+(\d|\.)+`)
e.checkNextLine(t, `Script:\s+\w+`)
e.checkScriptDump(t, scriptSize)
}
func (e *executor) checkScriptDump(t *testing.T, scriptSize int) {
e.checkNextLine(t, `INDEX\s+`)
for i := 0; i < scriptSize; i++ {
e.checkNextLine(t, `\d+\s+\w+`)
}
} }

View file

@ -1,4 +1,4 @@
package nep_test package main
import ( import (
"bytes" "bytes"
@ -7,14 +7,12 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"math/big" "math/big"
"os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"testing" "testing"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/nspcc-dev/neo-go/internal/versionutil"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
@ -28,15 +26,12 @@ import (
const ( const (
// nftOwnerAddr is the owner of NFT-ND HASHY token (../examples/nft-nd/nft.go). // nftOwnerAddr is the owner of NFT-ND HASHY token (../examples/nft-nd/nft.go).
nftOwnerAddr = "NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB" nftOwnerAddr = "NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB"
nftOwnerWallet = "../../examples/my_wallet.json" nftOwnerWallet = "../examples/my_wallet.json"
nftOwnerPass = "qwerty" nftOwnerPass = "qwerty"
// Keep contract NEFs consistent between runs.
_ = versionutil.TestVersion
) )
func TestNEP11Import(t *testing.T) { func TestNEP11Import(t *testing.T) {
e := testcli.NewExecutor(t, true) e := newExecutor(t, true)
tmpDir := t.TempDir() tmpDir := t.TempDir()
walletPath := filepath.Join(tmpDir, "walletForImport.json") walletPath := filepath.Join(tmpDir, "walletForImport.json")
@ -51,15 +46,12 @@ func TestNEP11Import(t *testing.T) {
args := []string{ args := []string{
"neo-go", "wallet", "nep11", "import", "neo-go", "wallet", "nep11", "import",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", walletPath, "--wallet", walletPath,
} }
// missing token hash // missing token hash
e.RunWithError(t, args...) e.RunWithError(t, args...)
// excessive parameters
e.RunWithError(t, append(args, "--token", nnsContractHash.StringLE(), "something")...)
// good: non-divisible // good: non-divisible
e.Run(t, append(args, "--token", nnsContractHash.StringLE())...) e.Run(t, append(args, "--token", nnsContractHash.StringLE())...)
@ -73,18 +65,14 @@ func TestNEP11Import(t *testing.T) {
e.RunWithError(t, append(args, "--token", neoContractHash.StringLE())...) e.RunWithError(t, append(args, "--token", neoContractHash.StringLE())...)
checkInfo := func(t *testing.T, h util.Uint160, name string, symbol string, decimals int) { checkInfo := func(t *testing.T, h util.Uint160, name string, symbol string, decimals int) {
e.CheckNextLine(t, "^Name:\\s*"+name) e.checkNextLine(t, "^Name:\\s*"+name)
e.CheckNextLine(t, "^Symbol:\\s*"+symbol) e.checkNextLine(t, "^Symbol:\\s*"+symbol)
e.CheckNextLine(t, "^Hash:\\s*"+h.StringLE()) e.checkNextLine(t, "^Hash:\\s*"+h.StringLE())
e.CheckNextLine(t, "^Decimals:\\s*"+strconv.Itoa(decimals)) e.checkNextLine(t, "^Decimals:\\s*"+strconv.Itoa(decimals))
e.CheckNextLine(t, "^Address:\\s*"+address.Uint160ToString(h)) e.checkNextLine(t, "^Address:\\s*"+address.Uint160ToString(h))
e.CheckNextLine(t, "^Standard:\\s*"+string(manifest.NEP11StandardName)) e.checkNextLine(t, "^Standard:\\s*"+string(manifest.NEP11StandardName))
} }
t.Run("Info", func(t *testing.T) { t.Run("Info", func(t *testing.T) {
t.Run("excessive parameters", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "nep11", "info",
"--wallet", walletPath, "--token", nnsContractHash.StringLE(), "qwerty")
})
t.Run("WithToken", func(t *testing.T) { t.Run("WithToken", func(t *testing.T) {
e.Run(t, "neo-go", "wallet", "nep11", "info", e.Run(t, "neo-go", "wallet", "nep11", "info",
"--wallet", walletPath, "--token", nnsContractHash.StringLE()) "--wallet", walletPath, "--token", nnsContractHash.StringLE())
@ -94,14 +82,12 @@ func TestNEP11Import(t *testing.T) {
e.Run(t, "neo-go", "wallet", "nep11", "info", e.Run(t, "neo-go", "wallet", "nep11", "info",
"--wallet", walletPath) "--wallet", walletPath)
checkInfo(t, nnsContractHash, "NameService", "NNS", 0) checkInfo(t, nnsContractHash, "NameService", "NNS", 0)
e.CheckNextLine(t, "") e.checkNextLine(t, "")
checkInfo(t, nfsContractHash, "NeoFS Object NFT", "NFSO", 2) checkInfo(t, nfsContractHash, "NeoFS Object NFT", "NFSO", 2)
}) })
}) })
t.Run("Remove", func(t *testing.T) { t.Run("Remove", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "nep11", "remove",
"--wallet", walletPath, "--token", nnsContractHash.StringLE(), "parameter")
e.In.WriteString("y\r") e.In.WriteString("y\r")
e.Run(t, "neo-go", "wallet", "nep11", "remove", e.Run(t, "neo-go", "wallet", "nep11", "remove",
"--wallet", walletPath, "--token", nnsContractHash.StringLE()) "--wallet", walletPath, "--token", nnsContractHash.StringLE())
@ -114,27 +100,27 @@ func TestNEP11Import(t *testing.T) {
} }
func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) { func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
e := testcli.NewExecutor(t, true) e := newExecutor(t, true)
tmpDir := t.TempDir() tmpDir := t.TempDir()
// copy wallet to temp dir in order not to overwrite the original file // copy wallet to temp dir in order not to overwrite the original file
bytesRead, err := os.ReadFile(nftOwnerWallet) bytesRead, err := ioutil.ReadFile(nftOwnerWallet)
require.NoError(t, err) require.NoError(t, err)
wall := filepath.Join(tmpDir, "my_wallet.json") wall := filepath.Join(tmpDir, "my_wallet.json")
err = os.WriteFile(wall, bytesRead, 0755) err = ioutil.WriteFile(wall, bytesRead, 0755)
require.NoError(t, err) require.NoError(t, err)
// transfer funds to contract owner // transfer funds to contract owner
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "nep17", "transfer", e.Run(t, "neo-go", "wallet", "nep17", "transfer",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", testcli.ValidatorWallet, "--wallet", validatorWallet,
"--to", nftOwnerAddr, "--to", nftOwnerAddr,
"--token", "GAS", "--token", "GAS",
"--amount", "10000", "--amount", "10000",
"--force", "--force",
"--from", testcli.ValidatorAddr) "--from", validatorAddr)
e.CheckTxPersisted(t) e.checkTxPersisted(t)
// deploy NFT HASHY contract // deploy NFT HASHY contract
h := deployNFTContract(t, e) h := deployNFTContract(t, e)
@ -143,14 +129,14 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// mint 1 HASHY token by transferring 10 GAS to HASHY contract // mint 1 HASHY token by transferring 10 GAS to HASHY contract
e.In.WriteString(nftOwnerPass + "\r") e.In.WriteString(nftOwnerPass + "\r")
e.Run(t, "neo-go", "wallet", "nep17", "transfer", e.Run(t, "neo-go", "wallet", "nep17", "transfer",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wall, "--wallet", wall,
"--to", h.StringLE(), "--to", h.StringLE(),
"--token", "GAS", "--token", "GAS",
"--amount", "10", "--amount", "10",
"--force", "--force",
"--from", nftOwnerAddr) "--from", nftOwnerAddr)
txMint, _ := e.CheckTxPersisted(t) txMint, _ := e.checkTxPersisted(t)
// get NFT ID from AER // get NFT ID from AER
aer, err := e.Chain.GetAppExecResults(txMint.Hash(), trigger.Application) aer, err := e.Chain.GetAppExecResults(txMint.Hash(), trigger.Application)
@ -166,59 +152,40 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
} }
tokenID := mint(t) tokenID := mint(t)
var hashBeforeTransfer = e.Chain.CurrentHeaderHash()
// check the balance // check the balance
cmdCheckBalance := []string{"neo-go", "wallet", "nep11", "balance", cmdCheckBalance := []string{"neo-go", "wallet", "nep11", "balance",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", wall, "--wallet", wall,
"--address", nftOwnerAddr} "--address", nftOwnerAddr}
checkBalanceResult := func(t *testing.T, acc string, ids ...[]byte) { checkBalanceResult := func(t *testing.T, acc string, amount string) {
e.CheckNextLine(t, "^\\s*Account\\s+"+acc) e.checkNextLine(t, "^\\s*Account\\s+"+acc)
e.CheckNextLine(t, "^\\s*HASHY:\\s+HASHY NFT \\("+h.StringLE()+"\\)") e.checkNextLine(t, "^\\s*HASHY:\\s+HASHY NFT \\("+h.StringLE()+"\\)")
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+amount+"$")
// Hashes can be ordered in any way, so make a regexp for them. e.checkEOF(t)
var tokstring = "("
for i, id := range ids {
if i > 0 {
tokstring += "|"
}
tokstring += hex.EncodeToString(id)
}
tokstring += ")"
for range ids {
e.CheckNextLine(t, "^\\s*Token: "+tokstring+"\\s*$")
e.CheckNextLine(t, "^\\s*Amount: 1\\s*$")
e.CheckNextLine(t, "^\\s*Updated: [0-9]+\\s*$")
}
e.CheckEOF(t)
} }
// balance check: by symbol, token is not imported // balance check: by symbol, token is not imported
e.Run(t, append(cmdCheckBalance, "--token", "HASHY")...) e.RunWithError(t, append(cmdCheckBalance, "--token", "HASHY")...)
checkBalanceResult(t, nftOwnerAddr, tokenID)
// balance check: excessive parameters
e.RunWithError(t, append(cmdCheckBalance, "--token", h.StringLE(), "neo-go")...)
// balance check: by hash, ok // balance check: by hash, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...) e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, nftOwnerAddr, tokenID) checkBalanceResult(t, nftOwnerAddr, "1")
// import token // import token
e.Run(t, "neo-go", "wallet", "nep11", "import", e.Run(t, "neo-go", "wallet", "nep11", "import",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wall, "--wallet", wall,
"--token", h.StringLE()) "--token", h.StringLE())
// balance check: by symbol, ok // balance check: by symbol, ok
e.Run(t, append(cmdCheckBalance, "--token", "HASHY")...) e.Run(t, append(cmdCheckBalance, "--token", "HASHY")...)
checkBalanceResult(t, nftOwnerAddr, tokenID) checkBalanceResult(t, nftOwnerAddr, "1")
// balance check: all accounts // balance check: all accounts
e.Run(t, "neo-go", "wallet", "nep11", "balance", e.Run(t, "neo-go", "wallet", "nep11", "balance",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wall, "--wallet", wall,
"--token", h.StringLE()) "--token", h.StringLE())
checkBalanceResult(t, nftOwnerAddr, tokenID) checkBalanceResult(t, nftOwnerAddr, "1")
// remove token from wallet // remove token from wallet
e.In.WriteString("y\r") e.In.WriteString("y\r")
@ -227,7 +194,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// ownerOf: missing contract hash // ownerOf: missing contract hash
cmdOwnerOf := []string{"neo-go", "wallet", "nep11", "ownerOf", cmdOwnerOf := []string{"neo-go", "wallet", "nep11", "ownerOf",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addr,
} }
e.RunWithError(t, cmdOwnerOf...) e.RunWithError(t, cmdOwnerOf...)
cmdOwnerOf = append(cmdOwnerOf, "--token", h.StringLE()) cmdOwnerOf = append(cmdOwnerOf, "--token", h.StringLE())
@ -238,11 +205,11 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// ownerOf: good // ownerOf: good
e.Run(t, cmdOwnerOf...) e.Run(t, cmdOwnerOf...)
e.CheckNextLine(t, nftOwnerAddr) e.checkNextLine(t, nftOwnerAddr)
// tokensOf: missing contract hash // tokensOf: missing contract hash
cmdTokensOf := []string{"neo-go", "wallet", "nep11", "tokensOf", cmdTokensOf := []string{"neo-go", "wallet", "nep11", "tokensOf",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addr,
} }
e.RunWithError(t, cmdTokensOf...) e.RunWithError(t, cmdTokensOf...)
cmdTokensOf = append(cmdTokensOf, "--token", h.StringLE()) cmdTokensOf = append(cmdTokensOf, "--token", h.StringLE())
@ -253,12 +220,12 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// tokensOf: good // tokensOf: good
e.Run(t, cmdTokensOf...) e.Run(t, cmdTokensOf...)
require.Equal(t, hex.EncodeToString(tokenID), e.GetNextLine(t)) require.Equal(t, hex.EncodeToString(tokenID), e.getNextLine(t))
// properties: no contract // properties: no contract
cmdProperties := []string{ cmdProperties := []string{
"neo-go", "wallet", "nep11", "properties", "neo-go", "wallet", "nep11", "properties",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addr,
} }
e.RunWithError(t, cmdProperties...) e.RunWithError(t, cmdProperties...)
cmdProperties = append(cmdProperties, "--token", h.StringLE()) cmdProperties = append(cmdProperties, "--token", h.StringLE())
@ -269,7 +236,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// properties: ok // properties: ok
e.Run(t, cmdProperties...) e.Run(t, cmdProperties...)
require.Equal(t, fmt.Sprintf(`{"name":"HASHY %s"}`, base64.StdEncoding.EncodeToString(tokenID)), e.GetNextLine(t)) require.Equal(t, fmt.Sprintf(`{"name":"HASHY %s"}`, base64.StdEncoding.EncodeToString(tokenID)), e.getNextLine(t))
// tokensOf: good, several tokens // tokensOf: good, several tokens
tokenID1 := mint(t) tokenID1 := mint(t)
@ -279,32 +246,30 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
fst, snd = snd, fst fst, snd = snd, fst
} }
require.Equal(t, hex.EncodeToString(fst), e.GetNextLine(t)) require.Equal(t, hex.EncodeToString(fst), e.getNextLine(t))
require.Equal(t, hex.EncodeToString(snd), e.GetNextLine(t)) require.Equal(t, hex.EncodeToString(snd), e.getNextLine(t))
// tokens: missing contract hash // tokens: missing contract hash
cmdTokens := []string{"neo-go", "wallet", "nep11", "tokens", cmdTokens := []string{"neo-go", "wallet", "nep11", "tokens",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addr,
} }
e.RunWithError(t, cmdTokens...) e.RunWithError(t, cmdTokens...)
cmdTokens = append(cmdTokens, "--token", h.StringLE()) cmdTokens = append(cmdTokens, "--token", h.StringLE())
// tokens: excessive parameters
e.RunWithError(t, append(cmdTokens, "additional")...)
// tokens: good, several tokens // tokens: good, several tokens
e.Run(t, cmdTokens...) e.Run(t, cmdTokens...)
require.Equal(t, hex.EncodeToString(fst), e.GetNextLine(t)) require.Equal(t, hex.EncodeToString(fst), e.getNextLine(t))
require.Equal(t, hex.EncodeToString(snd), e.GetNextLine(t)) require.Equal(t, hex.EncodeToString(snd), e.getNextLine(t))
// balance check: several tokens, ok // balance check: several tokens, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...) e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, nftOwnerAddr, tokenID, tokenID1) checkBalanceResult(t, nftOwnerAddr, "2")
cmdTransfer := []string{ cmdTransfer := []string{
"neo-go", "wallet", "nep11", "transfer", "neo-go", "wallet", "nep11", "transfer",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", wall, "--wallet", wall,
"--to", testcli.ValidatorAddr, "--to", validatorAddr,
"--from", nftOwnerAddr, "--from", nftOwnerAddr,
"--force", "--force",
} }
@ -322,25 +287,17 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// transfer: good // transfer: good
e.In.WriteString(nftOwnerPass + "\r") e.In.WriteString(nftOwnerPass + "\r")
e.Run(t, append(cmdTransfer, "--id", hex.EncodeToString(tokenID))...) e.Run(t, append(cmdTransfer, "--id", hex.EncodeToString(tokenID))...)
e.CheckTxPersisted(t) e.checkTxPersisted(t)
// check balance after transfer // check balance after transfer
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...) e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, nftOwnerAddr, tokenID1) checkBalanceResult(t, nftOwnerAddr, "1") // tokenID1
// check --await flag
tokenID2 := mint(t)
e.In.WriteString(nftOwnerPass + "\r")
e.Run(t, append(cmdTransfer, "--await", "--id", hex.EncodeToString(tokenID2))...)
e.CheckAwaitableTxPersisted(t)
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, nftOwnerAddr, tokenID1)
// transfer: good, to NEP-11-Payable contract, with data // transfer: good, to NEP-11-Payable contract, with data
verifyH := deployVerifyContract(t, e) verifyH := deployVerifyContract(t, e)
cmdTransfer = []string{ cmdTransfer = []string{
"neo-go", "wallet", "nep11", "transfer", "neo-go", "wallet", "nep11", "transfer",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", wall, "--wallet", wall,
"--to", verifyH.StringLE(), "--to", verifyH.StringLE(),
"--from", nftOwnerAddr, "--from", nftOwnerAddr,
@ -351,7 +308,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
} }
e.In.WriteString(nftOwnerPass + "\r") e.In.WriteString(nftOwnerPass + "\r")
e.Run(t, cmdTransfer...) e.Run(t, cmdTransfer...)
tx, _ := e.CheckTxPersisted(t) tx, _ := e.checkTxPersisted(t)
// check OnNEP11Payment event // check OnNEP11Payment event
aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application) aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
require.NoError(t, err) require.NoError(t, err)
@ -362,7 +319,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
ScriptHash: verifyH, ScriptHash: verifyH,
Name: "OnNEP11Payment", Name: "OnNEP11Payment",
Item: stackitem.NewArray([]stackitem.Item{ Item: stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(nftOwnerHash.BytesBE()), stackitem.NewBuffer(nftOwnerHash.BytesBE()),
stackitem.NewBigInteger(big.NewInt(1)), stackitem.NewBigInteger(big.NewInt(1)),
stackitem.NewByteArray(tokenID1), stackitem.NewByteArray(tokenID1),
stackitem.NewByteArray([]byte("some_data")), stackitem.NewByteArray([]byte("some_data")),
@ -371,36 +328,18 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// check balance after transfer // check balance after transfer
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...) e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, nftOwnerAddr) checkBalanceResult(t, nftOwnerAddr, "0")
// historic calls still remember the good old days.
cmdOwnerOf = append(cmdOwnerOf, "--historic", hashBeforeTransfer.StringLE())
e.Run(t, cmdOwnerOf...)
e.CheckNextLine(t, nftOwnerAddr)
cmdTokensOf = append(cmdTokensOf, "--historic", hashBeforeTransfer.StringLE())
e.Run(t, cmdTokensOf...)
require.Equal(t, hex.EncodeToString(tokenID), e.GetNextLine(t))
cmdTokens = append(cmdTokens, "--historic", hashBeforeTransfer.StringLE())
e.Run(t, cmdTokens...)
require.Equal(t, hex.EncodeToString(tokenID), e.GetNextLine(t))
// this one is not affected by transfer, but anyway
cmdProperties = append(cmdProperties, "--historic", hashBeforeTransfer.StringLE())
e.Run(t, cmdProperties...)
require.Equal(t, fmt.Sprintf(`{"name":"HASHY %s"}`, base64.StdEncoding.EncodeToString(tokenID)), e.GetNextLine(t))
} }
func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) { func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
e := testcli.NewExecutor(t, true) e := newExecutor(t, true)
tmpDir := t.TempDir() tmpDir := t.TempDir()
// copy wallet to temp dir in order not to overwrite the original file // copy wallet to temp dir in order not to overwrite the original file
bytesRead, err := os.ReadFile(testcli.ValidatorWallet) bytesRead, err := ioutil.ReadFile(validatorWallet)
require.NoError(t, err) require.NoError(t, err)
wall := filepath.Join(tmpDir, "my_wallet.json") wall := filepath.Join(tmpDir, "my_wallet.json")
err = os.WriteFile(wall, bytesRead, 0755) err = ioutil.WriteFile(wall, bytesRead, 0755)
require.NoError(t, err) require.NoError(t, err)
// deploy NeoFS Object contract // deploy NeoFS Object contract
@ -408,18 +347,18 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
mint := func(t *testing.T, containerID, objectID util.Uint256) []byte { mint := func(t *testing.T, containerID, objectID util.Uint256) []byte {
// mint 1.00 NFSO token by transferring 10 GAS to NFSO contract // mint 1.00 NFSO token by transferring 10 GAS to NFSO contract
e.In.WriteString(testcli.ValidatorPass + "\r") e.In.WriteString(validatorPass + "\r")
e.Run(t, "neo-go", "wallet", "nep17", "transfer", e.Run(t, "neo-go", "wallet", "nep17", "transfer",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wall, "--wallet", wall,
"--to", h.StringLE(), "--to", h.StringLE(),
"--token", "GAS", "--token", "GAS",
"--amount", "10", "--amount", "10",
"--force", "--force",
"--from", testcli.ValidatorAddr, "--from", validatorAddr,
"--", "[", "hash256:"+containerID.StringLE(), "hash256:"+objectID.StringLE(), "]", "--", "[", "hash256:"+containerID.StringLE(), "hash256:"+objectID.StringLE(), "]",
) )
txMint, _ := e.CheckTxPersisted(t) txMint, _ := e.checkTxPersisted(t)
// get NFT ID from AER // get NFT ID from AER
aer, err := e.Chain.GetAppExecResults(txMint.Hash(), trigger.Application) aer, err := e.Chain.GetAppExecResults(txMint.Hash(), trigger.Application)
@ -444,66 +383,55 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// check properties // check properties
e.Run(t, "neo-go", "wallet", "nep11", "properties", e.Run(t, "neo-go", "wallet", "nep11", "properties",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--token", h.StringLE(), "--token", h.StringLE(),
"--id", hex.EncodeToString(token1ID)) "--id", hex.EncodeToString(token1ID))
jProps := e.GetNextLine(t) jProps := e.getNextLine(t)
props := make(map[string]string) props := make(map[string]string)
require.NoError(t, json.Unmarshal([]byte(jProps), &props)) require.NoError(t, json.Unmarshal([]byte(jProps), &props))
require.Equal(t, base64.StdEncoding.EncodeToString(container1ID.BytesBE()), props["containerID"]) require.Equal(t, base64.StdEncoding.EncodeToString(container1ID.BytesBE()), props["containerID"])
require.Equal(t, base64.StdEncoding.EncodeToString(object1ID.BytesBE()), props["objectID"]) require.Equal(t, base64.StdEncoding.EncodeToString(object1ID.BytesBE()), props["objectID"])
e.CheckEOF(t) e.checkEOF(t)
type idAmount struct {
id string
amount string
}
// check the balance // check the balance
cmdCheckBalance := []string{"neo-go", "wallet", "nep11", "balance", cmdCheckBalance := []string{"neo-go", "wallet", "nep11", "balance",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", wall, "--wallet", wall,
"--address", testcli.ValidatorAddr} "--address", validatorAddr}
checkBalanceResult := func(t *testing.T, acc string, objs ...idAmount) { checkBalanceResult := func(t *testing.T, acc string, amount string, id []byte) {
e.CheckNextLine(t, "^\\s*Account\\s+"+acc) e.checkNextLine(t, "^\\s*Account\\s+"+acc)
e.CheckNextLine(t, "^\\s*NFSO:\\s+NeoFS Object NFT \\("+h.StringLE()+"\\)") if id == nil {
e.checkNextLine(t, "^\\s*NFSO:\\s+NeoFS Object NFT \\("+h.StringLE()+"\\)")
for _, o := range objs { } else {
e.CheckNextLine(t, "^\\s*Token: "+o.id+"\\s*$") e.checkNextLine(t, "^\\s*NFSO:\\s+NeoFS Object NFT \\("+h.StringLE()+", "+hex.EncodeToString(id)+"\\)")
e.CheckNextLine(t, "^\\s*Amount: "+o.amount+"\\s*$")
e.CheckNextLine(t, "^\\s*Updated: [0-9]+\\s*$")
} }
e.CheckEOF(t) e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+amount+"$")
} e.checkEOF(t)
tokz := []idAmount{
{hex.EncodeToString(token1ID), "1"},
{hex.EncodeToString(token2ID), "1"},
} }
// balance check: by symbol, token is not imported // balance check: by symbol, token is not imported
e.Run(t, append(cmdCheckBalance, "--token", "NFSO")...) e.RunWithError(t, append(cmdCheckBalance, "--token", "NFSO")...)
checkBalanceResult(t, testcli.ValidatorAddr, tokz...)
// overall NFSO balance check: by hash, ok // overall NFSO balance check: by hash, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...) e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, testcli.ValidatorAddr, tokz...) checkBalanceResult(t, validatorAddr, "2", nil)
// particular NFSO balance check: by hash, ok // particular NFSO balance check: by hash, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE(), "--id", hex.EncodeToString(token2ID))...) e.Run(t, append(cmdCheckBalance, "--token", h.StringLE(), "--id", hex.EncodeToString(token2ID))...)
checkBalanceResult(t, testcli.ValidatorAddr, tokz[1]) checkBalanceResult(t, validatorAddr, "1", token2ID)
// import token // import token
e.Run(t, "neo-go", "wallet", "nep11", "import", e.Run(t, "neo-go", "wallet", "nep11", "import",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wall, "--wallet", wall,
"--token", h.StringLE()) "--token", h.StringLE())
// overall balance check: by symbol, ok // overall balance check: by symbol, ok
e.Run(t, append(cmdCheckBalance, "--token", "NFSO")...) e.Run(t, append(cmdCheckBalance, "--token", "NFSO")...)
checkBalanceResult(t, testcli.ValidatorAddr, tokz...) checkBalanceResult(t, validatorAddr, "2", nil)
// particular balance check: by symbol, ok // particular balance check: by symbol, ok
e.Run(t, append(cmdCheckBalance, "--token", "NFSO", "--id", hex.EncodeToString(token1ID))...) e.Run(t, append(cmdCheckBalance, "--token", "NFSO", "--id", hex.EncodeToString(token1ID))...)
checkBalanceResult(t, testcli.ValidatorAddr, tokz[0]) checkBalanceResult(t, validatorAddr, "1", token1ID)
// remove token from wallet // remove token from wallet
e.In.WriteString("y\r") e.In.WriteString("y\r")
@ -512,7 +440,7 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// ownerOfD: missing contract hash // ownerOfD: missing contract hash
cmdOwnerOf := []string{"neo-go", "wallet", "nep11", "ownerOfD", cmdOwnerOf := []string{"neo-go", "wallet", "nep11", "ownerOfD",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addr,
} }
e.RunWithError(t, cmdOwnerOf...) e.RunWithError(t, cmdOwnerOf...)
cmdOwnerOf = append(cmdOwnerOf, "--token", h.StringLE()) cmdOwnerOf = append(cmdOwnerOf, "--token", h.StringLE())
@ -523,29 +451,29 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// ownerOfD: good // ownerOfD: good
e.Run(t, cmdOwnerOf...) e.Run(t, cmdOwnerOf...)
e.CheckNextLine(t, testcli.ValidatorAddr) e.checkNextLine(t, validatorAddr)
// tokensOf: missing contract hash // tokensOf: missing contract hash
cmdTokensOf := []string{"neo-go", "wallet", "nep11", "tokensOf", cmdTokensOf := []string{"neo-go", "wallet", "nep11", "tokensOf",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addr,
} }
e.RunWithError(t, cmdTokensOf...) e.RunWithError(t, cmdTokensOf...)
cmdTokensOf = append(cmdTokensOf, "--token", h.StringLE()) cmdTokensOf = append(cmdTokensOf, "--token", h.StringLE())
// tokensOf: missing owner address // tokensOf: missing owner address
e.RunWithError(t, cmdTokensOf...) e.RunWithError(t, cmdTokensOf...)
cmdTokensOf = append(cmdTokensOf, "--address", testcli.ValidatorAddr) cmdTokensOf = append(cmdTokensOf, "--address", validatorAddr)
// tokensOf: good // tokensOf: good
e.Run(t, cmdTokensOf...) e.Run(t, cmdTokensOf...)
require.Equal(t, hex.EncodeToString(token1ID), e.GetNextLine(t)) require.Equal(t, hex.EncodeToString(token1ID), e.getNextLine(t))
require.Equal(t, hex.EncodeToString(token2ID), e.GetNextLine(t)) require.Equal(t, hex.EncodeToString(token2ID), e.getNextLine(t))
e.CheckEOF(t) e.checkEOF(t)
// properties: no contract // properties: no contract
cmdProperties := []string{ cmdProperties := []string{
"neo-go", "wallet", "nep11", "properties", "neo-go", "wallet", "nep11", "properties",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addr,
} }
e.RunWithError(t, cmdProperties...) e.RunWithError(t, cmdProperties...)
cmdProperties = append(cmdProperties, "--token", h.StringLE()) cmdProperties = append(cmdProperties, "--token", h.StringLE())
@ -554,17 +482,14 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
e.RunWithError(t, cmdProperties...) e.RunWithError(t, cmdProperties...)
cmdProperties = append(cmdProperties, "--id", hex.EncodeToString(token2ID)) cmdProperties = append(cmdProperties, "--id", hex.EncodeToString(token2ID))
// properties: additional parameter
e.RunWithError(t, append(cmdProperties, "additiona")...)
// properties: ok // properties: ok
e.Run(t, cmdProperties...) e.Run(t, cmdProperties...)
jProps = e.GetNextLine(t) jProps = e.getNextLine(t)
props = make(map[string]string) props = make(map[string]string)
require.NoError(t, json.Unmarshal([]byte(jProps), &props)) require.NoError(t, json.Unmarshal([]byte(jProps), &props))
require.Equal(t, base64.StdEncoding.EncodeToString(container2ID.BytesBE()), props["containerID"]) require.Equal(t, base64.StdEncoding.EncodeToString(container2ID.BytesBE()), props["containerID"])
require.Equal(t, base64.StdEncoding.EncodeToString(object2ID.BytesBE()), props["objectID"]) require.Equal(t, base64.StdEncoding.EncodeToString(object2ID.BytesBE()), props["objectID"])
e.CheckEOF(t) e.checkEOF(t)
// tokensOf: good, several tokens // tokensOf: good, several tokens
e.Run(t, cmdTokensOf...) e.Run(t, cmdTokensOf...)
@ -573,75 +498,75 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
fst, snd = snd, fst fst, snd = snd, fst
} }
require.Equal(t, hex.EncodeToString(fst), e.GetNextLine(t)) require.Equal(t, hex.EncodeToString(fst), e.getNextLine(t))
require.Equal(t, hex.EncodeToString(snd), e.GetNextLine(t)) require.Equal(t, hex.EncodeToString(snd), e.getNextLine(t))
// tokens: missing contract hash // tokens: missing contract hash
cmdTokens := []string{"neo-go", "wallet", "nep11", "tokens", cmdTokens := []string{"neo-go", "wallet", "nep11", "tokens",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addr,
} }
e.RunWithError(t, cmdTokens...) e.RunWithError(t, cmdTokens...)
cmdTokens = append(cmdTokens, "--token", h.StringLE()) cmdTokens = append(cmdTokens, "--token", h.StringLE())
// tokens: good, several tokens // tokens: good, several tokens
e.Run(t, cmdTokens...) e.Run(t, cmdTokens...)
require.Equal(t, hex.EncodeToString(fst), e.GetNextLine(t)) require.Equal(t, hex.EncodeToString(fst), e.getNextLine(t))
require.Equal(t, hex.EncodeToString(snd), e.GetNextLine(t)) require.Equal(t, hex.EncodeToString(snd), e.getNextLine(t))
// balance check: several tokens, ok // balance check: several tokens, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...) e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, testcli.ValidatorAddr, tokz...) checkBalanceResult(t, validatorAddr, "2", nil)
cmdTransfer := []string{ cmdTransfer := []string{
"neo-go", "wallet", "nep11", "transfer", "neo-go", "wallet", "nep11", "transfer",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", wall, "--wallet", wall,
"--to", nftOwnerAddr, "--to", nftOwnerAddr,
"--from", testcli.ValidatorAddr, "--from", validatorAddr,
"--force", "--force",
} }
// transfer: unimported token with symbol id specified // transfer: unimported token with symbol id specified
e.In.WriteString(testcli.ValidatorPass + "\r") e.In.WriteString(validatorPass + "\r")
e.RunWithError(t, append(cmdTransfer, e.RunWithError(t, append(cmdTransfer,
"--token", "NFSO")...) "--token", "NFSO")...)
cmdTransfer = append(cmdTransfer, "--token", h.StringLE()) cmdTransfer = append(cmdTransfer, "--token", h.StringLE())
// transfer: no id specified // transfer: no id specified
e.In.WriteString(testcli.ValidatorPass + "\r") e.In.WriteString(validatorPass + "\r")
e.RunWithError(t, cmdTransfer...) e.RunWithError(t, cmdTransfer...)
// transfer: good // transfer: good
e.In.WriteString(testcli.ValidatorPass + "\r") e.In.WriteString(validatorPass + "\r")
e.Run(t, append(cmdTransfer, "--id", hex.EncodeToString(token1ID))...) e.Run(t, append(cmdTransfer, "--id", hex.EncodeToString(token1ID))...)
e.CheckTxPersisted(t) e.checkTxPersisted(t)
// check balance after transfer // check balance after transfer
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...) e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, testcli.ValidatorAddr, tokz[1]) // only token2ID expected to be on the balance checkBalanceResult(t, validatorAddr, "1", nil) // only token2ID expected to be on the balance
// transfer: good, 1/4 of the balance, to NEP-11-Payable contract, with data // transfer: good, 1/4 of the balance, to NEP-11-Payable contract, with data
verifyH := deployVerifyContract(t, e) verifyH := deployVerifyContract(t, e)
cmdTransfer = []string{ cmdTransfer = []string{
"neo-go", "wallet", "nep11", "transfer", "neo-go", "wallet", "nep11", "transfer",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", wall, "--wallet", wall,
"--to", verifyH.StringLE(), "--to", verifyH.StringLE(),
"--from", testcli.ValidatorAddr, "--from", validatorAddr,
"--token", h.StringLE(), "--token", h.StringLE(),
"--id", hex.EncodeToString(token2ID), "--id", hex.EncodeToString(token2ID),
"--amount", "0.25", "--amount", "0.25",
"--force", "--force",
"string:some_data", "string:some_data",
} }
e.In.WriteString(testcli.ValidatorPass + "\r") e.In.WriteString(validatorPass + "\r")
e.Run(t, cmdTransfer...) e.Run(t, cmdTransfer...)
tx, _ := e.CheckTxPersisted(t) tx, _ := e.checkTxPersisted(t)
// check OnNEP11Payment event // check OnNEP11Payment event
aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application) aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(aer[0].Events)) require.Equal(t, 2, len(aer[0].Events))
validatorHash, err := address.StringToUint160(testcli.ValidatorAddr) validatorHash, err := address.StringToUint160(validatorAddr)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, state.NotificationEvent{ require.Equal(t, state.NotificationEvent{
ScriptHash: verifyH, ScriptHash: verifyH,
@ -656,22 +581,17 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// check balance after transfer // check balance after transfer
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...) e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
tokz[1].amount = "0.75" checkBalanceResult(t, validatorAddr, "0.75", nil)
checkBalanceResult(t, testcli.ValidatorAddr, tokz[1])
} }
func deployNFSContract(t *testing.T, e *testcli.Executor) util.Uint160 { func deployNFSContract(t *testing.T, e *executor) util.Uint160 {
return testcli.DeployContract(t, e, "../../examples/nft-d/nft.go", "../../examples/nft-d/nft.yml", testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass) return deployContract(t, e, "../examples/nft-d/nft.go", "../examples/nft-d/nft.yml", validatorWallet, validatorAddr, validatorPass)
} }
func deployNFTContract(t *testing.T, e *testcli.Executor) util.Uint160 { func deployNFTContract(t *testing.T, e *executor) util.Uint160 {
return testcli.DeployContract(t, e, "../../examples/nft-nd/nft.go", "../../examples/nft-nd/nft.yml", nftOwnerWallet, nftOwnerAddr, nftOwnerPass) return deployContract(t, e, "../examples/nft-nd/nft.go", "../examples/nft-nd/nft.yml", nftOwnerWallet, nftOwnerAddr, nftOwnerPass)
} }
func deployNNSContract(t *testing.T, e *testcli.Executor) util.Uint160 { func deployNNSContract(t *testing.T, e *executor) util.Uint160 {
return testcli.DeployContract(t, e, "../../examples/nft-nd-nns/", "../../examples/nft-nd-nns/nns.yml", testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass) return deployContract(t, e, "../examples/nft-nd-nns/", "../examples/nft-nd-nns/nns.yml", validatorWallet, validatorAddr, validatorPass)
}
func deployVerifyContract(t *testing.T, e *testcli.Executor) util.Uint160 {
return testcli.DeployContract(t, e, "../smartcontract/testdata/verify.go", "../smartcontract/testdata/verify.yml", testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass)
} }

View file

@ -1,4 +1,4 @@
package nep_test package main
import ( import (
"io" "io"
@ -8,7 +8,6 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
@ -18,39 +17,21 @@ import (
) )
func TestNEP17Balance(t *testing.T) { func TestNEP17Balance(t *testing.T) {
e := testcli.NewExecutor(t, true) e := newExecutor(t, true)
args := []string{
"neo-go", "wallet", "nep17", "multitransfer",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet,
"--from", testcli.ValidatorAddr,
"GAS:" + testcli.TestWalletMultiAccount1 + ":1",
"NEO:" + testcli.TestWalletMultiAccount1 + ":10",
"GAS:" + testcli.TestWalletMultiAccount3 + ":3",
"--force",
}
e.In.WriteString("one\r")
e.Run(t, args...)
e.CheckTxPersisted(t)
cmdbalance := []string{"neo-go", "wallet", "nep17", "balance"} cmdbalance := []string{"neo-go", "wallet", "nep17", "balance"}
cmdbase := append(cmdbalance, cmdbase := append(cmdbalance,
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", testcli.TestWalletMultiPath, "--wallet", validatorWallet,
) )
cmd := append(cmdbase, "--address", testcli.TestWalletMultiAccount1) cmd := append(cmdbase, "--address", validatorAddr)
t.Run("excessive parameters", func(t *testing.T) {
e.RunWithError(t, append(cmd, "--token", "NEO", "gas")...)
})
t.Run("NEO", func(t *testing.T) { t.Run("NEO", func(t *testing.T) {
b, index := e.Chain.GetGoverningTokenBalance(testcli.TestWalletMultiAccount1Hash) b, index := e.Chain.GetGoverningTokenBalance(validatorHash)
checkResult := func(t *testing.T) { checkResult := func(t *testing.T) {
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1) e.checkNextLine(t, "^\\s*Account\\s+"+validatorAddr)
e.CheckNextLine(t, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)") e.checkNextLine(t, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+b.String()+"$") e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+b.String()+"$")
e.CheckNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10)) e.checkNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
e.CheckEOF(t) e.checkEOF(t)
} }
t.Run("Alias", func(t *testing.T) { t.Run("Alias", func(t *testing.T) {
e.Run(t, append(cmd, "--token", "NEO")...) e.Run(t, append(cmd, "--token", "NEO")...)
@ -63,55 +44,66 @@ func TestNEP17Balance(t *testing.T) {
}) })
t.Run("GAS", func(t *testing.T) { t.Run("GAS", func(t *testing.T) {
e.Run(t, append(cmd, "--token", "GAS")...) e.Run(t, append(cmd, "--token", "GAS")...)
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1) e.checkNextLine(t, "^\\s*Account\\s+"+validatorAddr)
e.CheckNextLine(t, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)") e.checkNextLine(t, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
b := e.Chain.GetUtilityTokenBalance(testcli.TestWalletMultiAccount1Hash) b := e.Chain.GetUtilityTokenBalance(validatorHash)
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(b.Int64()).String()+"$") e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(b.Int64()).String()+"$")
}) })
t.Run("zero balance of known token", func(t *testing.T) { t.Run("zero balance of known token", func(t *testing.T) {
e.Run(t, append(cmdbase, []string{"--token", "NEO", "--address", testcli.TestWalletMultiAccount2}...)...) e.Run(t, append(cmdbase, []string{"--token", "NEO"}...)...)
e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount2) addr1, err := address.StringToUint160("Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn")
e.CheckNextLine(t, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)") require.NoError(t, err)
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(0).String()+"$") e.checkNextLine(t, "^Account "+address.Uint160ToString(addr1))
e.CheckNextLine(t, "^\\s*Updated:") e.checkNextLine(t, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
e.CheckEOF(t) e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(0).String()+"$")
e.checkNextLine(t, "^\\s*Updated:")
e.checkNextLine(t, "^\\s*$")
}) })
t.Run("all accounts", func(t *testing.T) { t.Run("all accounts", func(t *testing.T) {
e.Run(t, cmdbase...) e.Run(t, cmdbase...)
addr1, err := address.StringToUint160("Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn")
require.NoError(t, err)
e.checkNextLine(t, "^Account "+address.Uint160ToString(addr1))
e.checkNextLine(t, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
balance := e.Chain.GetUtilityTokenBalance(addr1)
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(balance.Int64()).String()+"$")
e.checkNextLine(t, "^\\s*Updated:")
e.checkNextLine(t, "^\\s*$")
e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount1) addr2, err := address.StringToUint160("NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq")
require.NoError(t, err)
e.checkNextLine(t, "^Account "+address.Uint160ToString(addr2))
e.checkNextLine(t, "^\\s*$")
addr3, err := address.StringToUint160("NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP")
require.NoError(t, err)
e.checkNextLine(t, "^Account "+address.Uint160ToString(addr3))
// The order of assets is undefined. // The order of assets is undefined.
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
line := e.GetNextLine(t) line := e.getNextLine(t)
if strings.Contains(line, "GAS") { if strings.Contains(line, "GAS") {
e.CheckLine(t, line, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)") e.checkLine(t, line, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
balance := e.Chain.GetUtilityTokenBalance(testcli.TestWalletMultiAccount1Hash) balance = e.Chain.GetUtilityTokenBalance(addr3)
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(balance.Int64()).String()+"$") e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(balance.Int64()).String()+"$")
e.CheckNextLine(t, "^\\s*Updated:") e.checkNextLine(t, "^\\s*Updated:")
} else { } else {
balance, index := e.Chain.GetGoverningTokenBalance(testcli.TestWalletMultiAccount1Hash) balance, index := e.Chain.GetGoverningTokenBalance(validatorHash)
e.CheckLine(t, line, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)") e.checkLine(t, line, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+balance.String()+"$") e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+balance.String()+"$")
e.CheckNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10)) e.checkNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
} }
} }
e.CheckNextLine(t, "^\\s*$")
e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount2) e.checkNextLine(t, "^\\s*$")
e.CheckNextLine(t, "^\\s*$") addr4, err := address.StringToUint160("NQ3nAdFQXzemHC9uvr4af2Ysap6aZJpqgN") // deployed verify.go contract
require.NoError(t, err)
e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount3) e.checkNextLine(t, "^Account "+address.Uint160ToString(addr4))
e.CheckNextLine(t, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)") e.checkEOF(t)
balance := e.Chain.GetUtilityTokenBalance(testcli.TestWalletMultiAccount3Hash)
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(balance.Int64()).String()+"$")
e.CheckNextLine(t, "^\\s*Updated:")
e.CheckEOF(t)
}) })
t.Run("Bad token", func(t *testing.T) { t.Run("Bad token", func(t *testing.T) {
e.Run(t, append(cmd, "--token", "kek")...) e.Run(t, append(cmd, "--token", "kek")...)
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1) e.checkNextLine(t, "^\\s*Account\\s+"+validatorAddr)
e.CheckNextLine(t, `^\s*Can't find data for "kek" token\s*`) e.checkEOF(t)
e.CheckEOF(t)
}) })
t.Run("Bad wallet", func(t *testing.T) { t.Run("Bad wallet", func(t *testing.T) {
e.RunWithError(t, append(cmdbalance, "--wallet", "/dev/null")...) e.RunWithError(t, append(cmdbalance, "--wallet", "/dev/null")...)
@ -119,18 +111,19 @@ func TestNEP17Balance(t *testing.T) {
} }
func TestNEP17Transfer(t *testing.T) { func TestNEP17Transfer(t *testing.T) {
w, err := wallet.NewWalletFromFile("../testdata/testwallet.json") w, err := wallet.NewWalletFromFile("testdata/testwallet.json")
require.NoError(t, err) require.NoError(t, err)
defer w.Close()
e := testcli.NewExecutor(t, true) e := newExecutor(t, true)
args := []string{ args := []string{
"neo-go", "wallet", "nep17", "transfer", "neo-go", "wallet", "nep17", "transfer",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", testcli.ValidatorWallet, "--wallet", validatorWallet,
"--to", w.Accounts[0].Address, "--to", w.Accounts[0].Address,
"--token", "NEO", "--token", "NEO",
"--amount", "1", "--amount", "1",
"--from", testcli.ValidatorAddr, "--from", validatorAddr,
} }
t.Run("missing receiver", func(t *testing.T) { t.Run("missing receiver", func(t *testing.T) {
@ -161,20 +154,23 @@ func TestNEP17Transfer(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.In.WriteString("Y\r") e.In.WriteString("Y\r")
e.Run(t, args...) e.Run(t, args...)
e.CheckNextLine(t, `^Network fee:\s*(\d|\.)+`) e.checkNextLine(t, `^Network fee:\s*(\d|\.)+`)
e.CheckNextLine(t, `^System fee:\s*(\d|\.)+`) e.checkNextLine(t, `^System fee:\s*(\d|\.)+`)
e.CheckNextLine(t, `^Total fee:\s*(\d|\.)+`) e.checkNextLine(t, `^Total fee:\s*(\d|\.)+`)
e.CheckTxPersisted(t) e.checkTxPersisted(t)
sh := w.Accounts[0].ScriptHash() sh, err := address.StringToUint160(w.Accounts[0].Address)
require.NoError(t, err)
b, _ := e.Chain.GetGoverningTokenBalance(sh) b, _ := e.Chain.GetGoverningTokenBalance(sh)
require.Equal(t, big.NewInt(1), b) require.Equal(t, big.NewInt(1), b)
t.Run("with force", func(t *testing.T) { t.Run("with force", func(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, append(args, "--force")...) e.Run(t, append(args, "--force")...)
e.CheckTxPersisted(t) e.checkTxPersisted(t)
sh, err := address.StringToUint160(w.Accounts[0].Address)
require.NoError(t, err)
b, _ := e.Chain.GetGoverningTokenBalance(sh) b, _ := e.Chain.GetGoverningTokenBalance(sh)
require.Equal(t, big.NewInt(2), b) require.Equal(t, big.NewInt(2), b)
}) })
@ -185,20 +181,22 @@ func TestNEP17Transfer(t *testing.T) {
t.Run("default address", func(t *testing.T) { t.Run("default address", func(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "nep17", "multitransfer", e.Run(t, "neo-go", "wallet", "nep17", "multitransfer",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", testcli.ValidatorWallet, "--wallet", validatorWallet,
"--from", testcli.ValidatorAddr, "--from", validatorAddr,
"--force", "--force",
"NEO:"+validatorDefault+":42", "NEO:"+validatorDefault+":42",
"GAS:"+validatorDefault+":7") "GAS:"+validatorDefault+":7")
e.CheckTxPersisted(t) e.checkTxPersisted(t)
args := args[:len(args)-2] // cut '--from' argument args := args[:len(args)-2] // cut '--from' argument
args = append(args, "--force") args = append(args, "--force")
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, args...) e.Run(t, args...)
e.CheckTxPersisted(t) e.checkTxPersisted(t)
sh, err := address.StringToUint160(w.Accounts[0].Address)
require.NoError(t, err)
b, _ := e.Chain.GetGoverningTokenBalance(sh) b, _ := e.Chain.GetGoverningTokenBalance(sh)
require.Equal(t, big.NewInt(3), b) require.Equal(t, big.NewInt(3), b)
@ -211,70 +209,63 @@ func TestNEP17Transfer(t *testing.T) {
t.Run("with signers", func(t *testing.T) { t.Run("with signers", func(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "nep17", "multitransfer", e.Run(t, "neo-go", "wallet", "nep17", "multitransfer",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", testcli.ValidatorWallet, "--wallet", validatorWallet,
"--from", testcli.ValidatorAddr, "--from", validatorAddr,
"--force", "--force",
"NEO:"+validatorDefault+":42", "NEO:"+validatorDefault+":42",
"GAS:"+validatorDefault+":7", "GAS:"+validatorDefault+":7",
"--", testcli.ValidatorAddr+":Global") "--", validatorAddr+":Global")
e.CheckTxPersisted(t) e.checkTxPersisted(t)
}) })
validTil := e.Chain.BlockHeight() + 100 validTil := e.Chain.BlockHeight() + 100
cmd := []string{ cmd := []string{
"neo-go", "wallet", "nep17", "transfer", "neo-go", "wallet", "nep17", "transfer",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", testcli.ValidatorWallet, "--wallet", validatorWallet,
"--to", address.Uint160ToString(e.Chain.GetNotaryContractScriptHash()),
"--token", "GAS", "--token", "GAS",
"--amount", "1", "--amount", "1",
"--from", validatorAddr,
"--force", "--force",
"--from", testcli.ValidatorAddr} "[", validatorAddr, strconv.Itoa(int(validTil)), "]"}
t.Run("with await", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, append(cmd, "--to", nftOwnerAddr, "--await")...)
e.CheckAwaitableTxPersisted(t)
})
cmd = append(cmd, "--to", address.Uint160ToString(e.Chain.GetNotaryContractScriptHash()),
"[", testcli.ValidatorAddr, strconv.Itoa(int(validTil)), "]")
t.Run("with data", func(t *testing.T) { t.Run("with data", func(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, cmd...) e.Run(t, cmd...)
e.CheckTxPersisted(t) e.checkTxPersisted(t)
}) })
t.Run("with data and signers", func(t *testing.T) { t.Run("with data and signers", func(t *testing.T) {
t.Run("invalid sender's scope", func(t *testing.T) { t.Run("invalid sender's scope", func(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.RunWithError(t, append(cmd, "--", testcli.ValidatorAddr+":None")...) e.RunWithError(t, append(cmd, "--", validatorAddr+":None")...)
}) })
t.Run("good", func(t *testing.T) { t.Run("good", func(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, append(cmd, "--", testcli.ValidatorAddr+":Global")...) // CalledByEntry is enough, but it's the default value, so check something else e.Run(t, append(cmd, "--", validatorAddr+":Global")...) // CalledByEntry is enough, but it's the default value, so check something else
e.CheckTxPersisted(t) e.checkTxPersisted(t)
}) })
t.Run("several signers", func(t *testing.T) { t.Run("several signers", func(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, append(cmd, "--", testcli.ValidatorAddr, hVerify.StringLE())...) e.Run(t, append(cmd, "--", validatorAddr, hVerify.StringLE())...)
e.CheckTxPersisted(t) e.checkTxPersisted(t)
}) })
}) })
} }
func TestNEP17MultiTransfer(t *testing.T) { func TestNEP17MultiTransfer(t *testing.T) {
privs, _ := testcli.GenerateKeys(t, 3) privs, _ := generateKeys(t, 3)
e := testcli.NewExecutor(t, true) e := newExecutor(t, true)
neoContractHash, err := e.Chain.GetNativeContractScriptHash(nativenames.Neo) neoContractHash, err := e.Chain.GetNativeContractScriptHash(nativenames.Neo)
require.NoError(t, err) require.NoError(t, err)
args := []string{ args := []string{
"neo-go", "wallet", "nep17", "multitransfer", "neo-go", "wallet", "nep17", "multitransfer",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", testcli.ValidatorWallet, "--wallet", validatorWallet,
"--from", testcli.ValidatorAddr, "--from", validatorAddr,
"--force", "--force",
"NEO:" + privs[0].Address() + ":42", "NEO:" + privs[0].Address() + ":42",
"GAS:" + privs[1].Address() + ":7", "GAS:" + privs[1].Address() + ":7",
@ -285,7 +276,7 @@ func TestNEP17MultiTransfer(t *testing.T) {
t.Run("no cosigners", func(t *testing.T) { t.Run("no cosigners", func(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, args...) e.Run(t, args...)
e.CheckTxPersisted(t) e.checkTxPersisted(t)
b, _ := e.Chain.GetGoverningTokenBalance(privs[0].GetScriptHash()) b, _ := e.Chain.GetGoverningTokenBalance(privs[0].GetScriptHash())
require.Equal(t, big.NewInt(42), b) require.Equal(t, big.NewInt(42), b)
@ -298,24 +289,24 @@ func TestNEP17MultiTransfer(t *testing.T) {
t.Run("invalid sender scope", func(t *testing.T) { t.Run("invalid sender scope", func(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.RunWithError(t, append(args, e.RunWithError(t, append(args,
"--", testcli.ValidatorAddr+":None")...) // invalid sender scope "--", validatorAddr+":None")...) // invalid sender scope
}) })
t.Run("Global sender scope", func(t *testing.T) { t.Run("Global sender scope", func(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, append(args, e.Run(t, append(args,
"--", testcli.ValidatorAddr+":Global")...) "--", validatorAddr+":Global")...)
e.CheckTxPersisted(t) e.checkTxPersisted(t)
}) })
t.Run("Several cosigners", func(t *testing.T) { t.Run("Several cosigners", func(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, append(args, e.Run(t, append(args,
"--", testcli.ValidatorAddr, hVerify.StringLE())...) "--", validatorAddr, hVerify.StringLE())...)
e.CheckTxPersisted(t) e.checkTxPersisted(t)
}) })
} }
func TestNEP17ImportToken(t *testing.T) { func TestNEP17ImportToken(t *testing.T) {
e := testcli.NewExecutor(t, true) e := newExecutor(t, true)
tmpDir := t.TempDir() tmpDir := t.TempDir()
walletPath := filepath.Join(tmpDir, "walletForImport.json") walletPath := filepath.Join(tmpDir, "walletForImport.json")
@ -328,42 +319,33 @@ func TestNEP17ImportToken(t *testing.T) {
// missing token hash // missing token hash
e.RunWithError(t, "neo-go", "wallet", "nep17", "import", e.RunWithError(t, "neo-go", "wallet", "nep17", "import",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", walletPath) "--wallet", walletPath)
// additional parameter
e.RunWithError(t, "neo-go", "wallet", "nep17", "import",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", walletPath,
"--token", gasContractHash.StringLE(), "useless")
e.Run(t, "neo-go", "wallet", "nep17", "import", e.Run(t, "neo-go", "wallet", "nep17", "import",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", walletPath, "--wallet", walletPath,
"--token", gasContractHash.StringLE()) "--token", gasContractHash.StringLE())
e.Run(t, "neo-go", "wallet", "nep17", "import", e.Run(t, "neo-go", "wallet", "nep17", "import",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", walletPath, "--wallet", walletPath,
"--token", address.Uint160ToString(neoContractHash)) // try address instead of sh "--token", address.Uint160ToString(neoContractHash)) // try address instead of sh
// not a NEP-17 token // not a NEP-17 token
e.RunWithError(t, "neo-go", "wallet", "nep17", "import", e.RunWithError(t, "neo-go", "wallet", "nep17", "import",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", walletPath, "--wallet", walletPath,
"--token", nnsContractHash.StringLE()) "--token", nnsContractHash.StringLE())
t.Run("Info", func(t *testing.T) { t.Run("Info", func(t *testing.T) {
checkGASInfo := func(t *testing.T) { checkGASInfo := func(t *testing.T) {
e.CheckNextLine(t, "^Name:\\s*GasToken") e.checkNextLine(t, "^Name:\\s*GasToken")
e.CheckNextLine(t, "^Symbol:\\s*GAS") e.checkNextLine(t, "^Symbol:\\s*GAS")
e.CheckNextLine(t, "^Hash:\\s*"+gasContractHash.StringLE()) e.checkNextLine(t, "^Hash:\\s*"+gasContractHash.StringLE())
e.CheckNextLine(t, "^Decimals:\\s*8") e.checkNextLine(t, "^Decimals:\\s*8")
e.CheckNextLine(t, "^Address:\\s*"+address.Uint160ToString(gasContractHash)) e.checkNextLine(t, "^Address:\\s*"+address.Uint160ToString(gasContractHash))
e.CheckNextLine(t, "^Standard:\\s*"+string(manifest.NEP17StandardName)) e.checkNextLine(t, "^Standard:\\s*"+string(manifest.NEP17StandardName))
} }
t.Run("excessive parameters", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "nep17", "info",
"--wallet", walletPath, "--token", gasContractHash.StringLE(), "parameter")
})
t.Run("WithToken", func(t *testing.T) { t.Run("WithToken", func(t *testing.T) {
e.Run(t, "neo-go", "wallet", "nep17", "info", e.Run(t, "neo-go", "wallet", "nep17", "info",
"--wallet", walletPath, "--token", gasContractHash.StringLE()) "--wallet", walletPath, "--token", gasContractHash.StringLE())
@ -375,16 +357,14 @@ func TestNEP17ImportToken(t *testing.T) {
checkGASInfo(t) checkGASInfo(t)
_, err := e.Out.ReadString('\n') _, err := e.Out.ReadString('\n')
require.NoError(t, err) require.NoError(t, err)
e.CheckNextLine(t, "^Name:\\s*NeoToken") e.checkNextLine(t, "^Name:\\s*NeoToken")
e.CheckNextLine(t, "^Symbol:\\s*NEO") e.checkNextLine(t, "^Symbol:\\s*NEO")
e.CheckNextLine(t, "^Hash:\\s*"+neoContractHash.StringLE()) e.checkNextLine(t, "^Hash:\\s*"+neoContractHash.StringLE())
e.CheckNextLine(t, "^Decimals:\\s*0") e.checkNextLine(t, "^Decimals:\\s*0")
e.CheckNextLine(t, "^Address:\\s*"+address.Uint160ToString(neoContractHash)) e.checkNextLine(t, "^Address:\\s*"+address.Uint160ToString(neoContractHash))
e.CheckNextLine(t, "^Standard:\\s*"+string(manifest.NEP17StandardName)) e.checkNextLine(t, "^Standard:\\s*"+string(manifest.NEP17StandardName))
}) })
t.Run("Remove", func(t *testing.T) { t.Run("Remove", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "nep17", "remove",
"--wallet", walletPath, "--token", neoContractHash.StringLE(), "add")
e.In.WriteString("y\r") e.In.WriteString("y\r")
e.Run(t, "neo-go", "wallet", "nep17", "remove", e.Run(t, "neo-go", "wallet", "nep17", "remove",
"--wallet", walletPath, "--token", neoContractHash.StringLE()) "--wallet", walletPath, "--token", neoContractHash.StringLE())

View file

@ -1,28 +0,0 @@
package options
import "go.uber.org/zap/zapcore"
// FilteringCore is custom implementation of zapcore.Core that allows to filter
// log entries using custom filtering function.
type FilteringCore struct {
zapcore.Core
filter FilterFunc
}
// FilterFunc is the filter function that is called to check whether the given
// entry together with the associated fields is to be written to a core or not.
type FilterFunc func(zapcore.Entry) bool
// NewFilteringCore returns a core middleware that uses the given filter function
// to decide whether to log this message or not.
func NewFilteringCore(next zapcore.Core, filter FilterFunc) zapcore.Core {
return &FilteringCore{next, filter}
}
// Check implements zapcore.Core interface and performs log entries filtering.
func (c *FilteringCore) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
if c.filter(e) {
return c.Core.Check(e, ce)
}
return ce
}

View file

@ -6,61 +6,26 @@ package options
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"net/url"
"os"
"runtime"
"strconv"
"strings"
"time" "time"
"github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/cli/input"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/rpc/client"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli" "github.com/urfave/cli"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"golang.org/x/term"
"gopkg.in/yaml.v3"
) )
const ( // DefaultTimeout is the default timeout used for RPC requests.
// DefaultTimeout is the default timeout used for RPC requests. const DefaultTimeout = 10 * time.Second
DefaultTimeout = 10 * time.Second
// DefaultAwaitableTimeout is the default timeout used for RPC requests that
// require transaction awaiting. It is set to the approximate time of three
// Neo N3 mainnet blocks accepting.
DefaultAwaitableTimeout = 3 * 15 * time.Second
)
// RPCEndpointFlag is a long flag name for an RPC endpoint. It can be used to // RPCEndpointFlag is a long flag name for RPC endpoint. It can be used to
// check for flag presence in the context. // check for flag presence in the context.
const RPCEndpointFlag = "rpc-endpoint" const RPCEndpointFlag = "rpc-endpoint"
// Wallet is a set of flags used for wallet operations.
var Wallet = []cli.Flag{cli.StringFlag{
Name: "wallet, w",
Usage: "wallet to use to get the key for transaction signing; conflicts with --wallet-config flag",
}, cli.StringFlag{
Name: "wallet-config",
Usage: "path to wallet config to use to get the key for transaction signing; conflicts with --wallet flag"},
}
// Network is a set of flags for choosing the network to operate on // Network is a set of flags for choosing the network to operate on
// (privnet/mainnet/testnet). // (privnet/mainnet/testnet).
var Network = []cli.Flag{ var Network = []cli.Flag{
cli.BoolFlag{Name: "privnet, p", Usage: "use private network configuration (if --config-file option is not specified)"}, cli.BoolFlag{Name: "privnet, p"},
cli.BoolFlag{Name: "mainnet, m", Usage: "use mainnet network configuration (if --config-file option is not specified)"}, cli.BoolFlag{Name: "mainnet, m"},
cli.BoolFlag{Name: "testnet, t", Usage: "use testnet network configuration (if --config-file option is not specified)"}, cli.BoolFlag{Name: "testnet, t"},
cli.BoolFlag{Name: "unittest", Hidden: true}, cli.BoolFlag{Name: "unittest", Hidden: true},
} }
@ -72,47 +37,11 @@ var RPC = []cli.Flag{
}, },
cli.DurationFlag{ cli.DurationFlag{
Name: "timeout, s", Name: "timeout, s",
Value: DefaultTimeout, Usage: "Timeout for the operation (10 seconds by default)",
Usage: "Timeout for the operation",
}, },
} }
// Historic is a flag for commands that can perform historic invocations.
var Historic = cli.StringFlag{
Name: "historic",
Usage: "Use historic state (height, block hash or state root hash)",
}
// Config is a flag for commands that use node configuration.
var Config = cli.StringFlag{
Name: "config-path",
Usage: "path to directory with per-network configuration files (may be overridden by --config-file option for the configuration file)",
}
// ConfigFile is a flag for commands that use node configuration and provide
// path to the specific config file instead of config path.
var ConfigFile = cli.StringFlag{
Name: "config-file",
Usage: "path to the node configuration file (overrides --config-path option)",
}
// RelativePath is a flag for commands that use node configuration and provide
// a prefix to all relative paths in config files.
var RelativePath = cli.StringFlag{
Name: "relative-path",
Usage: "a prefix to all relative paths in the node configuration file",
}
// Debug is a flag for commands that allow node in debug mode usage.
var Debug = cli.BoolFlag{
Name: "debug, d",
Usage: "enable debug logging (LOTS of output, overrides configuration)",
}
var errNoEndpoint = errors.New("no RPC endpoint specified, use option '--" + RPCEndpointFlag + "' or '-r'") var errNoEndpoint = errors.New("no RPC endpoint specified, use option '--" + RPCEndpointFlag + "' or '-r'")
var errInvalidHistoric = errors.New("invalid 'historic' parameter, neither a block number, nor a block/state hash")
var errNoWallet = errors.New("no wallet parameter found, specify it with the '--wallet' or '-w' flag or specify wallet config file with the '--wallet-config' flag")
var errConflictingWalletFlags = errors.New("--wallet flag conflicts with --wallet-config flag, please, provide one of them to specify wallet location")
// GetNetwork examines Context's flags and returns the appropriate network. It // GetNetwork examines Context's flags and returns the appropriate network. It
// defaults to PrivNet if no flags are given. // defaults to PrivNet if no flags are given.
@ -130,25 +59,22 @@ func GetNetwork(ctx *cli.Context) netmode.Magic {
return net return net
} }
// GetTimeoutContext returns a context.Context with the default or a user-set timeout. // GetTimeoutContext returns a context.Context with default of user-set timeout.
func GetTimeoutContext(ctx *cli.Context) (context.Context, func()) { func GetTimeoutContext(ctx *cli.Context) (context.Context, func()) {
dur := ctx.Duration("timeout") dur := ctx.Duration("timeout")
if dur == 0 { if dur == 0 {
dur = DefaultTimeout dur = DefaultTimeout
} }
if !ctx.IsSet("timeout") && ctx.Bool("await") {
dur = DefaultAwaitableTimeout
}
return context.WithTimeout(context.Background(), dur) return context.WithTimeout(context.Background(), dur)
} }
// GetRPCClient returns an RPC client instance for the given Context. // GetRPCClient returns an RPC client instance for the given Context.
func GetRPCClient(gctx context.Context, ctx *cli.Context) (*rpcclient.Client, cli.ExitCoder) { func GetRPCClient(gctx context.Context, ctx *cli.Context) (*client.Client, cli.ExitCoder) {
endpoint := ctx.String(RPCEndpointFlag) endpoint := ctx.String(RPCEndpointFlag)
if len(endpoint) == 0 { if len(endpoint) == 0 {
return nil, cli.NewExitError(errNoEndpoint, 1) return nil, cli.NewExitError(errNoEndpoint, 1)
} }
c, err := rpcclient.New(gctx, endpoint, rpcclient.Options{}) c, err := client.New(gctx, endpoint, client.Options{})
if err != nil { if err != nil {
return nil, cli.NewExitError(err, 1) return nil, cli.NewExitError(err, 1)
} }
@ -158,253 +84,3 @@ func GetRPCClient(gctx context.Context, ctx *cli.Context) (*rpcclient.Client, cl
} }
return c, nil return c, nil
} }
// GetInvoker returns an invoker using the given RPC client, context and signers.
// It parses "--historic" parameter to adjust it.
func GetInvoker(c *rpcclient.Client, ctx *cli.Context, signers []transaction.Signer) (*invoker.Invoker, cli.ExitCoder) {
historic := ctx.String("historic")
if historic == "" {
return invoker.New(c, signers), nil
}
if index, err := strconv.ParseUint(historic, 10, 32); err == nil {
return invoker.NewHistoricAtHeight(uint32(index), c, signers), nil
}
if u256, err := util.Uint256DecodeStringLE(historic); err == nil {
// Might as well be a block hash, but it makes no practical difference.
return invoker.NewHistoricWithState(u256, c, signers), nil
}
return nil, cli.NewExitError(errInvalidHistoric, 1)
}
// GetRPCWithInvoker combines GetRPCClient with GetInvoker for cases where it's
// appropriate to do so.
func GetRPCWithInvoker(gctx context.Context, ctx *cli.Context, signers []transaction.Signer) (*rpcclient.Client, *invoker.Invoker, cli.ExitCoder) {
c, err := GetRPCClient(gctx, ctx)
if err != nil {
return nil, nil, err
}
inv, err := GetInvoker(c, ctx, signers)
if err != nil {
c.Close()
return nil, nil, err
}
return c, inv, err
}
// GetConfigFromContext looks at the path and the mode flags in the given config and
// returns an appropriate config.
func GetConfigFromContext(ctx *cli.Context) (config.Config, error) {
var (
configFile = ctx.String("config-file")
relativePath = ctx.String("relative-path")
)
if len(configFile) != 0 {
return config.LoadFile(configFile, relativePath)
}
var configPath = "./config"
if argCp := ctx.String("config-path"); argCp != "" {
configPath = argCp
}
return config.Load(configPath, GetNetwork(ctx), relativePath)
}
var (
// _winfileSinkRegistered denotes whether zap has registered
// user-supplied factory for all sinks with `winfile`-prefixed scheme.
_winfileSinkRegistered bool
_winfileSinkCloser func() error
)
// HandleLoggingParams reads logging parameters.
// If a user selected debug level -- function enables it.
// If logPath is configured -- function creates a dir and a file for logging.
// If logPath is configured on Windows -- function returns closer to be
// able to close sink for the opened log output file.
// If the program is run in TTY then logger adds timestamp to its entries.
func HandleLoggingParams(debug bool, cfg config.ApplicationConfiguration) (*zap.Logger, *zap.AtomicLevel, func() error, error) {
var (
level = zapcore.InfoLevel
err error
)
if len(cfg.LogLevel) > 0 {
level, err = zapcore.ParseLevel(cfg.LogLevel)
if err != nil {
return nil, nil, nil, fmt.Errorf("log setting: %w", err)
}
}
if debug {
level = zapcore.DebugLevel
}
cc := zap.NewProductionConfig()
cc.DisableCaller = true
cc.DisableStacktrace = true
cc.EncoderConfig.EncodeDuration = zapcore.StringDurationEncoder
cc.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
if term.IsTerminal(int(os.Stdout.Fd())) {
cc.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
} else {
cc.EncoderConfig.EncodeTime = func(t time.Time, encoder zapcore.PrimitiveArrayEncoder) {}
}
cc.Encoding = "console"
cc.Level = zap.NewAtomicLevelAt(level)
cc.Sampling = nil
if logPath := cfg.LogPath; logPath != "" {
if err := io.MakeDirForFile(logPath, "logger"); err != nil {
return nil, nil, nil, err
}
if runtime.GOOS == "windows" {
if !_winfileSinkRegistered {
// See https://github.com/uber-go/zap/issues/621.
err := zap.RegisterSink("winfile", func(u *url.URL) (zap.Sink, error) {
if u.User != nil {
return nil, fmt.Errorf("user and password not allowed with file URLs: got %v", u)
}
if u.Fragment != "" {
return nil, fmt.Errorf("fragments not allowed with file URLs: got %v", u)
}
if u.RawQuery != "" {
return nil, fmt.Errorf("query parameters not allowed with file URLs: got %v", u)
}
// Error messages are better if we check hostname and port separately.
if u.Port() != "" {
return nil, fmt.Errorf("ports not allowed with file URLs: got %v", u)
}
if hn := u.Hostname(); hn != "" && hn != "localhost" {
return nil, fmt.Errorf("file URLs must leave host empty or use localhost: got %v", u)
}
switch u.Path {
case "stdout":
return os.Stdout, nil
case "stderr":
return os.Stderr, nil
}
f, err := os.OpenFile(u.Path[1:], // Remove leading slash left after url.Parse.
os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
_winfileSinkCloser = func() error {
_winfileSinkCloser = nil
return f.Close()
}
return f, err
})
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to register windows-specific sinc: %w", err)
}
_winfileSinkRegistered = true
}
logPath = "winfile:///" + logPath
}
cc.OutputPaths = []string{logPath}
}
log, err := cc.Build()
return log, &cc.Level, _winfileSinkCloser, err
}
// GetRPCWithActor returns an RPC client instance and Actor instance for the given context.
func GetRPCWithActor(gctx context.Context, ctx *cli.Context, signers []actor.SignerAccount) (*rpcclient.Client, *actor.Actor, cli.ExitCoder) {
c, err := GetRPCClient(gctx, ctx)
if err != nil {
return nil, nil, err
}
a, actorErr := actor.New(c, signers)
if actorErr != nil {
c.Close()
return nil, nil, cli.NewExitError(fmt.Errorf("failed to create Actor: %w", actorErr), 1)
}
return c, a, nil
}
// GetAccFromContext returns account and wallet from context. If address is not set, default address is used.
func GetAccFromContext(ctx *cli.Context) (*wallet.Account, *wallet.Wallet, error) {
var addr util.Uint160
wPath := ctx.String("wallet")
walletConfigPath := ctx.String("wallet-config")
if len(wPath) != 0 && len(walletConfigPath) != 0 {
return nil, nil, errConflictingWalletFlags
}
if len(wPath) == 0 && len(walletConfigPath) == 0 {
return nil, nil, errNoWallet
}
var pass *string
if len(walletConfigPath) != 0 {
cfg, err := ReadWalletConfig(walletConfigPath)
if err != nil {
return nil, nil, err
}
wPath = cfg.Path
pass = &cfg.Password
}
wall, err := wallet.NewWalletFromFile(wPath)
if err != nil {
return nil, nil, err
}
addrFlag := ctx.Generic("address").(*flags.Address)
if addrFlag.IsSet {
addr = addrFlag.Uint160()
} else {
addr = wall.GetChangeAddress()
if addr.Equals(util.Uint160{}) {
return nil, wall, errors.New("can't get default address")
}
}
acc, err := GetUnlockedAccount(wall, addr, pass)
return acc, wall, err
}
// GetUnlockedAccount returns account from wallet, address and uses pass to unlock specified account if given.
// If the password is not given, then it is requested from user.
func GetUnlockedAccount(wall *wallet.Wallet, addr util.Uint160, pass *string) (*wallet.Account, error) {
acc := wall.GetAccount(addr)
if acc == nil {
return nil, fmt.Errorf("wallet contains no account for '%s'", address.Uint160ToString(addr))
}
if acc.CanSign() || acc.EncryptedWIF == "" {
return acc, nil
}
if pass == nil {
rawPass, err := input.ReadPassword(
fmt.Sprintf("Enter account %s password > ", address.Uint160ToString(addr)))
if err != nil {
return nil, fmt.Errorf("Error reading password: %w", err)
}
trimmed := strings.TrimRight(string(rawPass), "\n")
pass = &trimmed
}
err := acc.Decrypt(*pass, wall.Scrypt)
if err != nil {
return nil, err
}
return acc, nil
}
// ReadWalletConfig reads wallet config from the given path.
func ReadWalletConfig(configPath string) (*config.Wallet, error) {
file, err := os.Open(configPath)
if err != nil {
return nil, err
}
defer file.Close()
configData, err := os.ReadFile(configPath)
if err != nil {
return nil, fmt.Errorf("unable to read wallet config: %w", err)
}
cfg := &config.Wallet{}
err = yaml.Unmarshal(configData, &cfg)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal wallet config YAML: %w", err)
}
return cfg, nil
}

View file

@ -1,22 +1,20 @@
package options_test package main
import ( import (
"flag" "flag"
"testing" "testing"
"github.com/nspcc-dev/neo-go/cli/app"
"github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
func TestGetRPCClient(t *testing.T) { func TestGetRPCClient(t *testing.T) {
e := testcli.NewExecutor(t, true) e := newExecutor(t, true)
t.Run("no endpoint", func(t *testing.T) { t.Run("no endpoint", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError) set := flag.NewFlagSet("flagSet", flag.ExitOnError)
ctx := cli.NewContext(app.New(), set, nil) ctx := cli.NewContext(cli.NewApp(), set, nil)
gctx, _ := options.GetTimeoutContext(ctx) gctx, _ := options.GetTimeoutContext(ctx)
_, ec := options.GetRPCClient(gctx, ctx) _, ec := options.GetRPCClient(gctx, ctx)
require.Equal(t, 1, ec.ExitCode()) require.Equal(t, 1, ec.ExitCode())
@ -24,8 +22,8 @@ func TestGetRPCClient(t *testing.T) {
t.Run("success", func(t *testing.T) { t.Run("success", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError) set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String(options.RPCEndpointFlag, "http://"+e.RPC.Addresses()[0], "") set.String(options.RPCEndpointFlag, "http://"+e.RPC.Addr, "")
ctx := cli.NewContext(app.New(), set, nil) ctx := cli.NewContext(cli.NewApp(), set, nil)
gctx, _ := options.GetTimeoutContext(ctx) gctx, _ := options.GetTimeoutContext(ctx)
_, ec := options.GetRPCClient(gctx, ctx) _, ec := options.GetRPCClient(gctx, ctx)
require.Nil(t, ec) require.Nil(t, ec)

View file

@ -3,31 +3,40 @@ package paramcontext
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "io/ioutil"
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/context" "github.com/nspcc-dev/neo-go/pkg/smartcontract/context"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
) )
// InitAndSave creates an incompletely signed transaction which can be used // validUntilBlockIncrement is the number of extra blocks to add to an exported transaction.
// as an input to `multisig sign`. If a wallet.Account is given and can sign, const validUntilBlockIncrement = 50
// it's signed as well using it.
// InitAndSave creates incompletely signed transaction which can used
// as input to `multisig sign`.
func InitAndSave(net netmode.Magic, tx *transaction.Transaction, acc *wallet.Account, filename string) error { func InitAndSave(net netmode.Magic, tx *transaction.Transaction, acc *wallet.Account, filename string) error {
scCtx := context.NewParameterContext(context.TransactionType, net, tx) // avoid fast transaction expiration
if acc != nil && acc.CanSign() { tx.ValidUntilBlock += validUntilBlockIncrement
sign := acc.SignHashable(net, tx) priv := acc.PrivateKey()
if err := scCtx.AddSignature(acc.ScriptHash(), acc.Contract, acc.PublicKey(), sign); err != nil { pub := priv.PublicKey()
return fmt.Errorf("can't add signature: %w", err) sign := priv.SignHashable(uint32(net), tx)
} scCtx := context.NewParameterContext("Neo.Network.P2P.Payloads.Transaction", net, tx)
h, err := address.StringToUint160(acc.Address)
if err != nil {
return fmt.Errorf("invalid address: %s", acc.Address)
}
if err := scCtx.AddSignature(h, acc.Contract, pub, sign); err != nil {
return fmt.Errorf("can't add signature: %w", err)
} }
return Save(scCtx, filename) return Save(scCtx, filename)
} }
// Read reads the parameter context from the file. // Read reads parameter context from file.
func Read(filename string) (*context.ParameterContext, error) { func Read(filename string) (*context.ParameterContext, error) {
data, err := os.ReadFile(filename) data, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
return nil, fmt.Errorf("can't read input file: %w", err) return nil, fmt.Errorf("can't read input file: %w", err)
} }
@ -39,11 +48,11 @@ func Read(filename string) (*context.ParameterContext, error) {
return c, nil return c, nil
} }
// Save writes the parameter context to the file. // Save writes parameter context to file.
func Save(c *context.ParameterContext, filename string) error { func Save(c *context.ParameterContext, filename string) error {
if data, err := json.Marshal(c); err != nil { if data, err := json.Marshal(c); err != nil {
return fmt.Errorf("can't marshal transaction: %w", err) return fmt.Errorf("can't marshal transaction: %w", err)
} else if err := os.WriteFile(filename, data, 0644); err != nil { } else if err := ioutil.WriteFile(filename, data, 0644); err != nil {
return fmt.Errorf("can't write transaction to file: %w", err) return fmt.Errorf("can't write transaction to file: %w", err)
} }
return nil return nil

View file

@ -3,25 +3,25 @@ package query
import ( import (
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"encoding/hex"
"fmt" "fmt"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"text/tabwriter" "text/tabwriter"
"github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/cli/flags" "github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neo"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -38,39 +38,34 @@ func NewCommands() []cli.Command {
Usage: "Query data from RPC node", Usage: "Query data from RPC node",
Subcommands: []cli.Command{ Subcommands: []cli.Command{
{ {
Name: "candidates", Name: "candidates",
Usage: "Get candidates and votes", Usage: "Get candidates and votes",
UsageText: "neo-go query candidates -r endpoint [-s timeout]", Action: queryCandidates,
Action: queryCandidates, Flags: options.RPC,
Flags: options.RPC,
}, },
{ {
Name: "committee", Name: "committee",
Usage: "Get committee list", Usage: "Get committee list",
UsageText: "neo-go query committee -r endpoint [-s timeout]", Action: queryCommittee,
Action: queryCommittee, Flags: options.RPC,
Flags: options.RPC,
}, },
{ {
Name: "height", Name: "height",
Usage: "Get node height", Usage: "Get node height",
UsageText: "neo-go query height -r endpoint [-s timeout]", Action: queryHeight,
Action: queryHeight, Flags: options.RPC,
Flags: options.RPC,
}, },
{ {
Name: "tx", Name: "tx",
Usage: "Query transaction status", Usage: "Query transaction status",
UsageText: "neo-go query tx <hash> -r endpoint [-s timeout] [-v]", Action: queryTx,
Action: queryTx, Flags: queryTxFlags,
Flags: queryTxFlags,
}, },
{ {
Name: "voter", Name: "voter",
Usage: "Print NEO holder account state", Usage: "Print NEO holder account state",
UsageText: "neo-go query voter <address> -r endpoint [-s timeout]", Action: queryVoter,
Action: queryVoter, Flags: options.RPC,
Flags: options.RPC,
}, },
}, },
}} }}
@ -80,8 +75,6 @@ func queryTx(ctx *cli.Context) error {
args := ctx.Args() args := ctx.Args()
if len(args) == 0 { if len(args) == 0 {
return cli.NewExitError("Transaction hash is missing", 1) return cli.NewExitError("Transaction hash is missing", 1)
} else if len(args) > 1 {
return cli.NewExitError("only one transaction hash is accepted", 1)
} }
txHash, err := util.Uint256DecodeStringLE(strings.TrimPrefix(args[0], "0x")) txHash, err := util.Uint256DecodeStringLE(strings.TrimPrefix(args[0], "0x"))
@ -110,10 +103,7 @@ func queryTx(ctx *cli.Context) error {
} }
} }
err = DumpApplicationLog(ctx, res, &txOut.Transaction, &txOut.TransactionMetadata, ctx.Bool("verbose")) DumpApplicationLog(ctx, res, &txOut.Transaction, &txOut.TransactionMetadata, ctx.Bool("verbose"))
if err != nil {
return cli.NewExitError(err, 1)
}
return nil return nil
} }
@ -122,58 +112,52 @@ func DumpApplicationLog(
res *result.ApplicationLog, res *result.ApplicationLog,
tx *transaction.Transaction, tx *transaction.Transaction,
txMeta *result.TransactionMetadata, txMeta *result.TransactionMetadata,
verbose bool) error { verbose bool) {
var buf []byte buf := bytes.NewBuffer(nil)
buf = fmt.Appendf(buf, "Hash:\t%s\n", tx.Hash().StringLE()) // Ignore the errors below because `Write` to buffer doesn't return error.
buf = fmt.Appendf(buf, "OnChain:\t%t\n", res != nil) tw := tabwriter.NewWriter(buf, 0, 4, 4, '\t', 0)
_, _ = tw.Write([]byte("Hash:\t" + tx.Hash().StringLE() + "\n"))
_, _ = tw.Write([]byte(fmt.Sprintf("OnChain:\t%t\n", res != nil)))
if res == nil { if res == nil {
buf = fmt.Appendf(buf, "ValidUntil:\t%s\n", strconv.FormatUint(uint64(tx.ValidUntilBlock), 10)) _, _ = tw.Write([]byte("ValidUntil:\t" + strconv.FormatUint(uint64(tx.ValidUntilBlock), 10) + "\n"))
} else { } else {
if txMeta != nil { if txMeta != nil {
buf = fmt.Appendf(buf, "BlockHash:\t%s\n", txMeta.Blockhash.StringLE()) _, _ = tw.Write([]byte("BlockHash:\t" + txMeta.Blockhash.StringLE() + "\n"))
} }
if len(res.Executions) != 1 { if len(res.Executions) != 1 {
buf = fmt.Appendf(buf, "Success:\tunknown (no execution data)\n") _, _ = tw.Write([]byte("Success:\tunknown (no execution data)\n"))
} else { } else {
buf = fmt.Appendf(buf, "Success:\t%t\n", res.Executions[0].VMState == vmstate.Halt) _, _ = tw.Write([]byte(fmt.Sprintf("Success:\t%t\n", res.Executions[0].VMState == vm.HaltState)))
} }
} }
if verbose { if verbose {
for _, sig := range tx.Signers { for _, sig := range tx.Signers {
buf = fmt.Appendf(buf, "Signer:\t%s (%s)\n", address.Uint160ToString(sig.Account), sig.Scopes) _, _ = tw.Write([]byte(fmt.Sprintf("Signer:\t%s (%s)",
address.Uint160ToString(sig.Account),
sig.Scopes) + "\n"))
} }
buf = fmt.Appendf(buf, "SystemFee:\t%s GAS\n", fixedn.Fixed8(tx.SystemFee).String()) _, _ = tw.Write([]byte("SystemFee:\t" + fixedn.Fixed8(tx.SystemFee).String() + " GAS\n"))
buf = fmt.Appendf(buf, "NetworkFee:\t%s GAS\n", fixedn.Fixed8(tx.NetworkFee).String()) _, _ = tw.Write([]byte("NetworkFee:\t" + fixedn.Fixed8(tx.NetworkFee).String() + " GAS\n"))
buf = fmt.Appendf(buf, "Script:\t%s\n", base64.StdEncoding.EncodeToString(tx.Script)) _, _ = tw.Write([]byte("Script:\t" + base64.StdEncoding.EncodeToString(tx.Script) + "\n"))
v := vm.New() v := vm.New()
v.Load(tx.Script) v.Load(tx.Script)
opts := bytes.NewBuffer(nil) v.PrintOps(tw)
v.PrintOps(opts)
buf = append(buf, opts.Bytes()...)
if res != nil { if res != nil {
for _, e := range res.Executions { for _, e := range res.Executions {
if e.VMState != vmstate.Halt { if e.VMState != vm.HaltState {
buf = fmt.Appendf(buf, "Exception:\t%s\n", e.FaultException) _, _ = tw.Write([]byte("Exception:\t" + e.FaultException + "\n"))
} }
} }
} }
} }
tw := tabwriter.NewWriter(ctx.App.Writer, 0, 4, 4, '\t', 0) _ = tw.Flush()
_, err := tw.Write(buf) fmt.Fprint(ctx.App.Writer, buf.String())
if err != nil {
return err
}
return tw.Flush()
} }
func queryCandidates(ctx *cli.Context) error { func queryCandidates(ctx *cli.Context) error {
var err error var err error
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel() defer cancel()
@ -182,7 +166,7 @@ func queryCandidates(ctx *cli.Context) error {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
vals, err := c.GetCandidates() vals, err := c.GetNextBlockValidators()
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
@ -200,26 +184,20 @@ func queryCandidates(ctx *cli.Context) error {
} }
return vals[i].PublicKey.Cmp(&vals[j].PublicKey) == -1 return vals[i].PublicKey.Cmp(&vals[j].PublicKey) == -1
}) })
var res []byte buf := bytes.NewBuffer(nil)
res = fmt.Appendf(res, "Key\tVotes\tCommittee\tConsensus\n") tw := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0)
_, _ = tw.Write([]byte("Key\tVotes\tCommittee\tConsensus\n"))
for _, val := range vals { for _, val := range vals {
res = fmt.Appendf(res, "%s\t%d\t%t\t%t\n", val.PublicKey.StringCompressed(), val.Votes, comm.Contains(&val.PublicKey), val.Active) _, _ = tw.Write([]byte(fmt.Sprintf("%s\t%d\t%t\t%t\n", hex.EncodeToString(val.PublicKey.Bytes()), val.Votes, comm.Contains(&val.PublicKey), val.Active)))
} }
tw := tabwriter.NewWriter(ctx.App.Writer, 0, 2, 2, ' ', 0) _ = tw.Flush()
_, err = tw.Write(res) fmt.Fprint(ctx.App.Writer, buf.String())
if err != nil { return nil
return err
}
return tw.Flush()
} }
func queryCommittee(ctx *cli.Context) error { func queryCommittee(ctx *cli.Context) error {
var err error var err error
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel() defer cancel()
@ -234,7 +212,7 @@ func queryCommittee(ctx *cli.Context) error {
} }
for _, k := range comm { for _, k := range comm {
fmt.Fprintln(ctx.App.Writer, k.StringCompressed()) fmt.Fprintln(ctx.App.Writer, hex.EncodeToString(k.Bytes()))
} }
return nil return nil
} }
@ -242,10 +220,6 @@ func queryCommittee(ctx *cli.Context) error {
func queryHeight(ctx *cli.Context) error { func queryHeight(ctx *cli.Context) error {
var err error var err error
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel() defer cancel()
@ -274,8 +248,6 @@ func queryVoter(ctx *cli.Context) error {
args := ctx.Args() args := ctx.Args()
if len(args) == 0 { if len(args) == 0 {
return cli.NewExitError("No address specified", 1) return cli.NewExitError("No address specified", 1)
} else if len(args) > 1 {
return cli.NewExitError("this command only accepts one address", 1)
} }
addr, err := flags.ParseAddress(args[0]) addr, err := flags.ParseAddress(args[0])
@ -290,22 +262,39 @@ func queryVoter(ctx *cli.Context) error {
return exitErr return exitErr
} }
neoToken := neo.NewReader(invoker.New(c, nil)) neoHash, err := c.GetNativeContractHash(nativenames.Neo)
if err != nil {
st, err := neoToken.GetAccountState(addr) return cli.NewExitError(fmt.Errorf("failed to get NEO contract hash: %w", err), 1)
}
res, err := c.InvokeFunction(neoHash, "getAccountState", []smartcontract.Parameter{
{
Type: smartcontract.Hash160Type,
Value: addr,
},
}, nil)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
if st == nil { if res.State != "HALT" {
st = new(state.NEOBalance) return cli.NewExitError(fmt.Errorf("invocation failed: %s", res.FaultException), 1)
} }
dec, err := neoToken.Decimals() if len(res.Stack) == 0 {
return cli.NewExitError("result stack is empty", 1)
}
st := new(state.NEOBalance)
if _, ok := res.Stack[0].(stackitem.Null); !ok {
err = st.FromStackItem(res.Stack[0])
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to convert account state from stackitem: %w", err), 1)
}
}
dec, err := c.NEP17Decimals(neoHash)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to get decimals: %w", err), 1) return cli.NewExitError(fmt.Errorf("failed to get decimals: %w", err), 1)
} }
voted := "null" voted := "null"
if st.VoteTo != nil { if st.VoteTo != nil {
voted = fmt.Sprintf("%s (%s)", st.VoteTo.StringCompressed(), address.Uint160ToString(st.VoteTo.GetScriptHash())) voted = fmt.Sprintf("%s (%s)", hex.EncodeToString(st.VoteTo.Bytes()), address.Uint160ToString(st.VoteTo.GetScriptHash()))
} }
fmt.Fprintf(ctx.App.Writer, "\tVoted: %s\n", voted) fmt.Fprintf(ctx.App.Writer, "\tVoted: %s\n", voted)
fmt.Fprintf(ctx.App.Writer, "\tAmount : %s\n", fixedn.ToString(&st.Balance, int(dec))) fmt.Fprintf(ctx.App.Writer, "\tAmount : %s\n", fixedn.ToString(&st.Balance, int(dec)))

View file

@ -1,155 +0,0 @@
package query_test
import (
"encoding/base64"
"fmt"
"regexp"
"strconv"
"strings"
"testing"
"time"
"github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require"
)
func TestQueryTx(t *testing.T) {
e := testcli.NewExecutorSuspended(t)
w, err := wallet.NewWalletFromFile("../testdata/testwallet.json")
require.NoError(t, err)
transferArgs := []string{
"neo-go", "wallet", "nep17", "transfer",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet,
"--to", w.Accounts[0].Address,
"--token", "NEO",
"--from", testcli.ValidatorAddr,
"--force",
}
e.In.WriteString("one\r")
e.Run(t, append(transferArgs, "--amount", "1")...)
line := e.GetNextLine(t)
txHash, err := util.Uint256DecodeStringLE(line)
require.NoError(t, err)
tx, ok := e.Chain.GetMemPool().TryGetValue(txHash)
require.True(t, ok)
args := []string{"neo-go", "query", "tx", "--rpc-endpoint", "http://" + e.RPC.Addresses()[0]}
e.Run(t, append(args, txHash.StringLE())...)
e.CheckNextLine(t, `Hash:\s+`+txHash.StringLE())
e.CheckNextLine(t, `OnChain:\s+false`)
e.CheckNextLine(t, `ValidUntil:\s+`+strconv.FormatUint(uint64(tx.ValidUntilBlock), 10))
e.CheckEOF(t)
go e.Chain.Run()
require.Eventually(t, func() bool { _, aerErr := e.Chain.GetAppExecResults(txHash, trigger.Application); return aerErr == nil }, time.Second*2, time.Millisecond*50)
e.Run(t, append(args, txHash.StringLE())...)
e.CheckNextLine(t, `Hash:\s+`+txHash.StringLE())
e.CheckNextLine(t, `OnChain:\s+true`)
_, height, err := e.Chain.GetTransaction(txHash)
require.NoError(t, err)
e.CheckNextLine(t, `BlockHash:\s+`+e.Chain.GetHeaderHash(height).StringLE())
e.CheckNextLine(t, `Success:\s+true`)
e.CheckEOF(t)
t.Run("verbose", func(t *testing.T) {
e.Run(t, append(args, "--verbose", txHash.StringLE())...)
compareQueryTxVerbose(t, e, tx)
t.Run("FAULT", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, "neo-go", "contract", "invokefunction",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet,
"--address", testcli.ValidatorAddr,
"--force",
random.Uint160().StringLE(),
"randomMethod")
e.CheckNextLine(t, `Warning:`)
e.CheckNextLine(t, "Sending transaction")
line := strings.TrimPrefix(e.GetNextLine(t), "Sent invocation transaction ")
txHash, err := util.Uint256DecodeStringLE(line)
require.NoError(t, err)
require.Eventually(t, func() bool { _, aerErr := e.Chain.GetAppExecResults(txHash, trigger.Application); return aerErr == nil }, time.Second*2, time.Millisecond*50)
tx, _, err := e.Chain.GetTransaction(txHash)
require.NoError(t, err)
e.Run(t, append(args, "--verbose", txHash.StringLE())...)
compareQueryTxVerbose(t, e, tx)
})
})
t.Run("invalid", func(t *testing.T) {
t.Run("missing tx argument", func(t *testing.T) {
e.RunWithError(t, args...)
})
t.Run("excessive arguments", func(t *testing.T) {
e.RunWithError(t, append(args, txHash.StringLE(), txHash.StringLE())...)
})
t.Run("invalid hash", func(t *testing.T) {
e.RunWithError(t, append(args, "notahash")...)
})
t.Run("good hash, missing tx", func(t *testing.T) {
e.RunWithError(t, append(args, random.Uint256().StringLE())...)
})
})
}
func compareQueryTxVerbose(t *testing.T, e *testcli.Executor, tx *transaction.Transaction) {
e.CheckNextLine(t, `Hash:\s+`+tx.Hash().StringLE())
e.CheckNextLine(t, `OnChain:\s+true`)
_, height, err := e.Chain.GetTransaction(tx.Hash())
require.NoError(t, err)
e.CheckNextLine(t, `BlockHash:\s+`+e.Chain.GetHeaderHash(height).StringLE())
res, _ := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
e.CheckNextLine(t, fmt.Sprintf(`Success:\s+%t`, res[0].Execution.VMState == vmstate.Halt))
for _, s := range tx.Signers {
e.CheckNextLine(t, fmt.Sprintf(`Signer:\s+%s\s*\(%s\)`, address.Uint160ToString(s.Account), s.Scopes.String()))
}
e.CheckNextLine(t, `SystemFee:\s+`+fixedn.Fixed8(tx.SystemFee).String()+" GAS$")
e.CheckNextLine(t, `NetworkFee:\s+`+fixedn.Fixed8(tx.NetworkFee).String()+" GAS$")
e.CheckNextLine(t, `Script:\s+`+regexp.QuoteMeta(base64.StdEncoding.EncodeToString(tx.Script)))
c := vm.NewContext(tx.Script)
n := 0
for ; c.NextIP() < c.LenInstr(); _, _, err = c.Next() {
require.NoError(t, err)
n++
}
e.CheckScriptDump(t, n)
if res[0].Execution.VMState != vmstate.Halt {
e.CheckNextLine(t, `Exception:\s+`+regexp.QuoteMeta(res[0].Execution.FaultException))
}
e.CheckEOF(t)
}
func TestQueryHeight(t *testing.T) {
e := testcli.NewExecutor(t, true)
args := []string{"neo-go", "query", "height", "--rpc-endpoint", "http://" + e.RPC.Addresses()[0]}
e.Run(t, args...)
e.CheckNextLine(t, `^Latest block: [0-9]+$`)
e.CheckNextLine(t, `^Validated state: [0-9]+$`)
e.CheckEOF(t)
t.Run("excessive arguments", func(t *testing.T) {
e.RunWithError(t, append(args, "something")...)
})
}

149
cli/query_test.go Normal file
View file

@ -0,0 +1,149 @@
package main
import (
"encoding/base64"
"fmt"
"regexp"
"strconv"
"strings"
"testing"
"time"
"github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require"
)
func TestQueryTx(t *testing.T) {
e := newExecutorSuspended(t)
w, err := wallet.NewWalletFromFile("testdata/testwallet.json")
require.NoError(t, err)
defer w.Close()
transferArgs := []string{
"neo-go", "wallet", "nep17", "transfer",
"--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", validatorWallet,
"--to", w.Accounts[0].Address,
"--token", "NEO",
"--from", validatorAddr,
"--force",
}
e.In.WriteString("one\r")
e.Run(t, append(transferArgs, "--amount", "1")...)
line := e.getNextLine(t)
txHash, err := util.Uint256DecodeStringLE(line)
require.NoError(t, err)
tx, ok := e.Chain.GetMemPool().TryGetValue(txHash)
require.True(t, ok)
args := []string{"neo-go", "query", "tx", "--rpc-endpoint", "http://" + e.RPC.Addr}
e.Run(t, append(args, txHash.StringLE())...)
e.checkNextLine(t, `Hash:\s+`+txHash.StringLE())
e.checkNextLine(t, `OnChain:\s+false`)
e.checkNextLine(t, `ValidUntil:\s+`+strconv.FormatUint(uint64(tx.ValidUntilBlock), 10))
e.checkEOF(t)
height := e.Chain.BlockHeight()
go e.Chain.Run()
require.Eventually(t, func() bool { return e.Chain.BlockHeight() > height }, time.Second*2, time.Millisecond*50)
e.Run(t, append(args, txHash.StringLE())...)
e.checkNextLine(t, `Hash:\s+`+txHash.StringLE())
e.checkNextLine(t, `OnChain:\s+true`)
_, height, err = e.Chain.GetTransaction(txHash)
require.NoError(t, err)
e.checkNextLine(t, `BlockHash:\s+`+e.Chain.GetHeaderHash(int(height)).StringLE())
e.checkNextLine(t, `Success:\s+true`)
e.checkEOF(t)
t.Run("verbose", func(t *testing.T) {
e.Run(t, append(args, "--verbose", txHash.StringLE())...)
e.compareQueryTxVerbose(t, tx)
t.Run("FAULT", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, "neo-go", "contract", "invokefunction",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet,
"--address", validatorAddr,
"--force",
random.Uint160().StringLE(),
"randomMethod")
e.checkNextLine(t, `Warning:`)
e.checkNextLine(t, "Sending transaction")
line := strings.TrimPrefix(e.getNextLine(t), "Sent invocation transaction ")
txHash, err := util.Uint256DecodeStringLE(line)
require.NoError(t, err)
height := e.Chain.BlockHeight()
require.Eventually(t, func() bool { return e.Chain.BlockHeight() > height }, time.Second*2, time.Millisecond*50)
tx, _, err := e.Chain.GetTransaction(txHash)
require.NoError(t, err)
e.Run(t, append(args, "--verbose", txHash.StringLE())...)
e.compareQueryTxVerbose(t, tx)
})
})
t.Run("invalid", func(t *testing.T) {
t.Run("missing tx argument", func(t *testing.T) {
e.RunWithError(t, args...)
})
t.Run("invalid hash", func(t *testing.T) {
e.RunWithError(t, append(args, "notahash")...)
})
t.Run("good hash, missing tx", func(t *testing.T) {
e.RunWithError(t, append(args, random.Uint256().StringLE())...)
})
})
}
func (e *executor) compareQueryTxVerbose(t *testing.T, tx *transaction.Transaction) {
e.checkNextLine(t, `Hash:\s+`+tx.Hash().StringLE())
e.checkNextLine(t, `OnChain:\s+true`)
_, height, err := e.Chain.GetTransaction(tx.Hash())
require.NoError(t, err)
e.checkNextLine(t, `BlockHash:\s+`+e.Chain.GetHeaderHash(int(height)).StringLE())
res, _ := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
e.checkNextLine(t, fmt.Sprintf(`Success:\s+%t`, res[0].Execution.VMState == vm.HaltState))
for _, s := range tx.Signers {
e.checkNextLine(t, fmt.Sprintf(`Signer:\s+%s\s*\(%s\)`, address.Uint160ToString(s.Account), s.Scopes.String()))
}
e.checkNextLine(t, `SystemFee:\s+`+fixedn.Fixed8(tx.SystemFee).String()+" GAS$")
e.checkNextLine(t, `NetworkFee:\s+`+fixedn.Fixed8(tx.NetworkFee).String()+" GAS$")
e.checkNextLine(t, `Script:\s+`+regexp.QuoteMeta(base64.StdEncoding.EncodeToString(tx.Script)))
c := vm.NewContext(tx.Script)
n := 0
for ; c.NextIP() < c.LenInstr(); _, _, err = c.Next() {
require.NoError(t, err)
n++
}
e.checkScriptDump(t, n)
if res[0].Execution.VMState != vm.HaltState {
e.checkNextLine(t, `Exception:\s+`+regexp.QuoteMeta(res[0].Execution.FaultException))
}
e.checkEOF(t)
}
func TestQueryHeight(t *testing.T) {
e := newExecutor(t, true)
e.Run(t, "neo-go", "query", "height", "--rpc-endpoint", "http://"+e.RPC.Addr)
e.checkNextLine(t, `^Latest block: [0-9]+$`)
e.checkNextLine(t, `^Validated state: [0-9]+$`)
e.checkEOF(t)
}

View file

@ -1,158 +0,0 @@
package server_test
import (
"os"
"path/filepath"
"strconv"
"testing"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
)
// generated via `go run ./scripts/gendump/main.go --out ./cli/server/testdata/chain50x2.acc --blocks 50 --txs 2`.
const inDump = "./testdata/chain50x2.acc"
func TestDBRestoreDump(t *testing.T) {
tmpDir := t.TempDir()
loadConfig := func(t *testing.T) config.Config {
chainPath := filepath.Join(tmpDir, "neogotestchain")
cfg, err := config.LoadFile(filepath.Join("..", "..", "config", "protocol.unit_testnet.yml"))
require.NoError(t, err, "could not load config")
cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB
cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath = chainPath
return cfg
}
cfg := loadConfig(t)
out, err := yaml.Marshal(cfg)
require.NoError(t, err)
cfgPath := filepath.Join(tmpDir, "protocol.unit_testnet.yml")
require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm))
e := testcli.NewExecutor(t, false)
stateDump := filepath.Join(tmpDir, "neogo.teststate")
baseArgs := []string{"neo-go", "db", "restore", "--unittest",
"--config-path", tmpDir, "--in", inDump, "--dump", stateDump}
t.Run("excessive restore parameters", func(t *testing.T) {
e.RunWithError(t, append(baseArgs, "something")...)
})
// First 15 blocks.
e.Run(t, append(baseArgs, "--count", "15")...)
// Big count.
e.RunWithError(t, append(baseArgs, "--count", "1000")...)
// Continue 15..25
e.Run(t, append(baseArgs, "--count", "10")...)
// Continue till end.
e.Run(t, baseArgs...)
// Dump and compare.
dumpPath := filepath.Join(tmpDir, "testdump.acc")
t.Run("missing config", func(t *testing.T) {
e.RunWithError(t, "neo-go", "db", "dump", "--privnet",
"--config-path", tmpDir, "--out", dumpPath)
})
t.Run("bad logger config", func(t *testing.T) {
badConfigDir := t.TempDir()
logfile := filepath.Join(badConfigDir, "logdir")
require.NoError(t, os.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm))
cfg = loadConfig(t)
cfg.ApplicationConfiguration.LogPath = filepath.Join(logfile, "file.log")
out, err = yaml.Marshal(cfg)
require.NoError(t, err)
cfgPath = filepath.Join(badConfigDir, "protocol.unit_testnet.yml")
require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm))
e.RunWithError(t, "neo-go", "db", "dump", "--unittest",
"--config-path", badConfigDir, "--out", dumpPath)
})
t.Run("bad storage config", func(t *testing.T) {
badConfigDir := t.TempDir()
logfile := filepath.Join(badConfigDir, "logdir")
require.NoError(t, os.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm))
cfg = loadConfig(t)
cfg.ApplicationConfiguration.DBConfiguration.Type = ""
out, err = yaml.Marshal(cfg)
require.NoError(t, err)
cfgPath = filepath.Join(badConfigDir, "protocol.unit_testnet.yml")
require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm))
e.RunWithError(t, "neo-go", "db", "dump", "--unittest",
"--config-path", badConfigDir, "--out", dumpPath)
})
baseCmd := []string{"neo-go", "db", "dump", "--unittest",
"--config-path", tmpDir, "--out", dumpPath}
t.Run("invalid start/count", func(t *testing.T) {
e.RunWithError(t, append(baseCmd, "--start", "5", "--count", strconv.Itoa(50-5+1+1))...)
})
t.Run("excessive dump parameters", func(t *testing.T) {
e.RunWithError(t, append(baseCmd, "something")...)
})
e.Run(t, baseCmd...)
d1, err := os.ReadFile(inDump)
require.NoError(t, err)
d2, err := os.ReadFile(dumpPath)
require.NoError(t, err)
require.Equal(t, d1, d2, "dumps differ")
}
func TestDBDumpRestoreIncremental(t *testing.T) {
tmpDir := t.TempDir()
chainPath := filepath.Join(tmpDir, "neogotestchain")
nonincDump := filepath.Join(tmpDir, "nonincDump.acc")
incDump := filepath.Join(tmpDir, "incDump.acc")
cfg, err := config.LoadFile(filepath.Join("..", "..", "config", "protocol.unit_testnet.yml"))
require.NoError(t, err, "could not load config")
cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB
cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath = chainPath
out, err := yaml.Marshal(cfg)
require.NoError(t, err)
cfgPath := filepath.Join(tmpDir, "protocol.unit_testnet.yml")
require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm))
e := testcli.NewExecutor(t, false)
// Create DB from dump.
e.Run(t, "neo-go", "db", "restore", "--unittest", "--config-path", tmpDir, "--in", inDump)
// Create two dumps: non-incremental and incremental.
dumpBaseArgs := []string{"neo-go", "db", "dump", "--unittest",
"--config-path", tmpDir}
// Dump first 15 blocks to a non-incremental dump.
e.Run(t, append(dumpBaseArgs, "--out", nonincDump, "--count", "15")...)
// Dump second 15 blocks to an incremental dump.
e.Run(t, append(dumpBaseArgs, "--out", incDump, "--start", "15", "--count", "15")...)
// Clean the DB.
require.NoError(t, os.RemoveAll(chainPath))
// Restore chain from two dumps.
restoreBaseArgs := []string{"neo-go", "db", "restore", "--unittest", "--config-path", tmpDir}
// Restore first 15 blocks from non-incremental dump.
e.Run(t, append(restoreBaseArgs, "--in", nonincDump)...)
// Restore second 15 blocks from incremental dump.
e.Run(t, append(restoreBaseArgs, "--in", incDump, "-n", "--count", "15")...)
}

View file

@ -3,19 +3,19 @@ package server
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dboper"
) )
type dump []blockDump type dump []blockDump
type blockDump struct { type blockDump struct {
Block uint32 `json:"block"` Block uint32 `json:"block"`
Size int `json:"size"` Size int `json:"size"`
Storage []dboper.Operation `json:"storage"` Storage []storage.Operation `json:"storage"`
} }
func newDump() *dump { func newDump() *dump {
@ -63,7 +63,7 @@ func (d *dump) tryPersist(prefix string, index uint32) error {
} }
func readFile(path string) (*dump, error) { func readFile(path string) (*dump, error) {
data, err := os.ReadFile(path) data, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -4,15 +4,14 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"net/url"
"os" "os"
"os/signal" "os/signal"
"runtime"
"syscall" "syscall"
"time"
"github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/consensus" "github.com/nspcc-dev/neo-go/pkg/consensus"
"github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core"
"github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
@ -22,24 +21,32 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/network" "github.com/nspcc-dev/neo-go/pkg/network"
"github.com/nspcc-dev/neo-go/pkg/services/metrics" "github.com/nspcc-dev/neo-go/pkg/network/metrics"
"github.com/nspcc-dev/neo-go/pkg/rpc/server"
"github.com/nspcc-dev/neo-go/pkg/services/notary" "github.com/nspcc-dev/neo-go/pkg/services/notary"
"github.com/nspcc-dev/neo-go/pkg/services/oracle" "github.com/nspcc-dev/neo-go/pkg/services/oracle"
"github.com/nspcc-dev/neo-go/pkg/services/rpcsrv"
"github.com/nspcc-dev/neo-go/pkg/services/stateroot" "github.com/nspcc-dev/neo-go/pkg/services/stateroot"
"github.com/urfave/cli" "github.com/urfave/cli"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
) )
var (
// _winfileSinkRegistered denotes whether zap has registered
// user-supplied factory for all sinks with `winfile`-prefixed scheme.
_winfileSinkRegistered bool
_winfileSinkCloser func() error
)
// NewCommands returns 'node' command. // NewCommands returns 'node' command.
func NewCommands() []cli.Command { func NewCommands() []cli.Command {
cfgFlags := []cli.Flag{options.Config, options.ConfigFile, options.RelativePath} var cfgFlags = []cli.Flag{
cli.StringFlag{Name: "config-path"},
cli.BoolFlag{Name: "debug, d"},
}
cfgFlags = append(cfgFlags, options.Network...) cfgFlags = append(cfgFlags, options.Network...)
var cfgWithCountFlags = make([]cli.Flag, len(cfgFlags)) var cfgWithCountFlags = make([]cli.Flag, len(cfgFlags))
copy(cfgWithCountFlags, cfgFlags) copy(cfgWithCountFlags, cfgFlags)
cfgFlags = append(cfgFlags, options.Debug)
cfgWithCountFlags = append(cfgWithCountFlags, cfgWithCountFlags = append(cfgWithCountFlags,
cli.UintFlag{ cli.UintFlag{
Name: "count, c", Name: "count, c",
@ -74,45 +81,28 @@ func NewCommands() []cli.Command {
Usage: "use if dump is incremental", Usage: "use if dump is incremental",
}, },
) )
var cfgHeightFlags = make([]cli.Flag, len(cfgFlags)+1)
copy(cfgHeightFlags, cfgFlags)
cfgHeightFlags[len(cfgHeightFlags)-1] = cli.UintFlag{
Name: "height",
Usage: "Height of the state to reset DB to",
Required: true,
}
return []cli.Command{ return []cli.Command{
{ {
Name: "node", Name: "node",
Usage: "start a NeoGo node", Usage: "start a NEO node",
UsageText: "neo-go node [--config-path path] [-d] [-p/-m/-t] [--config-file file]", Action: startServer,
Action: startServer, Flags: cfgFlags,
Flags: cfgFlags,
}, },
{ {
Name: "db", Name: "db",
Usage: "database manipulations", Usage: "database manipulations",
Subcommands: []cli.Command{ Subcommands: []cli.Command{
{ {
Name: "dump", Name: "dump",
Usage: "dump blocks (starting with block #1) to the file", Usage: "dump blocks (starting with block #1) to the file",
UsageText: "neo-go db dump -o file [-s start] [-c count] [--config-path path] [-p/-m/-t] [--config-file file]", Action: dumpDB,
Action: dumpDB, Flags: cfgCountOutFlags,
Flags: cfgCountOutFlags,
}, },
{ {
Name: "restore", Name: "restore",
Usage: "restore blocks from the file", Usage: "restore blocks from the file",
UsageText: "neo-go db restore -i file [--dump] [-n] [-c count] [--config-path path] [-p/-m/-t] [--config-file file]", Action: restoreDB,
Action: restoreDB, Flags: cfgCountInFlags,
Flags: cfgCountInFlags,
},
{
Name: "reset",
Usage: "reset database to the previous state",
UsageText: "neo-go db reset --height height [--config-path path] [-p/-m/-t] [--config-file file]",
Action: resetDB,
Flags: cfgHeightFlags,
}, },
}, },
}, },
@ -123,7 +113,6 @@ func newGraceContext() context.Context {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
stop := make(chan os.Signal, 1) stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt) signal.Notify(stop, os.Interrupt)
signal.Notify(stop, syscall.SIGTERM)
go func() { go func() {
<-stop <-stop
cancel() cancel()
@ -131,36 +120,113 @@ func newGraceContext() context.Context {
return ctx return ctx
} }
// getConfigFromContext looks at path and mode flags in the given config and
// returns appropriate config.
func getConfigFromContext(ctx *cli.Context) (config.Config, error) {
configPath := "./config"
if argCp := ctx.String("config-path"); argCp != "" {
configPath = argCp
}
return config.Load(configPath, options.GetNetwork(ctx))
}
// handleLoggingParams reads logging parameters.
// If user selected debug level -- function enables it.
// If logPath is configured -- function creates dir and file for logging.
// If logPath is configured on Windows -- function returns closer to be
// able to close sink for opened log output file.
func handleLoggingParams(ctx *cli.Context, cfg config.ApplicationConfiguration) (*zap.Logger, func() error, error) {
level := zapcore.InfoLevel
if ctx.Bool("debug") {
level = zapcore.DebugLevel
}
cc := zap.NewProductionConfig()
cc.DisableCaller = true
cc.DisableStacktrace = true
cc.EncoderConfig.EncodeDuration = zapcore.StringDurationEncoder
cc.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
cc.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
cc.Encoding = "console"
cc.Level = zap.NewAtomicLevelAt(level)
cc.Sampling = nil
if logPath := cfg.LogPath; logPath != "" {
if err := io.MakeDirForFile(logPath, "logger"); err != nil {
return nil, nil, err
}
if runtime.GOOS == "windows" {
if !_winfileSinkRegistered {
// See https://github.com/uber-go/zap/issues/621.
err := zap.RegisterSink("winfile", func(u *url.URL) (zap.Sink, error) {
if u.User != nil {
return nil, fmt.Errorf("user and password not allowed with file URLs: got %v", u)
}
if u.Fragment != "" {
return nil, fmt.Errorf("fragments not allowed with file URLs: got %v", u)
}
if u.RawQuery != "" {
return nil, fmt.Errorf("query parameters not allowed with file URLs: got %v", u)
}
// Error messages are better if we check hostname and port separately.
if u.Port() != "" {
return nil, fmt.Errorf("ports not allowed with file URLs: got %v", u)
}
if hn := u.Hostname(); hn != "" && hn != "localhost" {
return nil, fmt.Errorf("file URLs must leave host empty or use localhost: got %v", u)
}
switch u.Path {
case "stdout":
return os.Stdout, nil
case "stderr":
return os.Stderr, nil
}
f, err := os.OpenFile(u.Path[1:], // Remove leading slash left after url.Parse.
os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
_winfileSinkCloser = func() error {
_winfileSinkCloser = nil
return f.Close()
}
return f, err
})
if err != nil {
return nil, nil, fmt.Errorf("failed to register windows-specific sinc: %w", err)
}
_winfileSinkRegistered = true
}
logPath = "winfile:///" + logPath
}
cc.OutputPaths = []string{logPath}
}
log, err := cc.Build()
return log, _winfileSinkCloser, err
}
func initBCWithMetrics(cfg config.Config, log *zap.Logger) (*core.Blockchain, *metrics.Service, *metrics.Service, error) { func initBCWithMetrics(cfg config.Config, log *zap.Logger) (*core.Blockchain, *metrics.Service, *metrics.Service, error) {
chain, _, err := initBlockChain(cfg, log) chain, err := initBlockChain(cfg, log)
if err != nil { if err != nil {
return nil, nil, nil, cli.NewExitError(err, 1) return nil, nil, nil, cli.NewExitError(err, 1)
} }
configureAddresses(&cfg.ApplicationConfiguration)
prometheus := metrics.NewPrometheusService(cfg.ApplicationConfiguration.Prometheus, log) prometheus := metrics.NewPrometheusService(cfg.ApplicationConfiguration.Prometheus, log)
pprof := metrics.NewPprofService(cfg.ApplicationConfiguration.Pprof, log) pprof := metrics.NewPprofService(cfg.ApplicationConfiguration.Pprof, log)
go chain.Run() go chain.Run()
err = prometheus.Start() go prometheus.Start()
if err != nil { go pprof.Start()
return nil, nil, nil, cli.NewExitError(fmt.Errorf("failed to start Prometheus service: %w", err), 1)
}
err = pprof.Start()
if err != nil {
return nil, nil, nil, cli.NewExitError(fmt.Errorf("failed to start Pprof service: %w", err), 1)
}
return chain, prometheus, pprof, nil return chain, prometheus, pprof, nil
} }
func dumpDB(ctx *cli.Context) error { func dumpDB(ctx *cli.Context) error {
if err := cmdargs.EnsureNone(ctx); err != nil { cfg, err := getConfigFromContext(ctx)
return err
}
cfg, err := options.GetConfigFromContext(ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
log, _, logCloser, err := options.HandleLoggingParams(ctx.Bool("debug"), cfg.ApplicationConfiguration) log, logCloser, err := handleLoggingParams(ctx, cfg.ApplicationConfiguration)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
@ -197,9 +263,6 @@ func dumpDB(ctx *cli.Context) error {
if count == 0 { if count == 0 {
count = chainCount - start count = chainCount - start
} }
if start != 0 {
writer.WriteU32LE(start)
}
writer.WriteU32LE(count) writer.WriteU32LE(count)
err = chaindump.Dump(chain, writer, start, count) err = chaindump.Dump(chain, writer, start, count)
if err != nil { if err != nil {
@ -209,14 +272,11 @@ func dumpDB(ctx *cli.Context) error {
} }
func restoreDB(ctx *cli.Context) error { func restoreDB(ctx *cli.Context) error {
if err := cmdargs.EnsureNone(ctx); err != nil { cfg, err := getConfigFromContext(ctx)
return err
}
cfg, err := options.GetConfigFromContext(ctx)
if err != nil { if err != nil {
return err return err
} }
log, _, logCloser, err := options.HandleLoggingParams(ctx.Bool("debug"), cfg.ApplicationConfiguration) log, logCloser, err := handleLoggingParams(ctx, cfg.ApplicationConfiguration)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
@ -237,7 +297,7 @@ func restoreDB(ctx *cli.Context) error {
dumpDir := ctx.String("dump") dumpDir := ctx.String("dump")
if dumpDir != "" { if dumpDir != "" {
cfg.ApplicationConfiguration.SaveStorageBatch = true cfg.ProtocolConfiguration.SaveStorageBatch = true
} }
chain, prometheus, pprof, err := initBCWithMetrics(cfg, log) chain, prometheus, pprof, err := initBCWithMetrics(cfg, log)
@ -325,54 +385,14 @@ func restoreDB(ctx *cli.Context) error {
return nil return nil
} }
func resetDB(ctx *cli.Context) error { func mkOracle(config network.ServerConfig, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (*oracle.Oracle, error) {
if err := cmdargs.EnsureNone(ctx); err != nil { if !config.OracleCfg.Enabled {
return err
}
cfg, err := options.GetConfigFromContext(ctx)
if err != nil {
return cli.NewExitError(err, 1)
}
h := uint32(ctx.Uint("height"))
log, _, logCloser, err := options.HandleLoggingParams(ctx.Bool("debug"), cfg.ApplicationConfiguration)
if err != nil {
return cli.NewExitError(err, 1)
}
if logCloser != nil {
defer func() { _ = logCloser() }()
}
chain, store, err := initBlockChain(cfg, log)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create Blockchain instance: %w", err), 1)
}
err = chain.Reset(h)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to reset chain state to height %d: %w", h, err), 1)
}
err = store.Close()
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to close the DB: %w", err), 1)
}
return nil
}
// oracleService is an interface representing Oracle service with network.Service
// capabilities and ability to submit oracle responses.
type oracleService interface {
rpcsrv.OracleHandler
network.Service
}
func mkOracle(config config.OracleConfiguration, magic netmode.Magic, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (oracleService, error) {
if !config.Enabled {
return nil, nil return nil, nil
} }
orcCfg := oracle.Config{ orcCfg := oracle.Config{
Log: log, Log: log,
Network: magic, Network: config.Net,
MainCfg: config, MainCfg: config.OracleCfg,
Chain: chain, Chain: chain,
OnTransaction: serv.RelayTxn, OnTransaction: serv.RelayTxn,
} }
@ -385,44 +405,41 @@ func mkOracle(config config.OracleConfiguration, magic netmode.Magic, chain *cor
return orc, nil return orc, nil
} }
func mkConsensus(config config.Consensus, tpb time.Duration, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (consensus.Service, error) { func mkConsensus(config network.ServerConfig, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (consensus.Service, error) {
if !config.Enabled { if config.Wallet == nil {
return nil, nil return nil, nil
} }
srv, err := consensus.NewService(consensus.Config{ srv, err := consensus.NewService(consensus.Config{
Logger: log, Logger: log,
Broadcast: serv.BroadcastExtensible, Broadcast: serv.BroadcastExtensible,
Chain: chain, Chain: chain,
BlockQueue: serv.GetBlockQueue(), ProtocolConfiguration: chain.GetConfig(),
ProtocolConfiguration: chain.GetConfig().ProtocolConfiguration,
RequestTx: serv.RequestTx, RequestTx: serv.RequestTx,
StopTxFlow: serv.StopTxFlow, Wallet: config.Wallet,
Wallet: config.UnlockWallet, TimePerBlock: config.TimePerBlock,
TimePerBlock: tpb,
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("can't initialize Consensus module: %w", err) return nil, fmt.Errorf("can't initialize Consensus module: %w", err)
} }
serv.AddConsensusService(srv, srv.OnPayload, srv.OnTransaction) serv.AddExtensibleHPService(srv, consensus.Category, srv.OnPayload, srv.OnTransaction)
return srv, nil return srv, nil
} }
func mkP2PNotary(config config.P2PNotary, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (*notary.Notary, error) { func mkP2PNotary(config network.ServerConfig, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (*notary.Notary, error) {
if !config.Enabled { if !config.P2PNotaryCfg.Enabled {
return nil, nil return nil, nil
} }
if !chain.P2PSigExtensionsEnabled() { if !chain.P2PSigExtensionsEnabled() {
return nil, errors.New("P2PSigExtensions are disabled, but Notary service is enabled") return nil, errors.New("P2PSigExtensions are disabled, but Notary service is enabled")
} }
cfg := notary.Config{ cfg := notary.Config{
MainCfg: config, MainCfg: config.P2PNotaryCfg,
Chain: chain, Chain: chain,
Log: log, Log: log,
} }
n, err := notary.NewNotary(cfg, serv.Net, serv.GetNotaryPool(), func(tx *transaction.Transaction) error { n, err := notary.NewNotary(cfg, serv.Net, serv.GetNotaryPool(), func(tx *transaction.Transaction) error {
err := serv.RelayTxn(tx) if err := serv.RelayTxn(tx); err != nil {
if err != nil && !errors.Is(err, core.ErrAlreadyExists) && !errors.Is(err, core.ErrAlreadyInPool) {
return fmt.Errorf("can't relay completed notary transaction: hash %s, error: %w", tx.Hash().StringLE(), err) return fmt.Errorf("can't relay completed notary transaction: hash %s, error: %w", tx.Hash().StringLE(), err)
} }
return nil return nil
@ -436,16 +453,11 @@ func mkP2PNotary(config config.P2PNotary, chain *core.Blockchain, serv *network.
} }
func startServer(ctx *cli.Context) error { func startServer(ctx *cli.Context) error {
if err := cmdargs.EnsureNone(ctx); err != nil { cfg, err := getConfigFromContext(ctx)
return err
}
cfg, err := options.GetConfigFromContext(ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
var logDebug = ctx.Bool("debug") log, logCloser, err := handleLoggingParams(ctx, cfg.ApplicationConfiguration)
log, logLevel, logCloser, err := options.HandleLoggingParams(logDebug, cfg.ApplicationConfiguration)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
@ -456,10 +468,7 @@ func startServer(ctx *cli.Context) error {
grace, cancel := context.WithCancel(newGraceContext()) grace, cancel := context.WithCancel(newGraceContext())
defer cancel() defer cancel()
serverConfig, err := network.NewServerConfig(cfg) serverConfig := network.NewServerConfig(cfg)
if err != nil {
return cli.NewExitError(err, 1)
}
chain, prometheus, pprof, err := initBCWithMetrics(cfg, log) chain, prometheus, pprof, err := initBCWithMetrics(cfg, log)
if err != nil { if err != nil {
@ -482,34 +491,26 @@ func startServer(ctx *cli.Context) error {
} }
serv.AddExtensibleService(sr, stateroot.Category, sr.OnPayload) serv.AddExtensibleService(sr, stateroot.Category, sr.OnPayload)
oracleSrv, err := mkOracle(cfg.ApplicationConfiguration.Oracle, cfg.ProtocolConfiguration.Magic, chain, serv, log) oracleSrv, err := mkOracle(serverConfig, chain, serv, log)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
dbftSrv, err := mkConsensus(cfg.ApplicationConfiguration.Consensus, serverConfig.TimePerBlock, chain, serv, log) _, err = mkConsensus(serverConfig, chain, serv, log)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
p2pNotary, err := mkP2PNotary(cfg.ApplicationConfiguration.P2PNotary, chain, serv, log) _, err = mkP2PNotary(serverConfig, chain, serv, log)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
rpcServer := server.New(chain, cfg.ApplicationConfiguration.RPC, serv, oracleSrv, log)
errChan := make(chan error) errChan := make(chan error)
rpcServer := rpcsrv.New(chain, cfg.ApplicationConfiguration.RPC, serv, oracleSrv, log, errChan)
serv.AddService(&rpcServer)
serv.Start() go serv.Start(errChan)
if !cfg.ApplicationConfiguration.RPC.StartWhenSynchronized { rpcServer.Start(errChan)
// Run RPC server in a separate routine. This is necessary to avoid a potential
// deadlock: Start() can write errors to errChan which is not yet read in the
// current execution context (see for-loop below).
go rpcServer.Start()
}
sigCh := make(chan os.Signal, 1) sighupCh := make(chan os.Signal, 1)
signal.Notify(sigCh, sighup) signal.Notify(sighupCh, syscall.SIGHUP)
signal.Notify(sigCh, sigusr1)
signal.Notify(sigCh, sigusr2)
fmt.Fprintln(ctx.App.Writer, Logo()) fmt.Fprintln(ctx.App.Writer, Logo())
fmt.Fprintln(ctx.App.Writer, serv.UserAgent) fmt.Fprintln(ctx.App.Writer, serv.UserAgent)
@ -522,119 +523,24 @@ Main:
case err := <-errChan: case err := <-errChan:
shutdownErr = fmt.Errorf("server error: %w", err) shutdownErr = fmt.Errorf("server error: %w", err)
cancel() cancel()
case sig := <-sigCh: case sig := <-sighupCh:
var newLogLevel = zapcore.InvalidLevel
log.Info("signal received", zap.Stringer("name", sig))
cfgnew, err := options.GetConfigFromContext(ctx)
if err != nil {
log.Warn("can't reread the config file, signal ignored", zap.Error(err))
break // Continue working.
}
if !cfg.ProtocolConfiguration.Equals(&cfgnew.ProtocolConfiguration) {
log.Warn("ProtocolConfiguration changed, signal ignored")
break // Continue working.
}
if !cfg.ApplicationConfiguration.EqualsButServices(&cfgnew.ApplicationConfiguration) {
log.Warn("ApplicationConfiguration changed in incompatible way, signal ignored")
break // Continue working.
}
if !logDebug && cfgnew.ApplicationConfiguration.LogLevel != cfg.ApplicationConfiguration.LogLevel {
newLogLevel, err = zapcore.ParseLevel(cfgnew.ApplicationConfiguration.LogLevel)
if err != nil {
log.Warn("wrong LogLevel in ApplicationConfiguration, signal ignored", zap.Error(err))
break // Continue working.
}
}
switch sig { switch sig {
case sighup: case syscall.SIGHUP:
if newLogLevel != zapcore.InvalidLevel { log.Info("SIGHUP received, restarting rpc-server")
logLevel.SetLevel(newLogLevel) serverErr := rpcServer.Shutdown()
log.Warn("using new logging level", zap.Stringer("level", newLogLevel)) if serverErr != nil {
} errChan <- fmt.Errorf("error while restarting rpc-server: %w", serverErr)
serv.DelService(&rpcServer) break
rpcServer.Shutdown()
rpcServer = rpcsrv.New(chain, cfgnew.ApplicationConfiguration.RPC, serv, oracleSrv, log, errChan)
serv.AddService(&rpcServer)
if !cfgnew.ApplicationConfiguration.RPC.StartWhenSynchronized || serv.IsInSync() {
// Here similar to the initial run (see above for-loop), so async.
go rpcServer.Start()
}
pprof.ShutDown()
pprof = metrics.NewPprofService(cfgnew.ApplicationConfiguration.Pprof, log)
err = pprof.Start()
if err != nil {
shutdownErr = fmt.Errorf("failed to start Pprof service: %w", err)
cancel() // Fatal error, like for RPC server.
}
prometheus.ShutDown()
prometheus = metrics.NewPrometheusService(cfgnew.ApplicationConfiguration.Prometheus, log)
err = prometheus.Start()
if err != nil {
shutdownErr = fmt.Errorf("failed to start Prometheus service: %w", err)
cancel() // Fatal error, like for RPC server.
}
case sigusr1:
if oracleSrv != nil {
serv.DelService(oracleSrv)
chain.SetOracle(nil)
rpcServer.SetOracleHandler(nil)
oracleSrv.Shutdown()
}
oracleSrv, err = mkOracle(cfgnew.ApplicationConfiguration.Oracle, cfgnew.ProtocolConfiguration.Magic, chain, serv, log)
if err != nil {
log.Error("failed to create oracle service", zap.Error(err))
break // Keep going.
}
if oracleSrv != nil {
rpcServer.SetOracleHandler(oracleSrv)
if serv.IsInSync() {
oracleSrv.Start()
}
}
if p2pNotary != nil {
serv.DelService(p2pNotary)
chain.SetNotary(nil)
p2pNotary.Shutdown()
}
p2pNotary, err = mkP2PNotary(cfgnew.ApplicationConfiguration.P2PNotary, chain, serv, log)
if err != nil {
log.Error("failed to create notary service", zap.Error(err))
break // Keep going.
}
if p2pNotary != nil && serv.IsInSync() {
p2pNotary.Start()
}
serv.DelExtensibleService(sr, stateroot.Category)
srMod.SetUpdateValidatorsCallback(nil)
sr.Shutdown()
sr, err = stateroot.New(cfgnew.ApplicationConfiguration.StateRoot, srMod, log, chain, serv.BroadcastExtensible)
if err != nil {
log.Error("failed to create state validation service", zap.Error(err))
break // The show must go on.
}
serv.AddExtensibleService(sr, stateroot.Category, sr.OnPayload)
if serv.IsInSync() {
sr.Start()
}
case sigusr2:
if dbftSrv != nil {
serv.DelConsensusService(dbftSrv)
dbftSrv.Shutdown()
}
dbftSrv, err = mkConsensus(cfgnew.ApplicationConfiguration.Consensus, serverConfig.TimePerBlock, chain, serv, log)
if err != nil {
log.Error("failed to create consensus service", zap.Error(err))
break // Whatever happens, I'll leave it all to chance.
}
if dbftSrv != nil && serv.IsInSync() {
dbftSrv.Start()
} }
rpcServer = server.New(chain, cfg.ApplicationConfiguration.RPC, serv, oracleSrv, log)
rpcServer.Start(errChan)
} }
cfg = cfgnew
case <-grace.Done(): case <-grace.Done():
signal.Stop(sigCh) signal.Stop(sighupCh)
serv.Shutdown() serv.Shutdown()
if serverErr := rpcServer.Shutdown(); serverErr != nil {
shutdownErr = fmt.Errorf("error on shutdown: %w", serverErr)
}
break Main break Main
} }
} }
@ -646,29 +552,39 @@ Main:
return nil return nil
} }
// initBlockChain initializes BlockChain with preselected DB. // configureAddresses sets up addresses for RPC, Prometheus and Pprof depending from the provided config.
func initBlockChain(cfg config.Config, log *zap.Logger) (*core.Blockchain, storage.Store, error) { // In case RPC or Prometheus or Pprof Address provided each of them will use it.
store, err := storage.NewStore(cfg.ApplicationConfiguration.DBConfiguration) // In case global Address (of the node) provided and RPC/Prometheus/Pprof don't have configured addresses they will
if err != nil { // use global one. So Node and RPC and Prometheus and Pprof will run on one address.
return nil, nil, cli.NewExitError(fmt.Errorf("could not initialize storage: %w", err), 1) func configureAddresses(cfg *config.ApplicationConfiguration) {
} if cfg.Address != "" {
if cfg.RPC.Address == "" {
chain, err := core.NewBlockchain(store, cfg.Blockchain(), log) cfg.RPC.Address = cfg.Address
if err != nil { }
errText := "could not initialize blockchain: %w" if cfg.Prometheus.Address == "" {
errArgs := []any{err} cfg.Prometheus.Address = cfg.Address
closeErr := store.Close() }
if closeErr != nil { if cfg.Pprof.Address == "" {
errText += "; failed to close the DB: %w" cfg.Pprof.Address = cfg.Address
errArgs = append(errArgs, closeErr)
} }
return nil, nil, cli.NewExitError(fmt.Errorf(errText, errArgs...), 1)
} }
return chain, store, nil
} }
// Logo returns NeoGo logo. // initBlockChain initializes BlockChain with preselected DB.
func initBlockChain(cfg config.Config, log *zap.Logger) (*core.Blockchain, error) {
store, err := storage.NewStore(cfg.ApplicationConfiguration.DBConfiguration)
if err != nil {
return nil, cli.NewExitError(fmt.Errorf("could not initialize storage: %w", err), 1)
}
chain, err := core.NewBlockchain(store, cfg.ProtocolConfiguration, log)
if err != nil {
return nil, cli.NewExitError(fmt.Errorf("could not initialize blockchain: %w", err), 1)
}
return chain, nil
}
// Logo returns Neo-Go logo.
func Logo() string { func Logo() string {
return ` return `
_ ____________ __________ _ ____________ __________

View file

@ -3,20 +3,20 @@ package server
import ( import (
"encoding/binary" "encoding/binary"
"flag" "flag"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"testing" "testing"
"go.uber.org/zap/zapcore"
"github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig" "github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/network/metrics"
"github.com/nspcc-dev/neo-go/pkg/rpc"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli" "github.com/urfave/cli"
"gopkg.in/yaml.v3" "go.uber.org/zap"
"gopkg.in/yaml.v2"
) )
// serverTestWD is the default working directory for server tests. // serverTestWD is the default working directory for server tests.
@ -31,58 +31,13 @@ func init() {
} }
func TestGetConfigFromContext(t *testing.T) { func TestGetConfigFromContext(t *testing.T) {
t.Run("config-path", func(t *testing.T) { set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set := flag.NewFlagSet("flagSet", flag.ExitOnError) set.String("config-path", "../../config", "")
set.String("config-path", "../../config", "") set.Bool("testnet", true, "")
set.Bool("testnet", true, "") ctx := cli.NewContext(cli.NewApp(), set, nil)
ctx := cli.NewContext(cli.NewApp(), set, nil) cfg, err := getConfigFromContext(ctx)
cfg, err := options.GetConfigFromContext(ctx) require.NoError(t, err)
require.NoError(t, err) require.Equal(t, netmode.TestNet, cfg.ProtocolConfiguration.Magic)
require.Equal(t, netmode.TestNet, cfg.ProtocolConfiguration.Magic)
})
t.Run("config-file", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("config-path", "../../config", "")
set.Bool("testnet", true, "")
set.String("config-file", "../../config/protocol.testnet.yml", "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
cfg, err := options.GetConfigFromContext(ctx)
require.NoError(t, err)
require.Equal(t, netmode.TestNet, cfg.ProtocolConfiguration.Magic)
})
t.Run("relative-path windows", func(t *testing.T) {
if runtime.GOOS != "windows" {
t.Skip("skipping Windows specific test")
}
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("relative-path", "..\\..\\config", "")
set.Bool("testnet", true, "")
set.String("config-file", ".\\testdata\\protocol.testnet.windows.yml", "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
cfg, err := options.GetConfigFromContext(ctx)
require.NoError(t, err)
require.Equal(t, filepath.Join("..", "..", "config", "chains", "testnet"), cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath)
require.Equal(t, "C:\\someFolder\\cn_wallet.json", cfg.ApplicationConfiguration.Consensus.UnlockWallet.Path)
require.Equal(t, "C:\\someFolder\\notary_wallet.json", cfg.ApplicationConfiguration.P2PNotary.UnlockWallet.Path)
})
t.Run("relative-path non-windows", func(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping non-Windows specific test")
}
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("relative-path", "../../config", "")
set.Bool("testnet", true, "")
set.String("config-file", "../../config/protocol.testnet.yml", "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
cfg, err := options.GetConfigFromContext(ctx)
require.NoError(t, err)
require.Equal(t, filepath.Join("..", "..", "config", "chains", "testnet"), cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath)
require.Equal(t, "/cn_wallet.json", cfg.ApplicationConfiguration.Consensus.UnlockWallet.Path)
require.Equal(t, "/notary_wallet.json", cfg.ApplicationConfiguration.P2PNotary.UnlockWallet.Path)
})
} }
func TestHandleLoggingParams(t *testing.T) { func TestHandleLoggingParams(t *testing.T) {
@ -91,75 +46,50 @@ func TestHandleLoggingParams(t *testing.T) {
t.Run("logdir is a file", func(t *testing.T) { t.Run("logdir is a file", func(t *testing.T) {
logfile := filepath.Join(d, "logdir") logfile := filepath.Join(d, "logdir")
require.NoError(t, os.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm)) require.NoError(t, ioutil.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm))
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
ctx := cli.NewContext(cli.NewApp(), set, nil)
cfg := config.ApplicationConfiguration{ cfg := config.ApplicationConfiguration{
LogPath: filepath.Join(logfile, "file.log"), LogPath: filepath.Join(logfile, "file.log"),
} }
_, lvl, closer, err := options.HandleLoggingParams(false, cfg) _, closer, err := handleLoggingParams(ctx, cfg)
require.Error(t, err) require.Error(t, err)
require.Nil(t, lvl)
require.Nil(t, closer)
})
t.Run("broken level", func(t *testing.T) {
cfg := config.ApplicationConfiguration{
LogPath: testLog,
LogLevel: "qwerty",
}
_, lvl, closer, err := options.HandleLoggingParams(false, cfg)
require.Error(t, err)
require.Nil(t, lvl)
require.Nil(t, closer) require.Nil(t, closer)
}) })
t.Run("default", func(t *testing.T) { t.Run("default", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
ctx := cli.NewContext(cli.NewApp(), set, nil)
cfg := config.ApplicationConfiguration{ cfg := config.ApplicationConfiguration{
LogPath: testLog, LogPath: testLog,
} }
logger, lvl, closer, err := options.HandleLoggingParams(false, cfg) logger, closer, err := handleLoggingParams(ctx, cfg)
require.NotNil(t, lvl)
require.NoError(t, err) require.NoError(t, err)
t.Cleanup(func() { t.Cleanup(func() {
if closer != nil { if closer != nil {
require.NoError(t, closer()) require.NoError(t, closer())
} }
}) })
require.Equal(t, zapcore.InfoLevel, lvl.Level()) require.True(t, logger.Core().Enabled(zap.InfoLevel))
require.True(t, logger.Core().Enabled(zapcore.InfoLevel)) require.False(t, logger.Core().Enabled(zap.DebugLevel))
require.False(t, logger.Core().Enabled(zapcore.DebugLevel))
})
t.Run("warn", func(t *testing.T) {
cfg := config.ApplicationConfiguration{
LogPath: testLog,
LogLevel: "warn",
}
logger, lvl, closer, err := options.HandleLoggingParams(false, cfg)
require.NoError(t, err)
t.Cleanup(func() {
if closer != nil {
require.NoError(t, closer())
}
})
require.Equal(t, zapcore.WarnLevel, lvl.Level())
require.True(t, logger.Core().Enabled(zapcore.WarnLevel))
require.False(t, logger.Core().Enabled(zapcore.InfoLevel))
}) })
t.Run("debug", func(t *testing.T) { t.Run("debug", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.Bool("debug", true, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
cfg := config.ApplicationConfiguration{ cfg := config.ApplicationConfiguration{
LogPath: testLog, LogPath: testLog,
} }
logger, lvl, closer, err := options.HandleLoggingParams(true, cfg) logger, closer, err := handleLoggingParams(ctx, cfg)
require.NoError(t, err) require.NoError(t, err)
t.Cleanup(func() { t.Cleanup(func() {
if closer != nil { if closer != nil {
require.NoError(t, closer()) require.NoError(t, closer())
} }
}) })
require.Equal(t, zapcore.DebugLevel, lvl.Level()) require.True(t, logger.Core().Enabled(zap.InfoLevel))
require.True(t, logger.Core().Enabled(zapcore.InfoLevel)) require.True(t, logger.Core().Enabled(zap.DebugLevel))
require.True(t, logger.Core().Enabled(zapcore.DebugLevel))
}) })
} }
@ -174,9 +104,9 @@ func TestInitBCWithMetrics(t *testing.T) {
set.Bool("testnet", true, "") set.Bool("testnet", true, "")
set.Bool("debug", true, "") set.Bool("debug", true, "")
ctx := cli.NewContext(cli.NewApp(), set, nil) ctx := cli.NewContext(cli.NewApp(), set, nil)
cfg, err := options.GetConfigFromContext(ctx) cfg, err := getConfigFromContext(ctx)
require.NoError(t, err) require.NoError(t, err)
logger, _, closer, err := options.HandleLoggingParams(true, cfg.ApplicationConfiguration) logger, closer, err := handleLoggingParams(ctx, cfg.ApplicationConfiguration)
require.NoError(t, err) require.NoError(t, err)
t.Cleanup(func() { t.Cleanup(func() {
if closer != nil { if closer != nil {
@ -266,7 +196,7 @@ func TestRestoreDB(t *testing.T) {
t.Run("invalid logger path", func(t *testing.T) { t.Run("invalid logger path", func(t *testing.T) {
badCfgDir := t.TempDir() badCfgDir := t.TempDir()
logfile := filepath.Join(badCfgDir, "logdir") logfile := filepath.Join(badCfgDir, "logdir")
require.NoError(t, os.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm)) require.NoError(t, ioutil.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm))
cfg, err := config.LoadFile(filepath.Join(goodCfg, "protocol.privnet.yml")) cfg, err := config.LoadFile(filepath.Join(goodCfg, "protocol.privnet.yml"))
require.NoError(t, err, "could not load config") require.NoError(t, err, "could not load config")
cfg.ApplicationConfiguration.LogPath = filepath.Join(logfile, "file.log") cfg.ApplicationConfiguration.LogPath = filepath.Join(logfile, "file.log")
@ -274,7 +204,7 @@ func TestRestoreDB(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
badCfgPath := filepath.Join(badCfgDir, "protocol.privnet.yml") badCfgPath := filepath.Join(badCfgDir, "protocol.privnet.yml")
require.NoError(t, os.WriteFile(badCfgPath, out, os.ModePerm)) require.NoError(t, ioutil.WriteFile(badCfgPath, out, os.ModePerm))
*cfgPath = badCfgDir *cfgPath = badCfgDir
require.Error(t, restoreDB(ctx)) require.Error(t, restoreDB(ctx))
@ -290,7 +220,7 @@ func TestRestoreDB(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
badCfgPath := filepath.Join(badCfgDir, "protocol.privnet.yml") badCfgPath := filepath.Join(badCfgDir, "protocol.privnet.yml")
require.NoError(t, os.WriteFile(badCfgPath, out, os.ModePerm)) require.NoError(t, ioutil.WriteFile(badCfgPath, out, os.ModePerm))
*cfgPath = badCfgDir *cfgPath = badCfgDir
require.Error(t, restoreDB(ctx)) require.Error(t, restoreDB(ctx))
@ -308,7 +238,7 @@ func TestRestoreDB(t *testing.T) {
}) })
t.Run("corrupted in: invalid block count", func(t *testing.T) { t.Run("corrupted in: invalid block count", func(t *testing.T) {
inPath := filepath.Join(t.TempDir(), "file3.acc") inPath := filepath.Join(t.TempDir(), "file3.acc")
require.NoError(t, os.WriteFile(inPath, []byte{1, 2, 3}, // file is expected to start from uint32 require.NoError(t, ioutil.WriteFile(inPath, []byte{1, 2, 3}, // file is expected to start from uint32
os.ModePerm)) os.ModePerm))
*in = inPath *in = inPath
require.Error(t, restoreDB(ctx)) require.Error(t, restoreDB(ctx))
@ -317,10 +247,10 @@ func TestRestoreDB(t *testing.T) {
}) })
t.Run("corrupted in: corrupted block", func(t *testing.T) { t.Run("corrupted in: corrupted block", func(t *testing.T) {
inPath := filepath.Join(t.TempDir(), "file3.acc") inPath := filepath.Join(t.TempDir(), "file3.acc")
b, err := os.ReadFile(testDump) b, err := ioutil.ReadFile(testDump)
require.NoError(t, err) require.NoError(t, err)
b[5] = 0xff // file is expected to start from uint32 (4 bytes) followed by the first block, so corrupt the first block bytes b[5] = 0xff // file is expected to start from uint32 (4 bytes) followed by the first block, so corrupt the first block bytes
require.NoError(t, os.WriteFile(inPath, b, os.ModePerm)) require.NoError(t, ioutil.WriteFile(inPath, b, os.ModePerm))
*in = inPath *in = inPath
require.Error(t, restoreDB(ctx)) require.Error(t, restoreDB(ctx))
@ -328,12 +258,12 @@ func TestRestoreDB(t *testing.T) {
}) })
t.Run("incremental dump", func(t *testing.T) { t.Run("incremental dump", func(t *testing.T) {
inPath := filepath.Join(t.TempDir(), "file1_incremental.acc") inPath := filepath.Join(t.TempDir(), "file1_incremental.acc")
b, err := os.ReadFile(testDump) b, err := ioutil.ReadFile(testDump)
require.NoError(t, err) require.NoError(t, err)
start := make([]byte, 4) start := make([]byte, 4)
t.Run("good", func(t *testing.T) { t.Run("good", func(t *testing.T) {
binary.LittleEndian.PutUint32(start, 1) // start from the first block binary.LittleEndian.PutUint32(start, 1) // start from the first block
require.NoError(t, os.WriteFile(inPath, append(start, b...), require.NoError(t, ioutil.WriteFile(inPath, append(start, b...),
os.ModePerm)) os.ModePerm))
*in = inPath *in = inPath
*incremental = true *incremental = true
@ -342,7 +272,7 @@ func TestRestoreDB(t *testing.T) {
}) })
t.Run("dump is too high", func(t *testing.T) { t.Run("dump is too high", func(t *testing.T) {
binary.LittleEndian.PutUint32(start, 2) // start from the second block binary.LittleEndian.PutUint32(start, 2) // start from the second block
require.NoError(t, os.WriteFile(inPath, append(start, b...), require.NoError(t, ioutil.WriteFile(inPath, append(start, b...),
os.ModePerm)) os.ModePerm))
*in = inPath *in = inPath
*incremental = true *incremental = true
@ -358,35 +288,74 @@ func TestRestoreDB(t *testing.T) {
require.NoError(t, restoreDB(ctx)) require.NoError(t, restoreDB(ctx))
} }
func TestConfigureAddresses(t *testing.T) {
defaultAddress := "http://127.0.0.1:10333"
customAddress := "http://127.0.0.1:10334"
t.Run("default addresses", func(t *testing.T) {
cfg := &config.ApplicationConfiguration{
Address: defaultAddress,
}
configureAddresses(cfg)
require.Equal(t, defaultAddress, cfg.RPC.Address)
require.Equal(t, defaultAddress, cfg.Prometheus.Address)
require.Equal(t, defaultAddress, cfg.Pprof.Address)
})
t.Run("custom RPC address", func(t *testing.T) {
cfg := &config.ApplicationConfiguration{
Address: defaultAddress,
RPC: rpc.Config{
Address: customAddress,
},
}
configureAddresses(cfg)
require.Equal(t, cfg.RPC.Address, customAddress)
require.Equal(t, cfg.Prometheus.Address, defaultAddress)
require.Equal(t, cfg.Pprof.Address, defaultAddress)
})
t.Run("custom Pprof address", func(t *testing.T) {
cfg := &config.ApplicationConfiguration{
Address: defaultAddress,
Pprof: metrics.Config{
Address: customAddress,
},
}
configureAddresses(cfg)
require.Equal(t, cfg.RPC.Address, defaultAddress)
require.Equal(t, cfg.Prometheus.Address, defaultAddress)
require.Equal(t, cfg.Pprof.Address, customAddress)
})
t.Run("custom Prometheus address", func(t *testing.T) {
cfg := &config.ApplicationConfiguration{
Address: defaultAddress,
Prometheus: metrics.Config{
Address: customAddress,
},
}
configureAddresses(cfg)
require.Equal(t, cfg.RPC.Address, defaultAddress)
require.Equal(t, cfg.Prometheus.Address, customAddress)
require.Equal(t, cfg.Pprof.Address, defaultAddress)
})
}
func TestInitBlockChain(t *testing.T) { func TestInitBlockChain(t *testing.T) {
t.Run("bad storage", func(t *testing.T) { t.Run("bad storage", func(t *testing.T) {
_, _, err := initBlockChain(config.Config{}, nil) _, err := initBlockChain(config.Config{}, nil)
require.Error(t, err) require.Error(t, err)
}) })
t.Run("empty logger", func(t *testing.T) { t.Run("empty logger", func(t *testing.T) {
_, _, err := initBlockChain(config.Config{ _, err := initBlockChain(config.Config{
ApplicationConfiguration: config.ApplicationConfiguration{ ApplicationConfiguration: config.ApplicationConfiguration{
DBConfiguration: dbconfig.DBConfiguration{ DBConfiguration: storage.DBConfiguration{
Type: dbconfig.InMemoryDB, Type: "inmemory",
}, },
}, },
}, nil) }, nil)
require.Error(t, err) require.Error(t, err)
}) })
} }
func TestResetDB(t *testing.T) {
d := t.TempDir()
err := os.Chdir(d)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, os.Chdir(serverTestWD)) })
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("config-path", filepath.Join(serverTestWD, "..", "..", "config"), "")
set.Bool("privnet", true, "")
set.Bool("debug", true, "")
set.Int("height", 0, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
err = resetDB(ctx)
require.NoError(t, err)
}

View file

@ -1,11 +0,0 @@
//go:build !windows
package server
import "syscall"
const (
sighup = syscall.SIGHUP
sigusr1 = syscall.SIGUSR1
sigusr2 = syscall.SIGUSR2
)

View file

@ -1,12 +0,0 @@
//go:build windows
package server
import "syscall"
const (
// Doesn't really matter, Windows can't do it.
sighup = syscall.SIGHUP
sigusr1 = syscall.Signal(0xa)
sigusr2 = syscall.Signal(0xc)
)

View file

@ -1,96 +0,0 @@
ProtocolConfiguration:
Magic: 894710606
MaxBlockSize: 2097152
MaxBlockSystemFee: 150000000000
MaxTraceableBlocks: 2102400
MaxTransactionsPerBlock: 5000
InitialGASSupply: 52000000
TimePerBlock: 15s
MemPoolSize: 50000
StandbyCommittee:
- 023e9b32ea89b94d066e649b124fd50e396ee91369e8e2a6ae1b11c170d022256d
- 03009b7540e10f2562e5fd8fac9eaec25166a58b26e412348ff5a86927bfac22a2
- 02ba2c70f5996f357a43198705859fae2cfea13e1172962800772b3d588a9d4abd
- 03408dcd416396f64783ac587ea1e1593c57d9fea880c8a6a1920e92a259477806
- 02a7834be9b32e2981d157cb5bbd3acb42cfd11ea5c3b10224d7a44e98c5910f1b
- 0214baf0ceea3a66f17e7e1e839ea25fd8bed6cd82e6bb6e68250189065f44ff01
- 030205e9cefaea5a1dfc580af20c8d5aa2468bb0148f1a5e4605fc622c80e604ba
- 025831cee3708e87d78211bec0d1bfee9f4c85ae784762f042e7f31c0d40c329b8
- 02cf9dc6e85d581480d91e88e8cbeaa0c153a046e89ded08b4cefd851e1d7325b5
- 03840415b0a0fcf066bcc3dc92d8349ebd33a6ab1402ef649bae00e5d9f5840828
- 026328aae34f149853430f526ecaa9cf9c8d78a4ea82d08bdf63dd03c4d0693be6
- 02c69a8d084ee7319cfecf5161ff257aa2d1f53e79bf6c6f164cff5d94675c38b3
- 0207da870cedb777fceff948641021714ec815110ca111ccc7a54c168e065bda70
- 035056669864feea401d8c31e447fb82dd29f342a9476cfd449584ce2a6165e4d7
- 0370c75c54445565df62cfe2e76fbec4ba00d1298867972213530cae6d418da636
- 03957af9e77282ae3263544b7b2458903624adc3f5dee303957cb6570524a5f254
- 03d84d22b8753cf225d263a3a782a4e16ca72ef323cfde04977c74f14873ab1e4c
- 02147c1b1d5728e1954958daff2f88ee2fa50a06890a8a9db3fa9e972b66ae559f
- 03c609bea5a4825908027e4ab217e7efc06e311f19ecad9d417089f14927a173d5
- 0231edee3978d46c335e851c76059166eb8878516f459e085c0dd092f0f1d51c21
- 03184b018d6b2bc093e535519732b3fd3f7551c8cffaf4621dd5a0b89482ca66c9
ValidatorsCount: 7
SeedList:
- seed1t5.neo.org:20333
- seed2t5.neo.org:20333
- seed3t5.neo.org:20333
- seed4t5.neo.org:20333
- seed5t5.neo.org:20333
VerifyTransactions: false
P2PSigExtensions: false
Hardforks:
Aspidochelone: 210000
Basilisk: 2680000
ApplicationConfiguration:
SkipBlockVerification: false
DBConfiguration:
Type: "leveldb" #other options: 'inmemory','boltdb'
# DB type options. Uncomment those you need in case you want to switch DB type.
LevelDBOptions:
DataDirectoryPath: ".\\chains\\testnet"
P2P:
Addresses:
- ":20333" # in form of "[host]:[port][:announcedPort]"
DialTimeout: 3s
ProtoTickInterval: 2s
PingInterval: 30s
PingTimeout: 90s
MaxPeers: 100
AttemptConnPeers: 20
MinPeers: 10
Relay: true
Consensus:
Enabled: false
UnlockWallet:
Path: "C:\\someFolder\\cn_wallet.json"
Password: "pass"
Oracle:
Enabled: false
AllowedContentTypes:
- application/json
P2PNotary:
Enabled: false
UnlockWallet:
Path: "C:\\someFolder\\notary_wallet.json"
Password: "pass"
RPC:
Enabled: true
Addresses:
- ":20332"
MaxGasInvoke: 15
EnableCORSWorkaround: false
TLSConfig:
Enabled: false
Addresses:
- ":20331"
CertFile: serv.crt
KeyFile: serv.key
Prometheus:
Enabled: true
Addresses:
- ":2112"
Pprof:
Enabled: false
Addresses:
- ":2113"

View file

@ -1,8 +1,8 @@
package server_test package main
import ( import (
"errors"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
@ -11,29 +11,27 @@ import (
"time" "time"
"github.com/nspcc-dev/neo-go/cli/server" "github.com/nspcc-dev/neo-go/cli/server"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v2"
) )
func TestServerStart(t *testing.T) { func TestServerStart(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
goodCfg, err := config.LoadFile(filepath.Join("..", "..", "config", "protocol.unit_testnet.yml")) goodCfg, err := config.LoadFile(filepath.Join("..", "config", "protocol.unit_testnet.yml"))
require.NoError(t, err, "could not load config") require.NoError(t, err, "could not load config")
ptr := &goodCfg ptr := &goodCfg
saveCfg := func(t *testing.T, f func(cfg *config.Config)) string { saveCfg := func(t *testing.T, f func(cfg *config.Config)) string {
cfg := *ptr cfg := *ptr
chainPath := filepath.Join(t.TempDir(), "neogotestchain") chainPath := filepath.Join(t.TempDir(), "neogotestchain")
cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB cfg.ApplicationConfiguration.DBConfiguration.Type = "leveldb"
cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath = chainPath cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath = chainPath
f(&cfg) f(&cfg)
out, err := yaml.Marshal(cfg) out, err := yaml.Marshal(cfg)
require.NoError(t, err) require.NoError(t, err)
cfgPath := filepath.Join(tmpDir, "protocol.unit_testnet.yml") cfgPath := filepath.Join(tmpDir, "protocol.unit_testnet.yml")
require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm)) require.NoError(t, ioutil.WriteFile(cfgPath, out, os.ModePerm))
t.Cleanup(func() { t.Cleanup(func() {
require.NoError(t, os.Remove(cfgPath)) require.NoError(t, os.Remove(cfgPath))
}) })
@ -41,7 +39,7 @@ func TestServerStart(t *testing.T) {
} }
baseCmd := []string{"neo-go", "node", "--unittest", "--config-path", tmpDir} baseCmd := []string{"neo-go", "node", "--unittest", "--config-path", tmpDir}
e := testcli.NewExecutor(t, false) e := newExecutor(t, false)
t.Run("invalid config path", func(t *testing.T) { t.Run("invalid config path", func(t *testing.T) {
e.RunWithError(t, baseCmd...) e.RunWithError(t, baseCmd...)
@ -49,7 +47,7 @@ func TestServerStart(t *testing.T) {
t.Run("bad logger config", func(t *testing.T) { t.Run("bad logger config", func(t *testing.T) {
badConfigDir := t.TempDir() badConfigDir := t.TempDir()
logfile := filepath.Join(badConfigDir, "logdir") logfile := filepath.Join(badConfigDir, "logdir")
require.NoError(t, os.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm)) require.NoError(t, ioutil.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm))
saveCfg(t, func(cfg *config.Config) { saveCfg(t, func(cfg *config.Config) {
cfg.ApplicationConfiguration.LogPath = filepath.Join(logfile, "file.log") cfg.ApplicationConfiguration.LogPath = filepath.Join(logfile, "file.log")
}) })
@ -77,8 +75,7 @@ func TestServerStart(t *testing.T) {
}) })
t.Run("invalid consensus config", func(t *testing.T) { t.Run("invalid consensus config", func(t *testing.T) {
saveCfg(t, func(cfg *config.Config) { saveCfg(t, func(cfg *config.Config) {
cfg.ApplicationConfiguration.Consensus.Enabled = true cfg.ApplicationConfiguration.UnlockWallet.Path = "bad_consensus_wallet.json"
cfg.ApplicationConfiguration.Consensus.UnlockWallet.Path = "bad_consensus_wallet.json"
}) })
e.RunWithError(t, baseCmd...) e.RunWithError(t, baseCmd...)
}) })
@ -102,11 +99,9 @@ func TestServerStart(t *testing.T) {
// We can't properly shutdown server on windows and release the resources. // We can't properly shutdown server on windows and release the resources.
// Also, windows doesn't support SIGHUP and SIGINT. // Also, windows doesn't support SIGHUP and SIGINT.
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
saveCfg(t, func(cfg *config.Config) {})
t.Run("excessive parameters", func(t *testing.T) {
e.RunWithError(t, append(baseCmd, "something")...)
})
t.Run("good", func(t *testing.T) { t.Run("good", func(t *testing.T) {
saveCfg(t, func(cfg *config.Config) {})
go func() { go func() {
e.Run(t, baseCmd...) e.Run(t, baseCmd...)
}() }()
@ -114,7 +109,7 @@ func TestServerStart(t *testing.T) {
var line string var line string
require.Eventually(t, func() bool { require.Eventually(t, func() bool {
line, err = e.Out.ReadString('\n') line, err = e.Out.ReadString('\n')
if err != nil && !errors.Is(err, io.EOF) { if err != nil && err != io.EOF {
t.Fatalf("unexpected error while reading CLI output: %s", err) t.Fatalf("unexpected error while reading CLI output: %s", err)
} }
return err == nil return err == nil
@ -123,11 +118,11 @@ func TestServerStart(t *testing.T) {
for _, expected := range lines { for _, expected := range lines {
// It should be regexp, so escape all backslashes. // It should be regexp, so escape all backslashes.
expected = strings.ReplaceAll(expected, `\`, `\\`) expected = strings.ReplaceAll(expected, `\`, `\\`)
e.CheckLine(t, line, expected) e.checkLine(t, line, expected)
line = e.GetNextLine(t) line = e.getNextLine(t)
} }
e.CheckNextLine(t, "") e.checkNextLine(t, "")
e.CheckEOF(t) e.checkEOF(t)
}) })
} }
} }

View file

@ -2,91 +2,51 @@ package smartcontract
import ( import (
"fmt" "fmt"
"io/ioutil"
"os" "os"
"strings"
"github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/binding" "github.com/nspcc-dev/neo-go/pkg/smartcontract/binding"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/rpcbinding"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/urfave/cli" "github.com/urfave/cli"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v2"
) )
var generatorFlags = []cli.Flag{
cli.StringFlag{
Name: "config, c",
Usage: "Configuration file to use",
},
cli.StringFlag{
Name: "manifest, m",
Required: true,
Usage: "Read contract manifest (*.manifest.json) file",
},
cli.StringFlag{
Name: "out, o",
Required: true,
Usage: "Output of the compiled wrapper",
},
cli.StringFlag{
Name: "hash",
Usage: "Smart-contract hash. If not passed, the wrapper will be designed for dynamic hash usage",
},
}
var generateWrapperCmd = cli.Command{ var generateWrapperCmd = cli.Command{
Name: "generate-wrapper", Name: "generate-wrapper",
Usage: "generate wrapper to use in other contracts", Usage: "generate wrapper to use in other contracts",
UsageText: "neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]", UsageText: "neo-go contract generate-wrapper --manifest manifest.json --out file.go",
Description: `Generates a Go wrapper to use it in other smart contracts. If the Description: ``,
--hash flag is provided, CALLT instruction is used for the target contract Action: contractGenerateWrapper,
invocation as an optimization of the wrapper contract code. If omitted, the Flags: []cli.Flag{
generated wrapper will be designed for dynamic hash usage, allowing cli.StringFlag{
the hash to be specified at runtime. Name: "config, c",
`, Usage: "Configuration file to use",
Action: contractGenerateWrapper, },
Flags: generatorFlags, cli.StringFlag{
} Name: "manifest, m",
Usage: "Read contract manifest (*.manifest.json) file",
var generateRPCWrapperCmd = cli.Command{ },
Name: "generate-rpcwrapper", cli.StringFlag{
Usage: "generate RPC wrapper to use for data reads", Name: "out, o",
UsageText: "neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]", Usage: "Output of the compiled contract",
Action: contractGenerateRPCWrapper, },
Flags: generatorFlags, cli.StringFlag{
Name: "hash",
Usage: "Smart-contract hash",
},
},
} }
// contractGenerateWrapper deploys contract.
func contractGenerateWrapper(ctx *cli.Context) error { func contractGenerateWrapper(ctx *cli.Context) error {
return contractGenerateSomething(ctx, binding.Generate) m, _, err := readManifest(ctx.String("manifest"))
}
func contractGenerateRPCWrapper(ctx *cli.Context) error {
return contractGenerateSomething(ctx, rpcbinding.Generate)
}
// contractGenerateSomething reads generator parameters and calls the given callback.
func contractGenerateSomething(ctx *cli.Context, cb func(binding.Config) error) error {
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
var (
h util.Uint160
err error
)
if hStr := ctx.String("hash"); len(hStr) != 0 {
h, err = util.Uint160DecodeStringLE(strings.TrimPrefix(hStr, "0x"))
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid contract hash: %w", err), 1)
}
}
m, _, err := readManifest(ctx.String("manifest"), h)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't read contract manifest: %w", err), 1) return cli.NewExitError(fmt.Errorf("can't read contract manifest: %w", err), 1)
} }
cfg := binding.NewConfig() cfg := binding.NewConfig()
if cfgPath := ctx.String("config"); cfgPath != "" { if cfgPath := ctx.String("config"); cfgPath != "" {
bs, err := os.ReadFile(cfgPath) bs, err := ioutil.ReadFile(cfgPath)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't read config file: %w", err), 1) return cli.NewExitError(fmt.Errorf("can't read config file: %w", err), 1)
} }
@ -95,7 +55,13 @@ func contractGenerateSomething(ctx *cli.Context, cb func(binding.Config) error)
return cli.NewExitError(fmt.Errorf("can't parse config file: %w", err), 1) return cli.NewExitError(fmt.Errorf("can't parse config file: %w", err), 1)
} }
} }
cfg.Manifest = m cfg.Manifest = m
h, err := util.Uint160DecodeStringLE(ctx.String("hash"))
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid contract hash: %w", err), 1)
}
cfg.Hash = h cfg.Hash = h
f, err := os.Create(ctx.String("out")) f, err := os.Create(ctx.String("out"))
@ -106,7 +72,7 @@ func contractGenerateSomething(ctx *cli.Context, cb func(binding.Config) error)
cfg.Output = f cfg.Output = f
err = cb(cfg) err = binding.Generate(cfg)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("error during generation: %w", err), 1) return cli.NewExitError(fmt.Errorf("error during generation: %w", err), 1)
} }

View file

@ -1,9 +1,8 @@
package smartcontract package smartcontract
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -46,15 +45,6 @@ func TestGenerate(t *testing.T) {
ReturnType: smartcontract.IntegerType, ReturnType: smartcontract.IntegerType,
Safe: true, Safe: true,
}, },
manifest.Method{
Name: "zum",
Parameters: []manifest.Parameter{
manifest.NewParameter("type", smartcontract.IntegerType),
manifest.NewParameter("typev", smartcontract.IntegerType),
manifest.NewParameter("func", smartcontract.IntegerType),
},
ReturnType: smartcontract.IntegerType,
},
manifest.Method{ manifest.Method{
Name: "justExecute", Name: "justExecute",
Parameters: []manifest.Parameter{ Parameters: []manifest.Parameter{
@ -77,6 +67,13 @@ func TestGenerate(t *testing.T) {
}, },
ReturnType: smartcontract.BoolType, ReturnType: smartcontract.BoolType,
}, },
manifest.Method{
Name: "emptyName",
Parameters: []manifest.Parameter{
manifest.NewParameter("", smartcontract.MapType),
},
ReturnType: smartcontract.AnyType,
},
manifest.Method{ manifest.Method{
Name: "searchStorage", Name: "searchStorage",
Parameters: []manifest.Parameter{ Parameters: []manifest.Parameter{
@ -118,7 +115,7 @@ func TestGenerate(t *testing.T) {
rawManifest, err := json.Marshal(m) rawManifest, err := json.Marshal(m)
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm)) require.NoError(t, ioutil.WriteFile(manifestFile, rawManifest, os.ModePerm))
h := util.Uint160{ h := util.Uint160{
0x04, 0x08, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x00, 0x01, 0x04, 0x08, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x00, 0x01,
@ -142,7 +139,7 @@ callflags:
doSomething: ReadStates doSomething: ReadStates
` `
cfgPath := filepath.Join(t.TempDir(), "binding.yml") cfgPath := filepath.Join(t.TempDir(), "binding.yml")
require.NoError(t, os.WriteFile(cfgPath, []byte(rawCfg), os.ModePerm)) require.NoError(t, ioutil.WriteFile(cfgPath, []byte(rawCfg), os.ModePerm))
require.NoError(t, app.Run([]string{"", "generate-wrapper", require.NoError(t, app.Run([]string{"", "generate-wrapper",
"--manifest", manifestFile, "--manifest", manifestFile,
@ -151,9 +148,7 @@ callflags:
"--hash", h.StringLE(), "--hash", h.StringLE(),
})) }))
const expected = `// Code generated by neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT. const expected = `// Package wrapper contains wrappers for MyContract contract.
// Package wrapper contains wrappers for MyContract contract.
package wrapper package wrapper
import ( import (
@ -174,8 +169,8 @@ func Sum(first int, second int) int {
return neogointernal.CallWithToken(Hash, "sum", int(contract.All), first, second).(int) return neogointernal.CallWithToken(Hash, "sum", int(contract.All), first, second).(int)
} }
// Sum2 invokes ` + "`sum`" + ` method of contract. // Sum_3 invokes ` + "`sum`" + ` method of contract.
func Sum2(first int, second int, third int) int { func Sum_3(first int, second int, third int) int {
return neogointernal.CallWithToken(Hash, "sum", int(contract.All), first, second, third).(int) return neogointernal.CallWithToken(Hash, "sum", int(contract.All), first, second, third).(int)
} }
@ -184,13 +179,8 @@ func Sum3() int {
return neogointernal.CallWithToken(Hash, "sum3", int(contract.ReadOnly)).(int) return neogointernal.CallWithToken(Hash, "sum3", int(contract.ReadOnly)).(int)
} }
// Zum invokes ` + "`zum`" + ` method of contract.
func Zum(typev int, typev_ int, funcv int) int {
return neogointernal.CallWithToken(Hash, "zum", int(contract.All), typev, typev_, funcv).(int)
}
// JustExecute invokes ` + "`justExecute`" + ` method of contract. // JustExecute invokes ` + "`justExecute`" + ` method of contract.
func JustExecute(arr []any) { func JustExecute(arr []interface{}) {
neogointernal.CallWithTokenNoRet(Hash, "justExecute", int(contract.All), arr) neogointernal.CallWithTokenNoRet(Hash, "justExecute", int(contract.All), arr)
} }
@ -200,10 +190,15 @@ func GetPublicKey() interop.PublicKey {
} }
// OtherTypes invokes ` + "`otherTypes`" + ` method of contract. // OtherTypes invokes ` + "`otherTypes`" + ` method of contract.
func OtherTypes(ctr interop.Hash160, tx interop.Hash256, sig interop.Signature, data any) bool { func OtherTypes(ctr interop.Hash160, tx interop.Hash256, sig interop.Signature, data interface{}) bool {
return neogointernal.CallWithToken(Hash, "otherTypes", int(contract.All), ctr, tx, sig, data).(bool) return neogointernal.CallWithToken(Hash, "otherTypes", int(contract.All), ctr, tx, sig, data).(bool)
} }
// EmptyName invokes ` + "`emptyName`" + ` method of contract.
func EmptyName(arg0 map[string]interface{}) interface{} {
return neogointernal.CallWithToken(Hash, "emptyName", int(contract.All), arg0).(interface{})
}
// SearchStorage invokes ` + "`searchStorage`" + ` method of contract. // SearchStorage invokes ` + "`searchStorage`" + ` method of contract.
func SearchStorage(ctx storage.Context) iterator.Iterator { func SearchStorage(ctx storage.Context) iterator.Iterator {
return neogointernal.CallWithToken(Hash, "searchStorage", int(contract.All), ctx).(iterator.Iterator) return neogointernal.CallWithToken(Hash, "searchStorage", int(contract.All), ctx).(iterator.Iterator)
@ -215,8 +210,8 @@ func GetFromMap(intMap map[string]int, indices []string) []int {
} }
// DoSomething invokes ` + "`doSomething`" + ` method of contract. // DoSomething invokes ` + "`doSomething`" + ` method of contract.
func DoSomething(bytes []byte, str string) any { func DoSomething(bytes []byte, str string) interface{} {
return neogointernal.CallWithToken(Hash, "doSomething", int(contract.ReadStates), bytes, str).(any) return neogointernal.CallWithToken(Hash, "doSomething", int(contract.ReadStates), bytes, str).(interface{})
} }
// GetBlockWrapper invokes ` + "`getBlockWrapper`" + ` method of contract. // GetBlockWrapper invokes ` + "`getBlockWrapper`" + ` method of contract.
@ -230,102 +225,9 @@ func MyFunc(in map[int]mycontract.Input) []mycontract.Output {
} }
` `
data, err := os.ReadFile(outFile) data, err := ioutil.ReadFile(outFile)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, expected, string(data)) require.Equal(t, expected, string(data))
require.NoError(t, app.Run([]string{"", "generate-wrapper",
"--manifest", manifestFile,
"--config", cfgPath,
"--out", outFile,
}))
expectedWithDynamicHash := `// Code generated by neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package wrapper contains wrappers for MyContract contract.
package wrapper
import (
"github.com/heyitsme/mycontract"
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
)
// Contract represents the MyContract smart contract.
type Contract struct {
Hash interop.Hash160
}
// NewContract returns a new Contract instance with the specified hash.
func NewContract(hash interop.Hash160) Contract {
return Contract{Hash: hash}
}
// Sum invokes ` + "`sum`" + ` method of contract.
func (c Contract) Sum(first int, second int) int {
return contract.Call(c.Hash, "sum", contract.All, first, second).(int)
}
// Sum2 invokes ` + "`sum`" + ` method of contract.
func (c Contract) Sum2(first int, second int, third int) int {
return contract.Call(c.Hash, "sum", contract.All, first, second, third).(int)
}
// Sum3 invokes ` + "`sum3`" + ` method of contract.
func (c Contract) Sum3() int {
return contract.Call(c.Hash, "sum3", contract.ReadOnly).(int)
}
// Zum invokes ` + "`zum`" + ` method of contract.
func (c Contract) Zum(typev int, typev_ int, funcv int) int {
return contract.Call(c.Hash, "zum", contract.All, typev, typev_, funcv).(int)
}
// JustExecute invokes ` + "`justExecute`" + ` method of contract.
func (c Contract) JustExecute(arr []any) {
contract.Call(c.Hash, "justExecute", contract.All, arr)
}
// GetPublicKey invokes ` + "`getPublicKey`" + ` method of contract.
func (c Contract) GetPublicKey() interop.PublicKey {
return contract.Call(c.Hash, "getPublicKey", contract.All).(interop.PublicKey)
}
// OtherTypes invokes ` + "`otherTypes`" + ` method of contract.
func (c Contract) OtherTypes(ctr interop.Hash160, tx interop.Hash256, sig interop.Signature, data any) bool {
return contract.Call(c.Hash, "otherTypes", contract.All, ctr, tx, sig, data).(bool)
}
// SearchStorage invokes ` + "`searchStorage`" + ` method of contract.
func (c Contract) SearchStorage(ctx storage.Context) iterator.Iterator {
return contract.Call(c.Hash, "searchStorage", contract.All, ctx).(iterator.Iterator)
}
// GetFromMap invokes ` + "`getFromMap`" + ` method of contract.
func (c Contract) GetFromMap(intMap map[string]int, indices []string) []int {
return contract.Call(c.Hash, "getFromMap", contract.All, intMap, indices).([]int)
}
// DoSomething invokes ` + "`doSomething`" + ` method of contract.
func (c Contract) DoSomething(bytes []byte, str string) any {
return contract.Call(c.Hash, "doSomething", contract.ReadStates, bytes, str).(any)
}
// GetBlockWrapper invokes ` + "`getBlockWrapper`" + ` method of contract.
func (c Contract) GetBlockWrapper() ledger.Block {
return contract.Call(c.Hash, "getBlockWrapper", contract.All).(ledger.Block)
}
// MyFunc invokes ` + "`myFunc`" + ` method of contract.
func (c Contract) MyFunc(in map[int]mycontract.Input) []mycontract.Output {
return contract.Call(c.Hash, "myFunc", contract.All, in).([]mycontract.Output)
}
`
data, err = os.ReadFile(outFile)
require.NoError(t, err)
require.Equal(t, expectedWithDynamicHash, string(data))
} }
func TestGenerateValidPackageName(t *testing.T) { func TestGenerateValidPackageName(t *testing.T) {
@ -335,7 +237,6 @@ func TestGenerateValidPackageName(t *testing.T) {
Name: "get", Name: "get",
Parameters: []manifest.Parameter{}, Parameters: []manifest.Parameter{},
ReturnType: smartcontract.IntegerType, ReturnType: smartcontract.IntegerType,
Safe: true,
}, },
) )
@ -344,25 +245,23 @@ func TestGenerateValidPackageName(t *testing.T) {
rawManifest, err := json.Marshal(m) rawManifest, err := json.Marshal(m)
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm)) require.NoError(t, ioutil.WriteFile(manifestFile, rawManifest, os.ModePerm))
h := util.Uint160{ h := util.Uint160{
0x04, 0x08, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x00, 0x01, 0x04, 0x08, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x00, 0x01,
0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF, 0x03, 0x04, 0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF, 0x03, 0x04,
} }
app := cli.NewApp() app := cli.NewApp()
app.Commands = []cli.Command{generateWrapperCmd, generateRPCWrapperCmd} app.Commands = []cli.Command{generateWrapperCmd}
require.NoError(t, app.Run([]string{"", "generate-wrapper", require.NoError(t, app.Run([]string{"", "generate-wrapper",
"--manifest", manifestFile, "--manifest", manifestFile,
"--out", outFile, "--out", outFile,
"--hash", "0x" + h.StringLE(), "--hash", h.StringLE(),
})) }))
data, err := os.ReadFile(outFile) data, err := ioutil.ReadFile(outFile)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, `// Code generated by neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT. require.Equal(t, `// Package myspacecontract contains wrappers for My space contract contract.
// Package myspacecontract contains wrappers for My space contract contract.
package myspacecontract package myspacecontract
import ( import (
@ -375,176 +274,9 @@ const Hash = "\x04\x08\x15\x16\x23\x42\x43\x44\x00\x01\xca\xfe\xba\xbe\xde\xad\x
// Get invokes `+"`get`"+` method of contract. // Get invokes `+"`get`"+` method of contract.
func Get() int { func Get() int {
return neogointernal.CallWithToken(Hash, "get", int(contract.ReadOnly)).(int) return neogointernal.CallWithToken(Hash, "get", int(contract.All)).(int)
} }
`, string(data)) `, string(data))
require.NoError(t, app.Run([]string{"", "generate-rpcwrapper",
"--manifest", manifestFile,
"--out", outFile,
"--hash", "0x" + h.StringLE(),
}))
data, err = os.ReadFile(outFile)
require.NoError(t, err)
require.Equal(t, `// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package myspacecontract contains RPC wrappers for My space contract contract.
package myspacecontract
import (
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"math/big"
)
// Hash contains contract hash.
var Hash = util.Uint160{0x4, 0x8, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x0, 0x1, 0xca, 0xfe, 0xba, 0xbe, 0xde, 0xad, 0xbe, 0xef, 0x3, 0x4}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
invoker Invoker
hash util.Uint160
}
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
func NewReader(invoker Invoker) *ContractReader {
var hash = Hash
return &ContractReader{invoker, hash}
}
// Get invokes `+"`get`"+` method of contract.
func (c *ContractReader) Get() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "get"))
}
`, string(data))
}
// rewriteExpectedOutputs denotes whether expected output files should be rewritten
// for TestGenerateRPCBindings and TestAssistedRPCBindings.
const rewriteExpectedOutputs = false
func TestGenerateRPCBindings(t *testing.T) {
tmpDir := t.TempDir()
app := cli.NewApp()
app.Commands = []cli.Command{generateWrapperCmd, generateRPCWrapperCmd}
var checkBinding = func(manifest string, hash string, good string) {
t.Run(manifest, func(t *testing.T) {
outFile := filepath.Join(tmpDir, "out.go")
require.NoError(t, app.Run([]string{"", "generate-rpcwrapper",
"--manifest", manifest,
"--out", outFile,
"--hash", hash,
}))
data, err := os.ReadFile(outFile)
require.NoError(t, err)
data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows.
if rewriteExpectedOutputs {
require.NoError(t, os.WriteFile(good, data, os.ModePerm))
} else {
expected, err := os.ReadFile(good)
require.NoError(t, err)
expected = bytes.ReplaceAll(expected, []byte("\r"), []byte{}) // Windows.
require.Equal(t, string(expected), string(data))
}
})
}
checkBinding(filepath.Join("testdata", "nex", "nex.manifest.json"),
"0xa2a67f09e8cf22c6bfd5cea24adc0f4bf0a11aa8",
filepath.Join("testdata", "nex", "nex.go"))
checkBinding(filepath.Join("testdata", "nameservice", "nns.manifest.json"),
"0x50ac1c37690cc2cfc594472833cf57505d5f46de",
filepath.Join("testdata", "nameservice", "nns.go"))
checkBinding(filepath.Join("testdata", "gas", "gas.manifest.json"),
"0xd2a4cff31913016155e38e474a2c06d08be276cf",
filepath.Join("testdata", "gas", "gas.go"))
checkBinding(filepath.Join("testdata", "verifyrpc", "verify.manifest.json"),
"0x00112233445566778899aabbccddeeff00112233",
filepath.Join("testdata", "verifyrpc", "verify.go"))
checkBinding(filepath.Join("testdata", "nonepiter", "iter.manifest.json"),
"0x00112233445566778899aabbccddeeff00112233",
filepath.Join("testdata", "nonepiter", "iter.go"))
require.False(t, rewriteExpectedOutputs)
}
func TestAssistedRPCBindings(t *testing.T) {
tmpDir := t.TempDir()
app := cli.NewApp()
app.Commands = NewCommands()
var checkBinding = func(source string, hasDefinedHash bool, guessEventTypes bool, suffix ...string) {
testName := source
if len(suffix) != 0 {
testName += suffix[0]
}
testName += fmt.Sprintf(", predefined hash: %t", hasDefinedHash)
t.Run(testName, func(t *testing.T) {
outFile := filepath.Join(tmpDir, "out.go")
configFile := filepath.Join(source, "config.yml")
expectedFile := filepath.Join(source, "rpcbindings.out")
if len(suffix) != 0 {
configFile = filepath.Join(source, "config"+suffix[0]+".yml")
expectedFile = filepath.Join(source, "rpcbindings"+suffix[0]+".out")
} else if !hasDefinedHash {
expectedFile = filepath.Join(source, "rpcbindings_dynamic_hash.out")
}
manifestF := filepath.Join(tmpDir, "manifest.json")
bindingF := filepath.Join(tmpDir, "binding.yml")
nefF := filepath.Join(tmpDir, "out.nef")
cmd := []string{"", "contract", "compile",
"--in", source,
"--config", configFile,
"--manifest", manifestF,
"--bindings", bindingF,
"--out", nefF,
}
if guessEventTypes {
cmd = append(cmd, "--guess-eventtypes")
}
require.NoError(t, app.Run(cmd))
cmds := []string{"", "contract", "generate-rpcwrapper",
"--config", bindingF,
"--manifest", manifestF,
"--out", outFile,
}
if hasDefinedHash {
cmds = append(cmds, "--hash", "0x00112233445566778899aabbccddeeff00112233")
}
require.NoError(t, app.Run(cmds))
data, err := os.ReadFile(outFile)
require.NoError(t, err)
data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows.
if rewriteExpectedOutputs {
require.NoError(t, os.WriteFile(expectedFile, data, os.ModePerm))
} else {
expected, err := os.ReadFile(expectedFile)
require.NoError(t, err)
expected = bytes.ReplaceAll(expected, []byte("\r"), []byte{}) // Windows.
require.Equal(t, string(expected), string(data))
}
})
}
for _, hasDefinedHash := range []bool{true, false} {
checkBinding(filepath.Join("testdata", "rpcbindings", "types"), hasDefinedHash, false)
checkBinding(filepath.Join("testdata", "rpcbindings", "structs"), hasDefinedHash, false)
}
checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), true, false)
checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), true, false, "_extended")
checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), true, true, "_guessed")
require.False(t, rewriteExpectedOutputs)
} }
func TestGenerate_Errors(t *testing.T) { func TestGenerate_Errors(t *testing.T) {
@ -557,45 +289,31 @@ func TestGenerate_Errors(t *testing.T) {
err := app.Run(append([]string{"", "generate-wrapper"}, args...)) err := app.Run(append([]string{"", "generate-wrapper"}, args...))
require.True(t, strings.Contains(err.Error(), msg), "got: %v", err) require.True(t, strings.Contains(err.Error(), msg), "got: %v", err)
} }
t.Run("invalid hash", func(t *testing.T) {
checkError(t, "invalid contract hash", "--hash", "xxx", "--manifest", "yyy", "--out", "zzz")
})
t.Run("missing manifest argument", func(t *testing.T) { t.Run("missing manifest argument", func(t *testing.T) {
checkError(t, "Required flag \"manifest\" not set", "--hash", util.Uint160{}.StringLE(), "--out", "zzz") checkError(t, errNoManifestFile.Error())
}) })
t.Run("missing manifest file", func(t *testing.T) { t.Run("missing manifest file", func(t *testing.T) {
checkError(t, "can't read contract manifest", "--manifest", "notexists", "--hash", util.Uint160{}.StringLE(), "--out", "zzz") checkError(t, "can't read contract manifest", "--manifest", "notexists")
})
t.Run("empty manifest", func(t *testing.T) {
manifestFile := filepath.Join(t.TempDir(), "invalid.json")
require.NoError(t, os.WriteFile(manifestFile, []byte("[]"), os.ModePerm))
checkError(t, "json: cannot unmarshal array into Go value of type manifest.Manifest", "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(), "--out", "zzz")
}) })
t.Run("invalid manifest", func(t *testing.T) { t.Run("invalid manifest", func(t *testing.T) {
manifestFile := filepath.Join(t.TempDir(), "invalid.json") manifestFile := filepath.Join(t.TempDir(), "invalid.json")
m := manifest.NewManifest("MyContract") // no methods require.NoError(t, ioutil.WriteFile(manifestFile, []byte("[]"), os.ModePerm))
rawManifest, err := json.Marshal(m) checkError(t, "", "--manifest", manifestFile)
require.NoError(t, err)
require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm))
checkError(t, "ABI: no methods", "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(), "--out", "zzz")
}) })
manifestFile := filepath.Join(t.TempDir(), "manifest.json") manifestFile := filepath.Join(t.TempDir(), "manifest.json")
m := manifest.NewManifest("MyContract") m := manifest.NewManifest("MyContract")
m.ABI.Methods = append(m.ABI.Methods, manifest.Method{
Name: "method0",
Offset: 0,
ReturnType: smartcontract.AnyType,
Safe: true,
})
rawManifest, err := json.Marshal(m) rawManifest, err := json.Marshal(m)
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm)) require.NoError(t, ioutil.WriteFile(manifestFile, rawManifest, os.ModePerm))
t.Run("invalid hash", func(t *testing.T) {
checkError(t, "invalid contract hash", "--manifest", manifestFile, "--hash", "xxx")
})
t.Run("missing config", func(t *testing.T) { t.Run("missing config", func(t *testing.T) {
checkError(t, "can't read config file", checkError(t, "can't read config file",
"--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(), "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(),
"--config", filepath.Join(t.TempDir(), "not.exists.yml"), "--out", "zzz") "--config", filepath.Join(t.TempDir(), "not.exists.yml"))
}) })
t.Run("invalid config", func(t *testing.T) { t.Run("invalid config", func(t *testing.T) {
rawCfg := `package: wrapper rawCfg := `package: wrapper
@ -603,110 +321,10 @@ callflags:
someFunc: ReadSometimes someFunc: ReadSometimes
` `
cfgPath := filepath.Join(t.TempDir(), "binding.yml") cfgPath := filepath.Join(t.TempDir(), "binding.yml")
require.NoError(t, os.WriteFile(cfgPath, []byte(rawCfg), os.ModePerm)) require.NoError(t, ioutil.WriteFile(cfgPath, []byte(rawCfg), os.ModePerm))
checkError(t, "can't parse config file", checkError(t, "can't parse config file",
"--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(), "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(),
"--config", cfgPath, "--out", "zzz") "--config", cfgPath)
})
}
func TestCompile_GuessEventTypes(t *testing.T) {
app := cli.NewApp()
app.Commands = NewCommands()
app.ExitErrHandler = func(*cli.Context, error) {}
checkError := func(t *testing.T, msg string, args ...string) {
// cli.ExitError doesn't implement wraping properly, so we check for an error message.
err := app.Run(args)
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), msg), "got: %v", err)
}
check := func(t *testing.T, source string, expectedErrText string) {
tmpDir := t.TempDir()
configFile := filepath.Join(source, "invalid.yml")
manifestF := filepath.Join(tmpDir, "invalid.manifest.json")
bindingF := filepath.Join(tmpDir, "invalid.binding.yml")
nefF := filepath.Join(tmpDir, "invalid.out.nef")
cmd := []string{"", "contract", "compile",
"--in", source,
"--config", configFile,
"--manifest", manifestF,
"--bindings", bindingF,
"--out", nefF,
"--guess-eventtypes",
}
checkError(t, expectedErrText, cmd...)
}
t.Run("not declared in manifest", func(t *testing.T) {
check(t, filepath.Join("testdata", "rpcbindings", "invalid1"), "inconsistent usages of event `Non declared event`: not declared in the contract config")
})
t.Run("invalid number of params", func(t *testing.T) {
check(t, filepath.Join("testdata", "rpcbindings", "invalid2"), "inconsistent usages of event `SomeEvent` against config: number of params mismatch: 2 vs 1")
})
/*
// TODO: this on is a controversial one. If event information is provided in the config file, then conversion code
// will be emitted by the compiler according to the parameter type provided via config. Thus, we can be sure that
// either event parameter has the type specified in the config file or the execution of the contract will fail.
// Thus, this testcase is always failing (no compilation error occures).
// Question: do we want to compare `RealType` of the emitted parameter with the one expected in the manifest?
t.Run("SC parameter type mismatch", func(t *testing.T) {
check(t, filepath.Join("testdata", "rpcbindings", "invalid3"), "inconsistent usages of event `SomeEvent` against config: number of params mismatch: 2 vs 1")
})
*/
t.Run("extended types mismatch", func(t *testing.T) {
check(t, filepath.Join("testdata", "rpcbindings", "invalid4"), "inconsistent usages of event `SomeEvent`: extended type of param #0 mismatch")
})
t.Run("named types redeclare", func(t *testing.T) {
check(t, filepath.Join("testdata", "rpcbindings", "invalid5"), "configured declared named type intersects with the contract's one: `invalid5.NamedStruct`")
})
}
func TestGenerateRPCBindings_Errors(t *testing.T) {
app := cli.NewApp()
app.Commands = NewCommands()
app.ExitErrHandler = func(*cli.Context, error) {}
t.Run("duplicating resulting fields", func(t *testing.T) {
check := func(t *testing.T, packageName string, autogen bool, expectedError string) {
tmpDir := t.TempDir()
source := filepath.Join("testdata", "rpcbindings", packageName)
configFile := filepath.Join(source, "invalid.yml")
out := filepath.Join(tmpDir, "rpcbindings.out")
manifestF := filepath.Join(tmpDir, "manifest.json")
bindingF := filepath.Join(tmpDir, "binding.yml")
nefF := filepath.Join(tmpDir, "out.nef")
cmd := []string{"", "contract", "compile",
"--in", source,
"--config", configFile,
"--manifest", manifestF,
"--bindings", bindingF,
"--out", nefF,
}
if autogen {
cmd = append(cmd, "--guess-eventtypes")
}
require.NoError(t, app.Run(cmd))
cmds := []string{"", "contract", "generate-rpcwrapper",
"--config", bindingF,
"--manifest", manifestF,
"--out", out,
}
err := app.Run(cmds)
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), expectedError), err.Error())
}
t.Run("event", func(t *testing.T) {
check(t, "invalid6", false, "error during generation: named type `SomeStruct` has two fields with identical resulting binding name `Field`")
})
t.Run("autogen event", func(t *testing.T) {
check(t, "invalid7", true, "error during generation: named type `invalid7.SomeStruct` has two fields with identical resulting binding name `Field`")
})
t.Run("struct", func(t *testing.T) {
check(t, "invalid8", false, "error during generation: named type `invalid8.SomeStruct` has two fields with identical resulting binding name `Field`")
})
}) })
} }

View file

@ -4,22 +4,34 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/cli/flags" "github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
func manifestAddGroup(ctx *cli.Context) error { func manifestAddGroup(ctx *cli.Context) error {
if err := cmdargs.EnsureNone(ctx); err != nil { walletPath := ctx.String("wallet")
return err if len(walletPath) == 0 {
return cli.NewExitError(errNoWallet, 1)
} }
w, err := wallet.NewWalletFromFile(walletPath)
if err != nil {
return cli.NewExitError(err, 1)
}
defer w.Close()
addr, err := flags.ParseAddress(ctx.String("account"))
if err != nil {
return cli.NewExitError(fmt.Errorf("account is invalid or missing: %w", err), 1)
}
sender, err := flags.ParseAddress(ctx.String("sender")) sender, err := flags.ParseAddress(ctx.String("sender"))
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("invalid sender: %w", err), 1) return cli.NewExitError(fmt.Errorf("invalid sender: %w", err), 1)
@ -31,23 +43,22 @@ func manifestAddGroup(ctx *cli.Context) error {
} }
mPath := ctx.String("manifest") mPath := ctx.String("manifest")
m, _, err := readManifest(mPath, util.Uint160{}) m, _, err := readManifest(mPath)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't read contract manifest: %w", err), 1) return cli.NewExitError(fmt.Errorf("can't read contract manifest: %w", err), 1)
} }
h := state.CreateContractHash(sender, nf.Checksum, m.Name) h := state.CreateContractHash(sender, nf.Checksum, m.Name)
gAcc, w, err := options.GetAccFromContext(ctx) gAcc, err := getUnlockedAccount(w, addr)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't get account to sign group with: %w", err), 1) return err
} }
defer w.Close()
var found bool var found bool
sig := gAcc.PrivateKey().Sign(h.BytesBE()) sig := gAcc.PrivateKey().Sign(h.BytesBE())
pub := gAcc.PublicKey() pub := gAcc.PrivateKey().PublicKey()
for i := range m.Groups { for i := range m.Groups {
if m.Groups[i].PublicKey.Equal(pub) { if m.Groups[i].PublicKey.Equal(pub) {
m.Groups[i].Signature = sig m.Groups[i].Signature = sig
@ -67,7 +78,7 @@ func manifestAddGroup(ctx *cli.Context) error {
return cli.NewExitError(fmt.Errorf("can't marshal manifest: %w", err), 1) return cli.NewExitError(fmt.Errorf("can't marshal manifest: %w", err), 1)
} }
err = os.WriteFile(mPath, rawM, os.ModePerm) err = ioutil.WriteFile(mPath, rawM, os.ModePerm)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't write manifest file: %w", err), 1) return cli.NewExitError(fmt.Errorf("can't write manifest file: %w", err), 1)
} }
@ -79,7 +90,7 @@ func readNEFFile(filename string) (*nef.File, []byte, error) {
return nil, nil, errors.New("no nef file was provided") return nil, nil, errors.New("no nef file was provided")
} }
f, err := os.ReadFile(filename) f, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -92,15 +103,12 @@ func readNEFFile(filename string) (*nef.File, []byte, error) {
return &nefFile, f, nil return &nefFile, f, nil
} }
// readManifest unmarshalls manifest got from the provided filename and checks func readManifest(filename string) (*manifest.Manifest, []byte, error) {
// it for validness against the provided contract hash. If empty hash is specified
// then no hash-related manifest groups check is performed.
func readManifest(filename string, hash util.Uint160) (*manifest.Manifest, []byte, error) {
if len(filename) == 0 { if len(filename) == 0 {
return nil, nil, errNoManifestFile return nil, nil, errNoManifestFile
} }
manifestBytes, err := os.ReadFile(filename) manifestBytes, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -110,8 +118,5 @@ func readManifest(filename string, hash util.Uint160) (*manifest.Manifest, []byt
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
if err := m.IsValid(hash, true); err != nil {
return nil, nil, fmt.Errorf("manifest is invalid: %w", err)
}
return m, manifestBytes, nil return m, manifestBytes, nil
} }

View file

@ -1,13 +1,14 @@
package smartcontract package smartcontract
import ( import (
"encoding/hex"
"errors" "errors"
"fmt" "fmt"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v2"
) )
type permission manifest.Permission type permission manifest.Permission
@ -18,41 +19,39 @@ const (
permMethodKey = "methods" permMethodKey = "methods"
) )
func (p permission) MarshalYAML() (any, error) { func (p permission) MarshalYAML() (interface{}, error) {
m := yaml.Node{Kind: yaml.MappingNode} m := make(yaml.MapSlice, 0, 2)
switch p.Contract.Type { switch p.Contract.Type {
case manifest.PermissionWildcard: case manifest.PermissionWildcard:
case manifest.PermissionHash: case manifest.PermissionHash:
m.Content = append(m.Content, m = append(m, yaml.MapItem{
&yaml.Node{Kind: yaml.ScalarNode, Value: permHashKey}, Key: permHashKey,
&yaml.Node{Kind: yaml.ScalarNode, Value: p.Contract.Value.(util.Uint160).StringLE()}) Value: p.Contract.Value.(util.Uint160).StringLE(),
})
case manifest.PermissionGroup: case manifest.PermissionGroup:
m.Content = append(m.Content, bs := p.Contract.Value.(*keys.PublicKey).Bytes()
&yaml.Node{Kind: yaml.ScalarNode, Value: permGroupKey}, m = append(m, yaml.MapItem{
&yaml.Node{Kind: yaml.ScalarNode, Value: p.Contract.Value.(*keys.PublicKey).StringCompressed()}) Key: permGroupKey,
Value: hex.EncodeToString(bs),
})
default: default:
return nil, fmt.Errorf("invalid permission type: %d", p.Contract.Type) return nil, fmt.Errorf("invalid permission type: %d", p.Contract.Type)
} }
var val any = "*" var val interface{} = "*"
if !p.Methods.IsWildcard() { if !p.Methods.IsWildcard() {
val = p.Methods.Value val = p.Methods.Value
} }
n := &yaml.Node{Kind: yaml.ScalarNode} m = append(m, yaml.MapItem{
err := n.Encode(val) Key: permMethodKey,
if err != nil { Value: val,
return nil, err })
}
m.Content = append(m.Content,
&yaml.Node{Kind: yaml.ScalarNode, Value: permMethodKey},
n)
return m, nil return m, nil
} }
func (p *permission) UnmarshalYAML(unmarshal func(any) error) error { func (p *permission) UnmarshalYAML(unmarshal func(interface{}) error) error {
var m map[string]any var m map[string]interface{}
if err := unmarshal(&m); err != nil { if err := unmarshal(&m); err != nil {
return err return err
} }
@ -64,7 +63,7 @@ func (p *permission) UnmarshalYAML(unmarshal func(any) error) error {
return p.fillMethods(m) return p.fillMethods(m)
} }
func (p *permission) fillType(m map[string]any) error { func (p *permission) fillType(m map[string]interface{}) error {
vh, ok1 := m[permHashKey] vh, ok1 := m[permHashKey]
vg, ok2 := m[permGroupKey] vg, ok2 := m[permGroupKey]
switch { switch {
@ -102,7 +101,7 @@ func (p *permission) fillType(m map[string]any) error {
return nil return nil
} }
func (p *permission) fillMethods(m map[string]any) error { func (p *permission) fillMethods(m map[string]interface{}) error {
methods, ok := m[permMethodKey] methods, ok := m[permMethodKey]
if !ok { if !ok {
return errors.New("'methods' field is missing from permission") return errors.New("'methods' field is missing from permission")
@ -114,7 +113,7 @@ func (p *permission) fillMethods(m map[string]any) error {
p.Methods.Value = nil p.Methods.Value = nil
return nil return nil
} }
case []any: case []interface{}:
ms := make([]string, len(mt)) ms := make([]string, len(mt))
for i := range mt { for i := range mt {
ms[i], ok = mt[i].(string) ms[i], ok = mt[i].(string)

View file

@ -5,48 +5,68 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/nspcc-dev/neo-go/cli/cmdargs" "github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/cli/flags" "github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/cli/input"
"github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/cli/txctx" "github.com/nspcc-dev/neo-go/cli/paramcontext"
"github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpc/client"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/binding"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli" "github.com/urfave/cli"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v2"
) )
// addressFlagName is a flag name used for address-related operations. It should be
// the same within the smartcontract package, thus, use this constant.
const addressFlagName = "address, a"
var ( var (
errNoInput = errors.New("no input file was found, specify an input file with the '--in or -i' flag") errNoInput = errors.New("no input file was found, specify an input file with the '--in or -i' flag")
errNoConfFile = errors.New("no config file was found, specify a config file with the '--config' or '-c' flag") errNoConfFile = errors.New("no config file was found, specify a config file with the '--config' or '-c' flag")
errNoManifestFile = errors.New("no manifest file was found, specify manifest file with '--manifest' or '-m' flag") errNoManifestFile = errors.New("no manifest file was found, specify manifest file with '--manifest' or '-m' flag")
errNoMethod = errors.New("no method specified for function invocation command") errNoMethod = errors.New("no method specified for function invocation command")
errNoWallet = errors.New("no wallet parameter found, specify it with the '--wallet or -w' flag")
errNoScriptHash = errors.New("no smart contract hash was provided, specify one as the first argument") errNoScriptHash = errors.New("no smart contract hash was provided, specify one as the first argument")
errNoSmartContractName = errors.New("no name was provided, specify the '--name or -n' flag") errNoSmartContractName = errors.New("no name was provided, specify the '--name or -n' flag")
errFileExist = errors.New("A file with given smart-contract name already exists") errFileExist = errors.New("A file with given smart-contract name already exists")
addressFlag = flags.AddressFlag{
Name: addressFlagName, walletFlag = cli.StringFlag{
Name: "wallet, w",
Usage: "wallet to use to get the key for transaction signing",
}
addressFlag = flags.AddressFlag{
Name: "address, a",
Usage: "address to use as transaction signee (and gas source)", Usage: "address to use as transaction signee (and gas source)",
} }
gasFlag = flags.Fixed8Flag{
Name: "gas, g",
Usage: "network fee to add to the transaction (prioritizing it)",
}
sysGasFlag = flags.Fixed8Flag{
Name: "sysgas, e",
Usage: "system fee to add to transaction (compensating for execution)",
}
outFlag = cli.StringFlag{
Name: "out",
Usage: "file to put JSON transaction to",
}
forceFlag = cli.BoolFlag{
Name: "force",
Usage: "force-push the transaction in case of bad VM state after test script invocation",
}
) )
// ModVersion contains `pkg/interop` module version // ModVersion contains `pkg/interop` module version
@ -68,7 +88,7 @@ func init() {
} }
// RuntimeNotify sends runtime notification with "Hello world!" name // RuntimeNotify sends runtime notification with "Hello world!" name
func RuntimeNotify(args []any) { func RuntimeNotify(args []interface{}) {
runtime.Notify(notificationName, args) runtime.Notify(notificationName, args)
}` }`
) )
@ -80,20 +100,16 @@ func NewCommands() []cli.Command {
Name: "in, i", Name: "in, i",
Usage: "Input location of the .nef file that needs to be invoked", Usage: "Input location of the .nef file that needs to be invoked",
}, },
options.Historic,
} }
testInvokeScriptFlags = append(testInvokeScriptFlags, options.RPC...) testInvokeScriptFlags = append(testInvokeScriptFlags, options.RPC...)
testInvokeFunctionFlags := []cli.Flag{options.Historic}
testInvokeFunctionFlags = append(testInvokeFunctionFlags, options.RPC...)
invokeFunctionFlags := []cli.Flag{ invokeFunctionFlags := []cli.Flag{
walletFlag,
addressFlag, addressFlag,
txctx.GasFlag, gasFlag,
txctx.SysGasFlag, sysGasFlag,
txctx.OutFlag, outFlag,
txctx.ForceFlag, forceFlag,
txctx.AwaitFlag,
} }
invokeFunctionFlags = append(invokeFunctionFlags, options.Wallet...)
invokeFunctionFlags = append(invokeFunctionFlags, options.RPC...) invokeFunctionFlags = append(invokeFunctionFlags, options.RPC...)
deployFlags := append(invokeFunctionFlags, []cli.Flag{ deployFlags := append(invokeFunctionFlags, []cli.Flag{
cli.StringFlag{ cli.StringFlag{
@ -105,45 +121,18 @@ func NewCommands() []cli.Command {
Usage: "Manifest input file (*.manifest.json)", Usage: "Manifest input file (*.manifest.json)",
}, },
}...) }...)
manifestAddGroupFlags := append([]cli.Flag{
cli.StringFlag{
Name: "sender, s",
Usage: "deploy transaction sender",
},
flags.AddressFlag{
Name: addressFlagName, // use the same name for handler code unification.
Usage: "account to sign group with",
},
cli.StringFlag{
Name: "nef, n",
Usage: "path to the NEF file",
},
cli.StringFlag{
Name: "manifest, m",
Usage: "path to the manifest",
},
}, options.Wallet...)
return []cli.Command{{ return []cli.Command{{
Name: "contract", Name: "contract",
Usage: "compile - debug - deploy smart contracts", Usage: "compile - debug - deploy smart contracts",
Subcommands: []cli.Command{ Subcommands: []cli.Command{
{ {
Name: "compile", Name: "compile",
Usage: "compile a smart contract to a .nef file", Usage: "compile a smart contract to a .nef file",
UsageText: "neo-go contract compile -i path [-o nef] [-v] [-d] [-m manifest] [-c yaml] [--bindings file] [--no-standards] [--no-events] [--no-permissions] [--guess-eventtypes]",
Description: `Compiles given smart contract to a .nef file and emits other associated
information (manifest, bindings configuration, debug information files) if
asked to. If none of --out, --manifest, --config, --bindings flags are specified,
then the output filenames for these flags will be guessed using the contract
name or path provided via --in option by trimming/adding corresponding suffixes
to the common part of the path. In the latter case the configuration filepath
will be guessed from the --in option using the same rule.
`,
Action: contractCompile, Action: contractCompile,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "in, i", Name: "in, i",
Usage: "Input file for the smart contract to be compiled (*.go file or directory)", Usage: "Input file for the smart contract to be compiled",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "out, o", Name: "out, o",
@ -177,42 +166,30 @@ func NewCommands() []cli.Command {
Name: "no-permissions", Name: "no-permissions",
Usage: "do not check if invoked contracts are allowed in manifest", Usage: "do not check if invoked contracts are allowed in manifest",
}, },
cli.BoolFlag{
Name: "guess-eventtypes",
Usage: "guess event types for smart-contract bindings configuration from the code usages",
},
cli.StringFlag{
Name: "bindings",
Usage: "output file for smart-contract bindings configuration",
},
}, },
}, },
{ {
Name: "deploy", Name: "deploy",
Usage: "deploy a smart contract (.nef with description)", Usage: "deploy a smart contract (.nef with description)",
UsageText: "neo-go contract deploy -r endpoint -w wallet [-a address] [-g gas] [-e sysgas] --in contract.nef --manifest contract.manifest.json [--out file] [--force] [--await] [data]", UsageText: "neo-go contract deploy -r endpoint -w wallet [-a address] [-g gas] [-e sysgas] --in contract.nef --manifest contract.manifest.json [--out file] [--force] [data]",
Description: `Deploys given contract into the chain. The gas parameter is for additional Description: `Deploys given contract into the chain. The gas parameter is for additional
gas to be added as a network fee to prioritize the transaction. The data gas to be added as a network fee to prioritize the transaction. The data
parameter is an optional parameter to be passed to '_deploy' method. When parameter is an optional parameter to be passed to '_deploy' method.
--await flag is specified, it waits for the transaction to be included
in a block.
`, `,
Action: contractDeploy, Action: contractDeploy,
Flags: deployFlags, Flags: deployFlags,
}, },
generateWrapperCmd, generateWrapperCmd,
generateRPCWrapperCmd,
{ {
Name: "invokefunction", Name: "invokefunction",
Usage: "invoke deployed contract on the blockchain", Usage: "invoke deployed contract on the blockchain",
UsageText: "neo-go contract invokefunction -r endpoint -w wallet [-a address] [-g gas] [-e sysgas] [--out file] [--force] [--await] scripthash [method] [arguments...] [--] [signers...]", UsageText: "neo-go contract invokefunction -r endpoint -w wallet [-a address] [-g gas] [-e sysgas] [--out file] [--force] scripthash [method] [arguments...] [--] [signers...]",
Description: `Executes given (as a script hash) deployed script with the given method, Description: `Executes given (as a script hash) deployed script with the given method,
arguments and signers. Sender is included in the list of signers by default arguments and signers. Sender is included in the list of signers by default
with None witness scope. If you'd like to change default sender's scope, with None witness scope. If you'd like to change default sender's scope,
specify it via signers parameter. See testinvokefunction documentation for specify it via signers parameter. See testinvokefunction documentation for
the details about parameters. It differs from testinvokefunction in that this the details about parameters. It differs from testinvokefunction in that this
command sends an invocation transaction to the network. When --await flag is command sends an invocation transaction to the network.
specified, it waits for the transaction to be included in a block.
`, `,
Action: invokeFunction, Action: invokeFunction,
Flags: invokeFunctionFlags, Flags: invokeFunctionFlags,
@ -220,7 +197,7 @@ func NewCommands() []cli.Command {
{ {
Name: "testinvokefunction", Name: "testinvokefunction",
Usage: "invoke deployed contract on the blockchain (test mode)", Usage: "invoke deployed contract on the blockchain (test mode)",
UsageText: "neo-go contract testinvokefunction -r endpoint [--historic index/hash] scripthash [method] [arguments...] [--] [signers...]", UsageText: "neo-go contract testinvokefunction -r endpoint scripthash [method] [arguments...] [--] [signers...]",
Description: `Executes given (as a script hash) deployed script with the given method, Description: `Executes given (as a script hash) deployed script with the given method,
arguments and signers (sender is not included by default). If no method is given arguments and signers (sender is not included by default). If no method is given
"" is passed to the script, if no arguments are given, an empty array is "" is passed to the script, if no arguments are given, an empty array is
@ -230,17 +207,117 @@ func NewCommands() []cli.Command {
follow the regular convention of smart contract arguments (method string and follow the regular convention of smart contract arguments (method string and
an array of other arguments). an array of other arguments).
` + cmdargs.ParamsParsingDoc + ` Arguments always do have regular Neo smart contract parameter types, either
specified explicitly or being inferred from the value. To specify the type
manually use "type:value" syntax where the type is one of the following:
'signature', 'bool', 'int', 'hash160', 'hash256', 'bytes', 'key' or 'string'.
Array types are also supported: use special space-separated '[' and ']'
symbols around array values to denote array bounds. Nested arrays are also
supported.
` + cmdargs.SignersParsingDoc + ` There is ability to provide an argument of 'bytearray' type via file. Use a
special 'filebytes' argument type for this with a filepath specified after
the colon, e.g. 'filebytes:my_file.txt'.
Given values are type-checked against given types with the following
restrictions applied:
* 'signature' type values should be hex-encoded and have a (decoded)
length of 64 bytes.
* 'bool' type values are 'true' and 'false'.
* 'int' values are decimal integers that can be successfully converted
from the string.
* 'hash160' values are Neo addresses and hex-encoded 20-bytes long (after
decoding) strings.
* 'hash256' type values should be hex-encoded and have a (decoded)
length of 32 bytes.
* 'bytes' type values are any hex-encoded things.
* 'filebytes' type values are filenames with the argument value inside.
* 'key' type values are hex-encoded marshalled public keys.
* 'string' type values are any valid UTF-8 strings. In the value's part of
the string the colon looses it's special meaning as a separator between
type and value and is taken literally.
If no type is explicitly specified, it is inferred from the value using the
following logic:
- anything that can be interpreted as a decimal integer gets
an 'int' type
- 'true' and 'false' strings get 'bool' type
- valid Neo addresses and 20 bytes long hex-encoded strings get 'hash160'
type
- valid hex-encoded public keys get 'key' type
- 32 bytes long hex-encoded values get 'hash256' type
- 64 bytes long hex-encoded values get 'signature' type
- any other valid hex-encoded values get 'bytes' type
- anything else is a 'string'
Backslash character is used as an escape character and allows to use colon in
an implicitly typed string. For any other characters it has no special
meaning, to get a literal backslash in the string use the '\\' sequence.
Examples:
* 'int:42' is an integer with a value of 42
* '42' is an integer with a value of 42
* 'bad' is a string with a value of 'bad'
* 'dead' is a byte array with a value of 'dead'
* 'string:dead' is a string with a value of 'dead'
* 'filebytes:my_data.txt' is bytes decoded from a content of my_data.txt
* 'AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y' is a hash160 with a value
of '23ba2703c53263e8d6e522dc32203339dcd8eee9'
* '\4\2' is an integer with a value of 42
* '\\4\2' is a string with a value of '\42'
* 'string:string' is a string with a value of 'string'
* 'string\:string' is a string with a value of 'string:string'
* '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c' is a
key with a value of '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c'
* '[ a b c ]' is an array with strings values 'a', 'b' and 'c'
* '[ a b [ c d ] e ]' is an array with 4 values: string 'a', string 'b',
array of two strings 'c' and 'd', string 'e'
* '[ ]' is an empty array
Signers represent a set of Uint160 hashes with witness scopes and are used
to verify hashes in System.Runtime.CheckWitness syscall. First signer is treated
as a sender. To specify signers use signer[:scope] syntax where
* 'signer' is a signer's address (as Neo address or hex-encoded 160 bit (20 byte)
LE value with or without '0x' prefix).
* 'scope' is a comma-separated set of cosigner's scopes, which could be:
- 'None' - default witness scope which may be used for the sender
to only pay fee for the transaction.
- 'Global' - allows this witness in all contexts. This cannot be combined
with other flags.
- 'CalledByEntry' - means that this condition must hold: EntryScriptHash
== CallingScriptHash. The witness/permission/signature
given on first invocation will automatically expire if
entering deeper internal invokes. This can be default
safe choice for native NEO/GAS.
- 'CustomContracts' - define valid custom contract hashes for witness check.
Hashes are be provided as hex-encoded LE value string.
At lest one hash must be provided. Multiple hashes
are separated by ':'.
- 'CustomGroups' - define custom public keys for group members. Public keys are
provided as short-form (1-byte prefix + 32 bytes) hex-encoded
values. At least one key must be provided. Multiple keys
are separated by ':'.
If no scopes were specified, 'CalledByEntry' used as default. If no signers were
specified, no array is passed. Note that scopes are properly handled by
neo-go RPC server only. C# implementation does not support scopes capability.
Examples:
* 'NNQk4QXsxvsrr3GSozoWBUxEmfag7B6hz5'
* 'NVquyZHoPirw6zAEPvY1ZezxM493zMWQqs:Global'
* '0x0000000009070e030d0f0e020d0c06050e030c02'
* '0000000009070e030d0f0e020d0c06050e030c02:CalledByEntry,` +
`CustomGroups:0206d7495ceb34c197093b5fc1cccf1996ada05e69ef67e765462a7f5d88ee14d0'
* '0000000009070e030d0f0e020d0c06050e030c02:CalledByEntry,` +
`CustomContracts:1011120009070e030d0f0e020d0c06050e030c02:0x1211100009070e030d0f0e020d0c06050e030c02'
`, `,
Action: testInvokeFunction, Action: testInvokeFunction,
Flags: testInvokeFunctionFlags, Flags: options.RPC,
}, },
{ {
Name: "testinvokescript", Name: "testinvokescript",
Usage: "Invoke compiled AVM code in NEF format on the blockchain (test mode, not creating a transaction for it)", Usage: "Invoke compiled AVM code in NEF format on the blockchain (test mode, not creating a transaction for it)",
UsageText: "neo-go contract testinvokescript -r endpoint -i input.nef [--historic index/hash] [signers...]", UsageText: "neo-go contract testinvokescript -r endpoint -i input.nef [signers...]",
Description: `Executes given compiled AVM instructions in NEF format with the given set of Description: `Executes given compiled AVM instructions in NEF format with the given set of
signers not included sender by default. See testinvokefunction documentation signers not included sender by default. See testinvokefunction documentation
for the details about parameters. for the details about parameters.
@ -249,10 +326,9 @@ func NewCommands() []cli.Command {
Flags: testInvokeScriptFlags, Flags: testInvokeScriptFlags,
}, },
{ {
Name: "init", Name: "init",
Usage: "initialize a new smart-contract in a directory with boiler plate code", Usage: "initialize a new smart-contract in a directory with boiler plate code",
UsageText: "neo-go contract init -n name [--skip-details]", Action: initSmartContract,
Action: initSmartContract,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "name, n", Name: "name, n",
@ -265,10 +341,9 @@ func NewCommands() []cli.Command {
}, },
}, },
{ {
Name: "inspect", Name: "inspect",
Usage: "creates a user readable dump of the program instructions", Usage: "creates a user readable dump of the program instructions",
UsageText: "neo-go contract inspect -i file [-c]", Action: inspect,
Action: inspect,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.BoolFlag{ cli.BoolFlag{
Name: "compile, c", Name: "compile, c",
@ -281,10 +356,9 @@ func NewCommands() []cli.Command {
}, },
}, },
{ {
Name: "calc-hash", Name: "calc-hash",
Usage: "calculates hash of a contract after deployment", Usage: "calculates hash of a contract after deployment",
UsageText: "neo-go contract calc-hash -i nef -m manifest -s address", Action: calcHash,
Action: calcHash,
Flags: []cli.Flag{ Flags: []cli.Flag{
flags.AddressFlag{ flags.AddressFlag{
Name: "sender, s", Name: "sender, s",
@ -305,11 +379,28 @@ func NewCommands() []cli.Command {
Usage: "manifest-related commands", Usage: "manifest-related commands",
Subcommands: []cli.Command{ Subcommands: []cli.Command{
{ {
Name: "add-group", Name: "add-group",
Usage: "adds group to the manifest", Usage: "adds group to the manifest",
UsageText: "neo-go contract manifest add-group -w wallet [--wallet-config path] -n nef -m manifest -a address -s address", Action: manifestAddGroup,
Action: manifestAddGroup, Flags: []cli.Flag{
Flags: manifestAddGroupFlags, walletFlag,
cli.StringFlag{
Name: "sender, s",
Usage: "deploy transaction sender",
},
cli.StringFlag{
Name: "account, a",
Usage: "account to sign group with",
},
cli.StringFlag{
Name: "nef, n",
Usage: "path to the NEF file",
},
cli.StringFlag{
Name: "manifest, m",
Usage: "path to the manifest",
},
},
}, },
}, },
}, },
@ -319,9 +410,6 @@ func NewCommands() []cli.Command {
// initSmartContract initializes a given directory with some boiler plate code. // initSmartContract initializes a given directory with some boiler plate code.
func initSmartContract(ctx *cli.Context) error { func initSmartContract(ctx *cli.Context) error {
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
contractName := ctx.String("name") contractName := ctx.String("name")
if contractName == "" { if contractName == "" {
return cli.NewExitError(errNoSmartContractName, 1) return cli.NewExitError(errNoSmartContractName, 1)
@ -346,15 +434,13 @@ func initSmartContract(ctx *cli.Context) error {
SourceURL: "http://example.com/", SourceURL: "http://example.com/",
SupportedStandards: []string{}, SupportedStandards: []string{},
SafeMethods: []string{}, SafeMethods: []string{},
Events: []compiler.HybridEvent{ Events: []manifest.Event{
{ {
Name: "Hello world!", Name: "Hello world!",
Parameters: []compiler.HybridParameter{ Parameters: []manifest.Parameter{
{ {
Parameter: manifest.Parameter{ Name: "args",
Name: "args", Type: smartcontract.ArrayType,
Type: smartcontract.ArrayType,
},
}, },
}, },
}, },
@ -365,7 +451,7 @@ func initSmartContract(ctx *cli.Context) error {
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
if err := os.WriteFile(filepath.Join(basePath, "neo-go.yml"), b, 0644); err != nil { if err := ioutil.WriteFile(filepath.Join(basePath, "neo-go.yml"), b, 0644); err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
@ -375,18 +461,15 @@ func initSmartContract(ctx *cli.Context) error {
} }
gm := []byte("module " + contractName + ` gm := []byte("module " + contractName + `
go 1.20
require ( require (
github.com/nspcc-dev/neo-go/pkg/interop ` + ver + ` github.com/nspcc-dev/neo-go/pkg/interop ` + ver + `
)`) )`)
if err := os.WriteFile(filepath.Join(basePath, "go.mod"), gm, 0644); err != nil { if err := ioutil.WriteFile(filepath.Join(basePath, "go.mod"), gm, 0644); err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
data := []byte(fmt.Sprintf(smartContractTmpl, contractName)) data := []byte(fmt.Sprintf(smartContractTmpl, contractName))
if err := os.WriteFile(filepath.Join(basePath, fileName), data, 0644); err != nil { if err := ioutil.WriteFile(filepath.Join(basePath, fileName), data, 0644); err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
@ -396,9 +479,6 @@ require (
} }
func contractCompile(ctx *cli.Context) error { func contractCompile(ctx *cli.Context) error {
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
src := ctx.String("in") src := ctx.String("in")
if len(src) == 0 { if len(src) == 0 {
return cli.NewExitError(errNoInput, 1) return cli.NewExitError(errNoInput, 1)
@ -406,48 +486,19 @@ func contractCompile(ctx *cli.Context) error {
manifestFile := ctx.String("manifest") manifestFile := ctx.String("manifest")
confFile := ctx.String("config") confFile := ctx.String("config")
debugFile := ctx.String("debug") debugFile := ctx.String("debug")
out := ctx.String("out") if len(confFile) == 0 && (len(manifestFile) != 0 || len(debugFile) != 0) {
bindings := ctx.String("bindings")
if len(confFile) == 0 && (len(manifestFile) != 0 || len(debugFile) != 0 || len(bindings) != 0) {
return cli.NewExitError(errNoConfFile, 1) return cli.NewExitError(errNoConfFile, 1)
} }
autocomplete := len(manifestFile) == 0 &&
len(confFile) == 0 &&
len(out) == 0 &&
len(bindings) == 0
if autocomplete {
var root string
fileInfo, err := os.Stat(src)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to stat source file or directory: %w", err), 1)
}
if fileInfo.IsDir() {
base := filepath.Base(fileInfo.Name())
if base == string(filepath.Separator) {
base = "contract"
}
root = filepath.Join(src, base)
} else {
root = strings.TrimSuffix(src, ".go")
}
manifestFile = root + ".manifest.json"
confFile = root + ".yml"
out = root + ".nef"
bindings = root + ".bindings.yml"
}
o := &compiler.Options{ o := &compiler.Options{
Outfile: out, Outfile: ctx.String("out"),
DebugInfo: debugFile, DebugInfo: debugFile,
ManifestFile: manifestFile, ManifestFile: manifestFile,
BindingsFile: bindings,
NoStandardCheck: ctx.Bool("no-standards"), NoStandardCheck: ctx.Bool("no-standards"),
NoEventsCheck: ctx.Bool("no-events"), NoEventsCheck: ctx.Bool("no-events"),
NoPermissionsCheck: ctx.Bool("no-permissions"), NoPermissionsCheck: ctx.Bool("no-permissions"),
GuessEventTypes: ctx.Bool("guess-eventtypes"),
} }
if len(confFile) != 0 { if len(confFile) != 0 {
@ -458,7 +509,6 @@ func contractCompile(ctx *cli.Context) error {
o.Name = conf.Name o.Name = conf.Name
o.SourceURL = conf.SourceURL o.SourceURL = conf.SourceURL
o.ContractEvents = conf.Events o.ContractEvents = conf.Events
o.DeclaredNamedTypes = conf.NamedTypes
o.ContractSupportedStandards = conf.SupportedStandards o.ContractSupportedStandards = conf.SupportedStandards
o.Permissions = make([]manifest.Permission, len(conf.Permissions)) o.Permissions = make([]manifest.Permission, len(conf.Permissions))
for i := range conf.Permissions { for i := range conf.Permissions {
@ -480,9 +530,6 @@ func contractCompile(ctx *cli.Context) error {
} }
func calcHash(ctx *cli.Context) error { func calcHash(ctx *cli.Context) error {
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
sender := ctx.Generic("sender").(*flags.Address) sender := ctx.Generic("sender").(*flags.Address)
if !sender.IsSet { if !sender.IsSet {
return cli.NewExitError("sender is not set", 1) return cli.NewExitError("sender is not set", 1)
@ -496,7 +543,7 @@ func calcHash(ctx *cli.Context) error {
if mpath == "" { if mpath == "" {
return cli.NewExitError(errors.New("no manifest file provided"), 1) return cli.NewExitError(errors.New("no manifest file provided"), 1)
} }
f, err := os.ReadFile(p) f, err := ioutil.ReadFile(p)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't read .nef file: %w", err), 1) return cli.NewExitError(fmt.Errorf("can't read .nef file: %w", err), 1)
} }
@ -504,7 +551,7 @@ func calcHash(ctx *cli.Context) error {
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't unmarshal .nef file: %w", err), 1) return cli.NewExitError(fmt.Errorf("can't unmarshal .nef file: %w", err), 1)
} }
manifestBytes, err := os.ReadFile(mpath) manifestBytes, err := ioutil.ReadFile(mpath)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to read manifest file: %w", err), 1) return cli.NewExitError(fmt.Errorf("failed to read manifest file: %w", err), 1)
} }
@ -530,9 +577,8 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
err error err error
exitErr *cli.ExitError exitErr *cli.ExitError
operation string operation string
params []any params = make([]smartcontract.Parameter, 0)
paramsStart = 1 paramsStart = 1
scParams []smartcontract.Parameter
cosigners []transaction.Signer cosigners []transaction.Signer
cosignersOffset = 0 cosignersOffset = 0
) )
@ -552,14 +598,10 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
paramsStart++ paramsStart++
if len(args) > paramsStart { if len(args) > paramsStart {
cosignersOffset, scParams, err = cmdargs.ParseParams(args[paramsStart:], true) cosignersOffset, params, err = cmdargs.ParseParams(args[paramsStart:], true)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
params = make([]any, len(scParams))
for i := range scParams {
params[i] = scParams[i]
}
} }
cosignersStart := paramsStart + cosignersOffset cosignersStart := paramsStart + cosignersOffset
@ -573,84 +615,112 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
w *wallet.Wallet w *wallet.Wallet
) )
if signAndPush { if signAndPush {
acc, w, err = options.GetAccFromContext(ctx) acc, w, err = getAccFromContext(ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
defer w.Close()
} }
return invokeWithArgs(ctx, acc, w, script, operation, params, cosigners) _, err = invokeWithArgs(ctx, acc, w, script, operation, params, cosigners)
return err
} }
func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet, script util.Uint160, operation string, params []any, cosigners []transaction.Signer) error { func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet, script util.Uint160, operation string, params []smartcontract.Parameter, cosigners []transaction.Signer) (util.Uint160, error) {
var ( var (
err error err error
signersAccounts []actor.SignerAccount gas, sysgas fixedn.Fixed8
resp *result.Invoke cosignersAccounts []client.SignerAccount
signAndPush = acc != nil resp *result.Invoke
inv *invoker.Invoker sender util.Uint160
act *actor.Actor signAndPush = acc != nil
) )
if signAndPush { if signAndPush {
signersAccounts, err = cmdargs.GetSignersAccounts(acc, wall, cosigners, transaction.None) gas = flags.Fixed8FromContext(ctx, "gas")
sysgas = flags.Fixed8FromContext(ctx, "sysgas")
sender, err = address.StringToUint160(acc.Address)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1) return sender, err
}
cosignersAccounts, err = cmdargs.GetSignersAccounts(wall, cosigners)
if err != nil {
return sender, cli.NewExitError(fmt.Errorf("failed to calculate network fee: %w", err), 1)
} }
} }
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel() defer cancel()
if signAndPush {
_, act, err = options.GetRPCWithActor(gctx, ctx, signersAccounts) c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return err
}
inv = &act.Invoker
} else {
_, inv, err = options.GetRPCWithInvoker(gctx, ctx, cosigners)
if err != nil {
return err
}
}
out := ctx.String("out")
resp, err = inv.Call(script, operation, params...)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return sender, err
}
out := ctx.String("out")
resp, err = c.InvokeFunction(script, operation, params, cosigners)
if err != nil {
return sender, cli.NewExitError(err, 1)
} }
if resp.State != "HALT" { if resp.State != "HALT" {
errText := fmt.Sprintf("Warning: %s VM state returned from the RPC node: %s", resp.State, resp.FaultException) errText := fmt.Sprintf("Warning: %s VM state returned from the RPC node: %s\n", resp.State, resp.FaultException)
if !signAndPush { action := "save"
return cli.NewExitError(errText, 1) process := "Saving"
} if signAndPush {
if out != "" {
action := "send" action += "and send"
process := "Sending" process += "and sending"
if out != "" { } else {
action = "save" action = "send"
process = "Saving" process = "Sending"
}
} }
if !ctx.Bool("force") { if !ctx.Bool("force") {
return cli.NewExitError(errText+".\nUse --force flag to "+action+" the transaction anyway.", 1) return sender, cli.NewExitError(errText+". Use --force flag to "+action+" the transaction anyway.", 1)
} }
fmt.Fprintln(ctx.App.Writer, errText+".\n"+process+" transaction...") fmt.Fprintln(ctx.App.Writer, errText+". "+process+" transaction...")
} }
if !signAndPush { if out != "" {
tx, err := c.CreateTxFromScript(resp.Script, acc, resp.GasConsumed+int64(sysgas), int64(gas), cosignersAccounts)
if err != nil {
return sender, cli.NewExitError(fmt.Errorf("failed to create tx: %w", err), 1)
}
m, err := c.GetNetwork()
if err != nil {
return sender, cli.NewExitError(fmt.Errorf("failed to save tx: %w", err), 1)
}
if err := paramcontext.InitAndSave(m, tx, acc, out); err != nil {
return sender, cli.NewExitError(err, 1)
}
fmt.Fprintln(ctx.App.Writer, tx.Hash().StringLE())
return sender, nil
}
if signAndPush {
if len(resp.Script) == 0 {
return sender, cli.NewExitError(errors.New("no script returned from the RPC node"), 1)
}
tx, err := c.CreateTxFromScript(resp.Script, acc, resp.GasConsumed+int64(sysgas), int64(gas), cosignersAccounts)
if err != nil {
return sender, cli.NewExitError(fmt.Errorf("failed to create tx: %w", err), 1)
}
if !ctx.Bool("force") {
err := input.ConfirmTx(ctx.App.Writer, tx)
if err != nil {
return sender, cli.NewExitError(err, 1)
}
}
txHash, err := c.SignAndPushTx(tx, acc, cosignersAccounts)
if err != nil {
return sender, cli.NewExitError(fmt.Errorf("failed to push invocation tx: %w", err), 1)
}
fmt.Fprintf(ctx.App.Writer, "Sent invocation transaction %s\n", txHash.StringLE())
} else {
b, err := json.MarshalIndent(resp, "", " ") b, err := json.MarshalIndent(resp, "", " ")
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return sender, cli.NewExitError(err, 1)
} }
fmt.Fprintln(ctx.App.Writer, string(b)) fmt.Fprintln(ctx.App.Writer, string(b))
return nil
} }
if len(resp.Script) == 0 {
return cli.NewExitError(errors.New("no script returned from the RPC node"), 1) return sender, nil
}
tx, err := act.MakeUnsignedUncheckedRun(resp.Script, resp.GasConsumed, nil)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create tx: %w", err), 1)
}
return txctx.SignAndSend(ctx, act, acc, tx)
} }
func testInvokeScript(ctx *cli.Context) error { func testInvokeScript(ctx *cli.Context) error {
@ -659,7 +729,7 @@ func testInvokeScript(ctx *cli.Context) error {
return cli.NewExitError(errNoInput, 1) return cli.NewExitError(errNoInput, 1)
} }
b, err := os.ReadFile(src) b, err := ioutil.ReadFile(src)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
@ -676,12 +746,12 @@ func testInvokeScript(ctx *cli.Context) error {
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel() defer cancel()
_, inv, err := options.GetRPCWithInvoker(gctx, ctx, signers) c, err := options.GetRPCClient(gctx, ctx)
if err != nil { if err != nil {
return err return err
} }
resp, err := inv.Run(nefFile.Script) resp, err := c.InvokeScript(nefFile.Script, signers)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
@ -702,16 +772,12 @@ type ProjectConfig struct {
SourceURL string SourceURL string
SafeMethods []string SafeMethods []string
SupportedStandards []string SupportedStandards []string
Events []compiler.HybridEvent Events []manifest.Event
Permissions []permission Permissions []permission
Overloads map[string]string `yaml:"overloads,omitempty"` Overloads map[string]string `yaml:"overloads,omitempty"`
NamedTypes map[string]binding.ExtendedType `yaml:"namedtypes,omitempty"`
} }
func inspect(ctx *cli.Context) error { func inspect(ctx *cli.Context) error {
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
in := ctx.String("in") in := ctx.String("in")
compile := ctx.Bool("compile") compile := ctx.Bool("compile")
if len(in) == 0 { if len(in) == 0 {
@ -727,7 +793,7 @@ func inspect(ctx *cli.Context) error {
return cli.NewExitError(fmt.Errorf("failed to compile: %w", err), 1) return cli.NewExitError(fmt.Errorf("failed to compile: %w", err), 1)
} }
} else { } else {
f, err := os.ReadFile(in) f, err := ioutil.ReadFile(in)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to read .nef file: %w", err), 1) return cli.NewExitError(fmt.Errorf("failed to read .nef file: %w", err), 1)
} }
@ -744,6 +810,52 @@ func inspect(ctx *cli.Context) error {
return nil return nil
} }
func getAccFromContext(ctx *cli.Context) (*wallet.Account, *wallet.Wallet, error) {
var addr util.Uint160
wPath := ctx.String("wallet")
if len(wPath) == 0 {
return nil, nil, cli.NewExitError(errNoWallet, 1)
}
wall, err := wallet.NewWalletFromFile(wPath)
if err != nil {
return nil, nil, cli.NewExitError(err, 1)
}
addrFlag := ctx.Generic("address").(*flags.Address)
if addrFlag.IsSet {
addr = addrFlag.Uint160()
} else {
addr = wall.GetChangeAddress()
}
acc, err := getUnlockedAccount(wall, addr)
return acc, wall, err
}
func getUnlockedAccount(wall *wallet.Wallet, addr util.Uint160) (*wallet.Account, error) {
acc := wall.GetAccount(addr)
if acc == nil {
return nil, cli.NewExitError(fmt.Errorf("wallet contains no account for '%s'", address.Uint160ToString(addr)), 1)
}
if acc.PrivateKey() != nil {
return acc, nil
}
rawPass, err := input.ReadPassword(
fmt.Sprintf("Enter account %s password > ", address.Uint160ToString(addr)))
if err != nil {
return nil, cli.NewExitError(fmt.Errorf("Error reading password: %w", err), 1)
}
pass := strings.TrimRight(string(rawPass), "\n")
err = acc.Decrypt(pass, wall.Scrypt)
if err != nil {
return nil, cli.NewExitError(err, 1)
}
return acc, nil
}
// contractDeploy deploys contract. // contractDeploy deploys contract.
func contractDeploy(ctx *cli.Context) error { func contractDeploy(ctx *cli.Context) error {
nefFile, f, err := readNEFFile(ctx.String("in")) nefFile, f, err := readNEFFile(ctx.String("in"))
@ -751,13 +863,21 @@ func contractDeploy(ctx *cli.Context) error {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
m, manifestBytes, err := readManifest(ctx.String("manifest"), util.Uint160{}) m, manifestBytes, err := readManifest(ctx.String("manifest"))
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to read manifest file: %w", err), 1) return cli.NewExitError(fmt.Errorf("failed to read manifest file: %w", err), 1)
} }
var appCallParams = []any{f, manifestBytes} appCallParams := []smartcontract.Parameter{
{
Type: smartcontract.ByteArrayType,
Value: f,
},
{
Type: smartcontract.ByteArrayType,
Value: manifestBytes,
},
}
signOffset, data, err := cmdargs.ParseParams(ctx.Args(), true) signOffset, data, err := cmdargs.ParseParams(ctx.Args(), true)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("unable to parse 'data' parameter: %w", err), 1) return cli.NewExitError(fmt.Errorf("unable to parse 'data' parameter: %w", err), 1)
@ -769,12 +889,23 @@ func contractDeploy(ctx *cli.Context) error {
appCallParams = append(appCallParams, data[0]) appCallParams = append(appCallParams, data[0])
} }
acc, w, err := options.GetAccFromContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return err
}
mgmtHash, err := c.GetNativeContractHash(nativenames.Management)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to get management contract's hash: %w", err), 1)
}
acc, w, err := getAccFromContext(ctx)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't get sender address: %w", err), 1) return cli.NewExitError(fmt.Errorf("can't get sender address: %w", err), 1)
} }
defer w.Close()
sender := acc.ScriptHash()
cosigners, sgnErr := cmdargs.GetSignersFromContext(ctx, signOffset) cosigners, sgnErr := cmdargs.GetSignersFromContext(ctx, signOffset)
if sgnErr != nil { if sgnErr != nil {
@ -786,7 +917,7 @@ func contractDeploy(ctx *cli.Context) error {
}} }}
} }
extErr := invokeWithArgs(ctx, acc, w, management.Hash, "deploy", appCallParams, cosigners) sender, extErr := invokeWithArgs(ctx, acc, w, mgmtHash, "deploy", appCallParams, cosigners)
if extErr != nil { if extErr != nil {
return extErr return extErr
} }
@ -799,7 +930,7 @@ func contractDeploy(ctx *cli.Context) error {
// ParseContractConfig reads contract configuration file (.yaml) and returns unmarshalled ProjectConfig. // ParseContractConfig reads contract configuration file (.yaml) and returns unmarshalled ProjectConfig.
func ParseContractConfig(confFile string) (ProjectConfig, error) { func ParseContractConfig(confFile string) (ProjectConfig, error) {
conf := ProjectConfig{} conf := ProjectConfig{}
confBytes, err := os.ReadFile(confFile) confBytes, err := ioutil.ReadFile(confFile)
if err != nil { if err != nil {
return conf, cli.NewExitError(err, 1) return conf, cli.NewExitError(err, 1)
} }

View file

@ -1,7 +1,9 @@
package smartcontract package smartcontract
import ( import (
"encoding/hex"
"flag" "flag"
"io/ioutil"
"os" "os"
"testing" "testing"
@ -10,7 +12,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli" "github.com/urfave/cli"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v2"
) )
func TestInitSmartContract(t *testing.T) { func TestInitSmartContract(t *testing.T) {
@ -29,13 +31,13 @@ func TestInitSmartContract(t *testing.T) {
dirInfo, err := os.Stat(contractName) dirInfo, err := os.Stat(contractName)
require.NoError(t, err) require.NoError(t, err)
require.True(t, dirInfo.IsDir()) require.True(t, dirInfo.IsDir())
files, err := os.ReadDir(contractName) files, err := ioutil.ReadDir(contractName)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 3, len(files)) require.Equal(t, 3, len(files))
require.Equal(t, "go.mod", files[0].Name()) require.Equal(t, "go.mod", files[0].Name())
require.Equal(t, "main.go", files[1].Name()) require.Equal(t, "main.go", files[1].Name())
require.Equal(t, "neo-go.yml", files[2].Name()) require.Equal(t, "neo-go.yml", files[2].Name())
main, err := os.ReadFile(contractName + "/" + files[1].Name()) main, err := ioutil.ReadFile(contractName + "/" + files[1].Name())
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, require.Equal(t,
`package `+contractName+` `package `+contractName+`
@ -50,25 +52,25 @@ func init() {
} }
// RuntimeNotify sends runtime notification with "Hello world!" name // RuntimeNotify sends runtime notification with "Hello world!" name
func RuntimeNotify(args []any) { func RuntimeNotify(args []interface{}) {
runtime.Notify(notificationName, args) runtime.Notify(notificationName, args)
}`, string(main)) }`, string(main))
manifest, err := os.ReadFile(contractName + "/" + files[2].Name()) manifest, err := ioutil.ReadFile(contractName + "/" + files[2].Name())
require.NoError(t, err) require.NoError(t, err)
expected := `name: testContract require.Equal(t,
`name: testContract
sourceurl: http://example.com/ sourceurl: http://example.com/
safemethods: [] safemethods: []
supportedstandards: [] supportedstandards: []
events: events:
- name: Hello world! - name: Hello world!
parameters: parameters:
- name: args - name: args
type: Array type: Array
permissions: permissions:
- methods: '*' - methods: '*'
` `, string(manifest))
require.Equal(t, expected, string(manifest))
} }
func testPermissionMarshal(t *testing.T, p *manifest.Permission, expected string) { func testPermissionMarshal(t *testing.T, p *manifest.Permission, expected string) {
@ -108,8 +110,8 @@ func TestPermissionMarshal(t *testing.T) {
p.Methods.Add("abc") p.Methods.Add("abc")
p.Methods.Add("lamao") p.Methods.Add("lamao")
testPermissionMarshal(t, p, testPermissionMarshal(t, p,
"group: "+priv.PublicKey().StringCompressed()+"\n"+ "group: "+hex.EncodeToString(priv.PublicKey().Bytes())+"\n"+
"methods:\n - abc\n - lamao\n") "methods:\n- abc\n- lamao\n")
}) })
} }
@ -117,7 +119,7 @@ func TestPermissionUnmarshalInvalid(t *testing.T) {
priv, err := keys.NewPrivateKey() priv, err := keys.NewPrivateKey()
require.NoError(t, err) require.NoError(t, err)
pub := priv.PublicKey().StringCompressed() pub := hex.EncodeToString(priv.PublicKey().Bytes())
u160 := random.Uint160().StringLE() u160 := random.Uint160().StringLE()
testCases := []string{ testCases := []string{
"hash: []\nmethods: '*'\n", // invalid hash type "hash: []\nmethods: '*'\n", // invalid hash type

View file

@ -1,52 +0,0 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package gastoken contains RPC wrappers for GasToken contract.
package gastoken
import (
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// Hash contains contract hash.
var Hash = util.Uint160{0xcf, 0x76, 0xe2, 0x8b, 0xd0, 0x6, 0x2c, 0x4a, 0x47, 0x8e, 0xe3, 0x55, 0x61, 0x1, 0x13, 0x19, 0xf3, 0xcf, 0xa4, 0xd2}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
nep17.Invoker
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
nep17.Actor
}
// ContractReader implements safe contract methods.
type ContractReader struct {
nep17.TokenReader
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
nep17.TokenWriter
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
func NewReader(invoker Invoker) *ContractReader {
var hash = Hash
return &ContractReader{*nep17.NewReader(invoker, hash), invoker, hash}
}
// New creates an instance of Contract using Hash and the given Actor.
func New(actor Actor) *Contract {
var hash = Hash
var nep17t = nep17.New(actor, hash)
return &Contract{ContractReader{nep17t.TokenReader, actor, hash}, nep17t.TokenWriter, actor, hash}
}

View file

@ -1 +0,0 @@
{"name":"GasToken","groups":[],"features":{},"supportedstandards":["NEP-17"],"abi":{"methods":[{"name":"balanceOf","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","offset":0,"safe":true},{"name":"decimals","parameters":[],"returntype":"Integer","offset":7,"safe":true},{"name":"symbol","parameters":[],"returntype":"String","offset":14,"safe":true},{"name":"totalSupply","parameters":[],"returntype":"Integer","offset":21,"safe":true},{"name":"transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","offset":28,"safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}

View file

@ -1,511 +0,0 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package nameservice contains RPC wrappers for NameService contract.
package nameservice
import (
"errors"
"fmt"
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
"unicode/utf8"
)
// Hash contains contract hash.
var Hash = util.Uint160{0xde, 0x46, 0x5f, 0x5d, 0x50, 0x57, 0xcf, 0x33, 0x28, 0x47, 0x94, 0xc5, 0xcf, 0xc2, 0xc, 0x69, 0x37, 0x1c, 0xac, 0x50}
// SetAdminEvent represents "SetAdmin" event emitted by the contract.
type SetAdminEvent struct {
Name string
OldAdmin util.Uint160
NewAdmin util.Uint160
}
// RenewEvent represents "Renew" event emitted by the contract.
type RenewEvent struct {
Name string
OldExpiration *big.Int
NewExpiration *big.Int
}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
nep11.Invoker
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
nep11.Actor
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
nep11.NonDivisibleReader
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
nep11.BaseWriter
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
func NewReader(invoker Invoker) *ContractReader {
var hash = Hash
return &ContractReader{*nep11.NewNonDivisibleReader(invoker, hash), invoker, hash}
}
// New creates an instance of Contract using Hash and the given Actor.
func New(actor Actor) *Contract {
var hash = Hash
var nep11ndt = nep11.NewNonDivisible(actor, hash)
return &Contract{ContractReader{nep11ndt.NonDivisibleReader, actor, hash}, nep11ndt.BaseWriter, actor, hash}
}
// Roots invokes `roots` method of contract.
func (c *ContractReader) Roots() (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.invoker.Call(c.hash, "roots"))
}
// RootsExpanded is similar to Roots (uses the same contract
// method), but can be useful if the server used doesn't support sessions and
// doesn't expand iterators. It creates a script that will get the specified
// number of result items from the iterator right in the VM and return them to
// you. It's only limited by VM stack and GAS available for RPC invocations.
func (c *ContractReader) RootsExpanded(_numOfIteratorItems int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "roots", _numOfIteratorItems))
}
// GetPrice invokes `getPrice` method of contract.
func (c *ContractReader) GetPrice(length *big.Int) (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "getPrice", length))
}
// IsAvailable invokes `isAvailable` method of contract.
func (c *ContractReader) IsAvailable(name string) (bool, error) {
return unwrap.Bool(c.invoker.Call(c.hash, "isAvailable", name))
}
// GetRecord invokes `getRecord` method of contract.
func (c *ContractReader) GetRecord(name string, typev *big.Int) (string, error) {
return unwrap.UTF8String(c.invoker.Call(c.hash, "getRecord", name, typev))
}
// GetAllRecords invokes `getAllRecords` method of contract.
func (c *ContractReader) GetAllRecords(name string) (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.invoker.Call(c.hash, "getAllRecords", name))
}
// GetAllRecordsExpanded is similar to GetAllRecords (uses the same contract
// method), but can be useful if the server used doesn't support sessions and
// doesn't expand iterators. It creates a script that will get the specified
// number of result items from the iterator right in the VM and return them to
// you. It's only limited by VM stack and GAS available for RPC invocations.
func (c *ContractReader) GetAllRecordsExpanded(name string, _numOfIteratorItems int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "getAllRecords", _numOfIteratorItems, name))
}
// Resolve invokes `resolve` method of contract.
func (c *ContractReader) Resolve(name string, typev *big.Int) (string, error) {
return unwrap.UTF8String(c.invoker.Call(c.hash, "resolve", name, typev))
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(nef []byte, manifest string) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", nef, manifest)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(nef []byte, manifest string) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", nef, manifest)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(nef []byte, manifest string) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, nef, manifest)
}
// AddRoot creates a transaction invoking `addRoot` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) AddRoot(root string) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "addRoot", root)
}
// AddRootTransaction creates a transaction invoking `addRoot` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) AddRootTransaction(root string) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "addRoot", root)
}
// AddRootUnsigned creates a transaction invoking `addRoot` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) AddRootUnsigned(root string) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "addRoot", nil, root)
}
// SetPrice creates a transaction invoking `setPrice` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) SetPrice(priceList []any) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "setPrice", priceList)
}
// SetPriceTransaction creates a transaction invoking `setPrice` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) SetPriceTransaction(priceList []any) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "setPrice", priceList)
}
// SetPriceUnsigned creates a transaction invoking `setPrice` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) SetPriceUnsigned(priceList []any) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "setPrice", nil, priceList)
}
func (c *Contract) scriptForRegister(name string, owner util.Uint160) ([]byte, error) {
return smartcontract.CreateCallWithAssertScript(c.hash, "register", name, owner)
}
// Register creates a transaction invoking `register` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Register(name string, owner util.Uint160) (util.Uint256, uint32, error) {
script, err := c.scriptForRegister(name, owner)
if err != nil {
return util.Uint256{}, 0, err
}
return c.actor.SendRun(script)
}
// RegisterTransaction creates a transaction invoking `register` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) RegisterTransaction(name string, owner util.Uint160) (*transaction.Transaction, error) {
script, err := c.scriptForRegister(name, owner)
if err != nil {
return nil, err
}
return c.actor.MakeRun(script)
}
// RegisterUnsigned creates a transaction invoking `register` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) RegisterUnsigned(name string, owner util.Uint160) (*transaction.Transaction, error) {
script, err := c.scriptForRegister(name, owner)
if err != nil {
return nil, err
}
return c.actor.MakeUnsignedRun(script, nil)
}
// Renew creates a transaction invoking `renew` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Renew(name string) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "renew", name)
}
// RenewTransaction creates a transaction invoking `renew` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) RenewTransaction(name string) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "renew", name)
}
// RenewUnsigned creates a transaction invoking `renew` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) RenewUnsigned(name string) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "renew", nil, name)
}
// Renew2 creates a transaction invoking `renew` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Renew2(name string, years *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "renew", name, years)
}
// Renew2Transaction creates a transaction invoking `renew` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) Renew2Transaction(name string, years *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "renew", name, years)
}
// Renew2Unsigned creates a transaction invoking `renew` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) Renew2Unsigned(name string, years *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "renew", nil, name, years)
}
// SetAdmin creates a transaction invoking `setAdmin` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) SetAdmin(name string, admin util.Uint160) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "setAdmin", name, admin)
}
// SetAdminTransaction creates a transaction invoking `setAdmin` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) SetAdminTransaction(name string, admin util.Uint160) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "setAdmin", name, admin)
}
// SetAdminUnsigned creates a transaction invoking `setAdmin` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) SetAdminUnsigned(name string, admin util.Uint160) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "setAdmin", nil, name, admin)
}
// SetRecord creates a transaction invoking `setRecord` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) SetRecord(name string, typev *big.Int, data string) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "setRecord", name, typev, data)
}
// SetRecordTransaction creates a transaction invoking `setRecord` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) SetRecordTransaction(name string, typev *big.Int, data string) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "setRecord", name, typev, data)
}
// SetRecordUnsigned creates a transaction invoking `setRecord` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) SetRecordUnsigned(name string, typev *big.Int, data string) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "setRecord", nil, name, typev, data)
}
// DeleteRecord creates a transaction invoking `deleteRecord` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) DeleteRecord(name string, typev *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "deleteRecord", name, typev)
}
// DeleteRecordTransaction creates a transaction invoking `deleteRecord` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) DeleteRecordTransaction(name string, typev *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "deleteRecord", name, typev)
}
// DeleteRecordUnsigned creates a transaction invoking `deleteRecord` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) DeleteRecordUnsigned(name string, typev *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "deleteRecord", nil, name, typev)
}
// SetAdminEventsFromApplicationLog retrieves a set of all emitted events
// with "SetAdmin" name from the provided [result.ApplicationLog].
func SetAdminEventsFromApplicationLog(log *result.ApplicationLog) ([]*SetAdminEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*SetAdminEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "SetAdmin" {
continue
}
event := new(SetAdminEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize SetAdminEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to SetAdminEvent or
// returns an error if it's not possible to do to so.
func (e *SetAdminEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 3 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.Name, err = func(item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Name: %w", err)
}
index++
e.OldAdmin, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field OldAdmin: %w", err)
}
index++
e.NewAdmin, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field NewAdmin: %w", err)
}
return nil
}
// RenewEventsFromApplicationLog retrieves a set of all emitted events
// with "Renew" name from the provided [result.ApplicationLog].
func RenewEventsFromApplicationLog(log *result.ApplicationLog) ([]*RenewEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*RenewEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "Renew" {
continue
}
event := new(RenewEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize RenewEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to RenewEvent or
// returns an error if it's not possible to do to so.
func (e *RenewEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 3 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.Name, err = func(item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Name: %w", err)
}
index++
e.OldExpiration, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field OldExpiration: %w", err)
}
index++
e.NewExpiration, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field NewExpiration: %w", err)
}
return nil
}

View file

@ -1,441 +0,0 @@
{
"abi" : {
"events" : [
{
"parameters" : [
{
"name" : "from",
"type" : "Hash160"
},
{
"name" : "to",
"type" : "Hash160"
},
{
"name" : "amount",
"type" : "Integer"
},
{
"type" : "ByteArray",
"name" : "tokenId"
}
],
"name" : "Transfer"
},
{
"parameters" : [
{
"type" : "String",
"name" : "name"
},
{
"type" : "Hash160",
"name" : "oldAdmin"
},
{
"type" : "Hash160",
"name" : "newAdmin"
}
],
"name" : "SetAdmin"
},
{
"name" : "Renew",
"parameters" : [
{
"name" : "name",
"type" : "String"
},
{
"type" : "Integer",
"name" : "oldExpiration"
},
{
"name" : "newExpiration",
"type" : "Integer"
}
]
}
],
"methods" : [
{
"safe" : true,
"parameters" : [],
"name" : "symbol",
"returntype" : "String",
"offset" : 0
},
{
"parameters" : [],
"name" : "decimals",
"returntype" : "Integer",
"safe" : true,
"offset" : 6
},
{
"offset" : 8,
"returntype" : "Integer",
"name" : "totalSupply",
"parameters" : [],
"safe" : true
},
{
"offset" : 53,
"safe" : true,
"parameters" : [
{
"type" : "ByteArray",
"name" : "tokenId"
}
],
"name" : "ownerOf",
"returntype" : "Hash160"
},
{
"safe" : true,
"name" : "properties",
"returntype" : "Map",
"parameters" : [
{
"type" : "ByteArray",
"name" : "tokenId"
}
],
"offset" : 184
},
{
"safe" : true,
"returntype" : "Integer",
"name" : "balanceOf",
"parameters" : [
{
"name" : "owner",
"type" : "Hash160"
}
],
"offset" : 341
},
{
"safe" : true,
"returntype" : "InteropInterface",
"name" : "tokens",
"parameters" : [],
"offset" : 453
},
{
"safe" : true,
"name" : "tokensOf",
"returntype" : "InteropInterface",
"parameters" : [
{
"name" : "owner",
"type" : "Hash160"
}
],
"offset" : 494
},
{
"offset" : 600,
"safe" : false,
"parameters" : [
{
"type" : "Hash160",
"name" : "to"
},
{
"name" : "tokenId",
"type" : "ByteArray"
},
{
"name" : "data",
"type" : "Any"
}
],
"name" : "transfer",
"returntype" : "Boolean"
},
{
"name" : "update",
"returntype" : "Void",
"parameters" : [
{
"type" : "ByteArray",
"name" : "nef"
},
{
"name" : "manifest",
"type" : "String"
}
],
"safe" : false,
"offset" : 1121
},
{
"offset" : 1291,
"returntype" : "Void",
"name" : "addRoot",
"parameters" : [
{
"name" : "root",
"type" : "String"
}
],
"safe" : false
},
{
"offset" : 1725,
"safe" : true,
"parameters" : [],
"returntype" : "InteropInterface",
"name" : "roots"
},
{
"offset" : 1757,
"parameters" : [
{
"type" : "Array",
"name" : "priceList"
}
],
"name" : "setPrice",
"returntype" : "Void",
"safe" : false
},
{
"offset" : 1952,
"safe" : true,
"parameters" : [
{
"name" : "length",
"type" : "Integer"
}
],
"name" : "getPrice",
"returntype" : "Integer"
},
{
"offset" : 2017,
"safe" : true,
"parameters" : [
{
"type" : "String",
"name" : "name"
}
],
"name" : "isAvailable",
"returntype" : "Boolean"
},
{
"offset" : 2405,
"parameters" : [
{
"type" : "String",
"name" : "name"
},
{
"type" : "Hash160",
"name" : "owner"
}
],
"name" : "register",
"returntype" : "Boolean",
"safe" : false
},
{
"name" : "renew",
"returntype" : "Integer",
"parameters" : [
{
"type" : "String",
"name" : "name"
}
],
"safe" : false,
"offset" : 3113
},
{
"offset" : 3123,
"safe" : false,
"parameters" : [
{
"name" : "name",
"type" : "String"
},
{
"name" : "years",
"type" : "Integer"
}
],
"name" : "renew",
"returntype" : "Integer"
},
{
"offset" : 3697,
"parameters" : [
{
"type" : "String",
"name" : "name"
},
{
"name" : "admin",
"type" : "Hash160"
}
],
"name" : "setAdmin",
"returntype" : "Void",
"safe" : false
},
{
"safe" : false,
"returntype" : "Void",
"name" : "setRecord",
"parameters" : [
{
"name" : "name",
"type" : "String"
},
{
"type" : "Integer",
"name" : "type"
},
{
"name" : "data",
"type" : "String"
}
],
"offset" : 3921
},
{
"name" : "getRecord",
"returntype" : "String",
"parameters" : [
{
"name" : "name",
"type" : "String"
},
{
"type" : "Integer",
"name" : "type"
}
],
"safe" : true,
"offset" : 5877
},
{
"returntype" : "InteropInterface",
"name" : "getAllRecords",
"parameters" : [
{
"name" : "name",
"type" : "String"
}
],
"safe" : true,
"offset" : 6201
},
{
"safe" : false,
"returntype" : "Void",
"name" : "deleteRecord",
"parameters" : [
{
"name" : "name",
"type" : "String"
},
{
"type" : "Integer",
"name" : "type"
}
],
"offset" : 6281
},
{
"offset" : 6565,
"name" : "resolve",
"returntype" : "String",
"parameters" : [
{
"name" : "name",
"type" : "String"
},
{
"type" : "Integer",
"name" : "type"
}
],
"safe" : true
},
{
"safe" : false,
"parameters" : [
{
"name" : "data",
"type" : "Any"
},
{
"name" : "update",
"type" : "Boolean"
}
],
"name" : "_deploy",
"returntype" : "Void",
"offset" : 7152
},
{
"offset" : 7514,
"parameters" : [],
"returntype" : "Void",
"name" : "_initialize",
"safe" : false
}
]
},
"supportedstandards" : [
"NEP-11"
],
"permissions" : [
{
"contract" : "0x726cb6e0cd8628a1350a611384688911ab75f51b",
"methods" : [
"ripemd160"
]
},
{
"contract" : "0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0",
"methods" : [
"atoi",
"deserialize",
"serialize",
"stringSplit"
]
},
{
"contract" : "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5",
"methods" : [
"getCommittee"
]
},
{
"contract" : "0xfffdc93764dbaddd97c48f252a53ea4643faa3fd",
"methods" : [
"getContract",
"update"
]
},
{
"methods" : [
"onNEP11Payment"
],
"contract" : "*"
}
],
"features" : {},
"name" : "NameService",
"trusts" : [],
"extra" : {
"Author" : "The Neo Project",
"Description" : "Neo Name Service",
"Email" : "dev@neo.org"
},
"groups" : []
}

View file

@ -1,339 +0,0 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package nextoken contains RPC wrappers for NEX Token contract.
package nextoken
import (
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
)
// Hash contains contract hash.
var Hash = util.Uint160{0xa8, 0x1a, 0xa1, 0xf0, 0x4b, 0xf, 0xdc, 0x4a, 0xa2, 0xce, 0xd5, 0xbf, 0xc6, 0x22, 0xcf, 0xe8, 0x9, 0x7f, 0xa6, 0xa2}
// OnMintEvent represents "OnMint" event emitted by the contract.
type OnMintEvent struct {
From util.Uint160
To util.Uint160
Amount *big.Int
SwapId *big.Int
}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
nep17.Invoker
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
nep17.Actor
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
nep17.TokenReader
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
nep17.TokenWriter
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
func NewReader(invoker Invoker) *ContractReader {
var hash = Hash
return &ContractReader{*nep17.NewReader(invoker, hash), invoker, hash}
}
// New creates an instance of Contract using Hash and the given Actor.
func New(actor Actor) *Contract {
var hash = Hash
var nep17t = nep17.New(actor, hash)
return &Contract{ContractReader{nep17t.TokenReader, actor, hash}, nep17t.TokenWriter, actor, hash}
}
// Cap invokes `cap` method of contract.
func (c *ContractReader) Cap() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "cap"))
}
// GetMinter invokes `getMinter` method of contract.
func (c *ContractReader) GetMinter() (*keys.PublicKey, error) {
return unwrap.PublicKey(c.invoker.Call(c.hash, "getMinter"))
}
// GetOwner invokes `getOwner` method of contract.
func (c *ContractReader) GetOwner() (util.Uint160, error) {
return unwrap.Uint160(c.invoker.Call(c.hash, "getOwner"))
}
// TotalMinted invokes `totalMinted` method of contract.
func (c *ContractReader) TotalMinted() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "totalMinted"))
}
// ChangeMinter creates a transaction invoking `changeMinter` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) ChangeMinter(newMinter *keys.PublicKey) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "changeMinter", newMinter)
}
// ChangeMinterTransaction creates a transaction invoking `changeMinter` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) ChangeMinterTransaction(newMinter *keys.PublicKey) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "changeMinter", newMinter)
}
// ChangeMinterUnsigned creates a transaction invoking `changeMinter` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) ChangeMinterUnsigned(newMinter *keys.PublicKey) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "changeMinter", nil, newMinter)
}
// ChangeOwner creates a transaction invoking `changeOwner` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) ChangeOwner(newOwner util.Uint160) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "changeOwner", newOwner)
}
// ChangeOwnerTransaction creates a transaction invoking `changeOwner` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) ChangeOwnerTransaction(newOwner util.Uint160) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "changeOwner", newOwner)
}
// ChangeOwnerUnsigned creates a transaction invoking `changeOwner` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) ChangeOwnerUnsigned(newOwner util.Uint160) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "changeOwner", nil, newOwner)
}
// Destroy creates a transaction invoking `destroy` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Destroy() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "destroy")
}
// DestroyTransaction creates a transaction invoking `destroy` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) DestroyTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "destroy")
}
// DestroyUnsigned creates a transaction invoking `destroy` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) DestroyUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "destroy", nil)
}
// MaxSupply creates a transaction invoking `maxSupply` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) MaxSupply() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "maxSupply")
}
// MaxSupplyTransaction creates a transaction invoking `maxSupply` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) MaxSupplyTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "maxSupply")
}
// MaxSupplyUnsigned creates a transaction invoking `maxSupply` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) MaxSupplyUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "maxSupply", nil)
}
// Mint creates a transaction invoking `mint` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Mint(from util.Uint160, to util.Uint160, amount *big.Int, swapId *big.Int, signature []byte, data any) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "mint", from, to, amount, swapId, signature, data)
}
// MintTransaction creates a transaction invoking `mint` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) MintTransaction(from util.Uint160, to util.Uint160, amount *big.Int, swapId *big.Int, signature []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "mint", from, to, amount, swapId, signature, data)
}
// MintUnsigned creates a transaction invoking `mint` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) MintUnsigned(from util.Uint160, to util.Uint160, amount *big.Int, swapId *big.Int, signature []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "mint", nil, from, to, amount, swapId, signature, data)
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(nef []byte, manifest []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", nef, manifest)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", nef, manifest)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, nef, manifest)
}
// UpdateCap creates a transaction invoking `updateCap` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) UpdateCap(newCap *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "updateCap", newCap)
}
// UpdateCapTransaction creates a transaction invoking `updateCap` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateCapTransaction(newCap *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "updateCap", newCap)
}
// UpdateCapUnsigned creates a transaction invoking `updateCap` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateCapUnsigned(newCap *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "updateCap", nil, newCap)
}
// OnMintEventsFromApplicationLog retrieves a set of all emitted events
// with "OnMint" name from the provided [result.ApplicationLog].
func OnMintEventsFromApplicationLog(log *result.ApplicationLog) ([]*OnMintEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*OnMintEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "OnMint" {
continue
}
event := new(OnMintEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize OnMintEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to OnMintEvent or
// returns an error if it's not possible to do to so.
func (e *OnMintEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 4 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.From, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field From: %w", err)
}
index++
e.To, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field To: %w", err)
}
index++
e.Amount, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Amount: %w", err)
}
index++
e.SwapId, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field SwapId: %w", err)
}
return nil
}

View file

@ -1,275 +0,0 @@
{
"name" : "NEX Token",
"abi" : {
"events" : [
{
"parameters" : [
{
"type" : "Hash160",
"name" : "from"
},
{
"name" : "to",
"type" : "Hash160"
},
{
"name" : "amount",
"type" : "Integer"
}
],
"name" : "Transfer"
},
{
"name" : "OnMint",
"parameters" : [
{
"name" : "from",
"type" : "Hash160"
},
{
"type" : "Hash160",
"name" : "to"
},
{
"type" : "Integer",
"name" : "amount"
},
{
"name" : "swapId",
"type" : "Integer"
}
]
}
],
"methods" : [
{
"parameters" : [],
"offset" : 0,
"name" : "_initialize",
"safe" : false,
"returntype" : "Void"
},
{
"parameters" : [
{
"type" : "Any",
"name" : "data"
},
{
"name" : "isUpdate",
"type" : "Boolean"
}
],
"offset" : 3,
"name" : "_deploy",
"safe" : false,
"returntype" : "Void"
},
{
"parameters" : [
{
"type" : "Hash160",
"name" : "holder"
}
],
"offset" : 484,
"returntype" : "Integer",
"safe" : true,
"name" : "balanceOf"
},
{
"safe" : true,
"returntype" : "Integer",
"name" : "cap",
"offset" : 1881,
"parameters" : []
},
{
"name" : "changeMinter",
"safe" : false,
"returntype" : "Void",
"parameters" : [
{
"name" : "newMinter",
"type" : "PublicKey"
}
],
"offset" : 1678
},
{
"parameters" : [
{
"type" : "Hash160",
"name" : "newOwner"
}
],
"offset" : 1659,
"name" : "changeOwner",
"safe" : false,
"returntype" : "Void"
},
{
"offset" : 466,
"parameters" : [],
"safe" : true,
"name" : "decimals",
"returntype" : "Integer"
},
{
"returntype" : "Void",
"safe" : false,
"name" : "destroy",
"parameters" : [],
"offset" : 1194
},
{
"safe" : true,
"returntype" : "PublicKey",
"name" : "getMinter",
"offset" : 1671,
"parameters" : []
},
{
"parameters" : [],
"offset" : 1652,
"name" : "getOwner",
"safe" : true,
"returntype" : "Hash160"
},
{
"name" : "maxSupply",
"safe" : false,
"returntype" : "Integer",
"parameters" : [],
"offset" : 468
},
{
"offset" : 1222,
"parameters" : [
{
"name" : "from",
"type" : "Hash160"
},
{
"name" : "to",
"type" : "Hash160"
},
{
"type" : "Integer",
"name" : "amount"
},
{
"name" : "swapId",
"type" : "Integer"
},
{
"name" : "signature",
"type" : "Signature"
},
{
"name" : "data",
"type" : "Any"
}
],
"safe" : false,
"name" : "mint",
"returntype" : "Void"
},
{
"safe" : true,
"name" : "symbol",
"returntype" : "String",
"parameters" : [],
"offset" : 460
},
{
"offset" : 1854,
"parameters" : [],
"name" : "totalMinted",
"safe" : true,
"returntype" : "Integer"
},
{
"offset" : 478,
"parameters" : [],
"name" : "totalSupply",
"safe" : true,
"returntype" : "Integer"
},
{
"offset" : 543,
"parameters" : [
{
"name" : "from",
"type" : "Hash160"
},
{
"name" : "to",
"type" : "Hash160"
},
{
"type" : "Integer",
"name" : "amount"
},
{
"name" : "data",
"type" : "Any"
}
],
"name" : "transfer",
"safe" : false,
"returntype" : "Boolean"
},
{
"offset" : 1205,
"parameters" : [
{
"type" : "ByteArray",
"name" : "nef"
},
{
"name" : "manifest",
"type" : "ByteArray"
}
],
"safe" : false,
"returntype" : "Void",
"name" : "update"
},
{
"offset" : 1717,
"parameters" : [
{
"type" : "Integer",
"name" : "newCap"
}
],
"returntype" : "Void",
"safe" : false,
"name" : "updateCap"
}
]
},
"supportedstandards" : [
"NEP-17"
],
"extra" : null,
"trusts" : [],
"features" : {},
"groups" : [],
"permissions" : [
{
"methods" : [
"onNEP17Payment"
],
"contract" : "*"
},
{
"methods" : [
"update",
"destroy"
],
"contract" : "0xfffdc93764dbaddd97c48f252a53ea4643faa3fd"
}
]
}

View file

@ -1,63 +0,0 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package nonnepxxcontractwithiterators contains RPC wrappers for Non-NEPXX contract with iterators contract.
package nonnepxxcontractwithiterators
import (
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// Hash contains contract hash.
var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error)
CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...any) (*result.Invoke, error)
TerminateSession(sessionID uuid.UUID) error
TraverseIterator(sessionID uuid.UUID, iterator *result.Iterator, num int) ([]stackitem.Item, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
invoker Invoker
hash util.Uint160
}
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
func NewReader(invoker Invoker) *ContractReader {
var hash = Hash
return &ContractReader{invoker, hash}
}
// Tokens invokes `tokens` method of contract.
func (c *ContractReader) Tokens() (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.invoker.Call(c.hash, "tokens"))
}
// TokensExpanded is similar to Tokens (uses the same contract
// method), but can be useful if the server used doesn't support sessions and
// doesn't expand iterators. It creates a script that will get the specified
// number of result items from the iterator right in the VM and return them to
// you. It's only limited by VM stack and GAS available for RPC invocations.
func (c *ContractReader) TokensExpanded(_numOfIteratorItems int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "tokens", _numOfIteratorItems))
}
// GetAllRecords invokes `getAllRecords` method of contract.
func (c *ContractReader) GetAllRecords(name string) (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.invoker.Call(c.hash, "getAllRecords", name))
}
// GetAllRecordsExpanded is similar to GetAllRecords (uses the same contract
// method), but can be useful if the server used doesn't support sessions and
// doesn't expand iterators. It creates a script that will get the specified
// number of result items from the iterator right in the VM and return them to
// you. It's only limited by VM stack and GAS available for RPC invocations.
func (c *ContractReader) GetAllRecordsExpanded(name string, _numOfIteratorItems int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "getAllRecords", _numOfIteratorItems, name))
}

View file

@ -1,33 +0,0 @@
{
"groups" : [],
"abi" : {
"events" : [],
"methods" : [
{
"parameters" : [],
"safe" : true,
"name" : "tokens",
"offset" : 0,
"returntype" : "InteropInterface"
},
{
"offset" : 1,
"returntype" : "InteropInterface",
"safe" : true,
"parameters" : [
{
"type" : "String",
"name" : "name"
}
],
"name" : "getAllRecords"
}
]
},
"supportedstandards" : [],
"trusts" : [],
"extra" : {},
"permissions" : [],
"name" : "Non-NEPXX contract with iterators",
"features" : {}
}

View file

@ -1,7 +0,0 @@
package invalid1
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
func Main() {
runtime.Notify("Non declared event")
}

View file

@ -1 +0,0 @@
name: Test undeclared event

View file

@ -1,7 +0,0 @@
package invalid2
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
func Main() {
runtime.Notify("SomeEvent", "p1", "p2")
}

View file

@ -1,6 +0,0 @@
name: Test undeclared event
events:
- name: SomeEvent
parameters:
- name: p1
type: String

View file

@ -1,7 +0,0 @@
package invalid3
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
func Main() {
runtime.Notify("SomeEvent", "p1", 5)
}

View file

@ -1,8 +0,0 @@
name: Test undeclared event
events:
- name: SomeEvent
parameters:
- name: p1
type: String
- name: p2
type: String

View file

@ -1,17 +0,0 @@
package invalid4
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
type SomeStruct1 struct {
Field1 int
}
type SomeStruct2 struct {
Field2 string
}
func Main() {
// Inconsistent event params usages (different named types throughout the usages).
runtime.Notify("SomeEvent", SomeStruct1{Field1: 123})
runtime.Notify("SomeEvent", SomeStruct2{Field2: "str"})
}

View file

@ -1,6 +0,0 @@
name: Test undeclared event
events:
- name: SomeEvent
parameters:
- name: p1
type: Array

View file

@ -1,12 +0,0 @@
package invalid5
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
type NamedStruct struct {
SomeInt int
}
func Main() NamedStruct {
runtime.Notify("SomeEvent", []interface{}{123})
return NamedStruct{SomeInt: 123}
}

View file

@ -1,16 +0,0 @@
name: Test undeclared event
events:
- name: SomeEvent
parameters:
- name: p1
type: Array
extendedtype:
base: Array
name: invalid5.NamedStruct
namedtypes:
invalid5.NamedStruct:
base: Array
name: invalid5.NamedStruct
fields:
- field: SomeInt
base: Integer

View file

@ -1,14 +0,0 @@
package invalid6
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
type SomeStruct struct {
Field int
// RPC binding generator will convert this field into exported, which matches
// exactly the existing Field.
field int
}
func Main() {
runtime.Notify("SomeEvent", SomeStruct{Field: 123, field: 123})
}

View file

@ -1,18 +0,0 @@
name: Test duplicating event fields
events:
- name: SomeEvent
parameters:
- name: p1
type: Struct
extendedtype:
base: Struct
name: SomeStruct
namedtypes:
SomeStruct:
base: Struct
name: SomeStruct
fields:
- field: Field
base: Integer
- field: field
base: Integer

View file

@ -1,14 +0,0 @@
package invalid7
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
type SomeStruct struct {
Field int
// RPC binding generator will convert this field into exported, which matches
// exactly the existing Field.
field int
}
func Main() {
runtime.Notify("SomeEvent", SomeStruct{Field: 123, field: 123})
}

View file

@ -1,6 +0,0 @@
name: Test duplicating autogenerated event fields
events:
- name: SomeEvent
parameters:
- name: p1
type: Struct

View file

@ -1,16 +0,0 @@
package invalid8
type SomeStruct struct {
Field int
// RPC binding generator will convert this field into exported, which matches
// exactly the existing Field.
field int
}
func Main() SomeStruct {
s := SomeStruct{
Field: 1,
field: 2,
}
return s
}

View file

@ -1 +0,0 @@
name: Test duplicating struct fields

View file

@ -1,23 +0,0 @@
name: "Notifications"
sourceurl: https://github.com/nspcc-dev/neo-go/
events:
- name: "! complicated name %$#"
parameters:
- name: ! complicated param @#$%
type: String
- name: "SomeMap"
parameters:
- name: m
type: Map
- name: "SomeStruct"
parameters:
- name: s
type: Struct
- name: "SomeArray"
parameters:
- name: a
type: Array
- name: "SomeUnexportedField"
parameters:
- name: s
type: Struct

View file

@ -1,60 +0,0 @@
name: "Notifications"
sourceurl: https://github.com/nspcc-dev/neo-go/
events:
- name: "! complicated name %$#"
parameters:
- name: ! complicated param @#$%
type: String
- name: "SomeMap"
parameters:
- name: m
type: Map
extendedtype:
base: Map
key: Integer
value:
base: Map
key: String
value:
base: Array
value:
base: Hash160
- name: "SomeStruct"
parameters:
- name: s
type: Struct
extendedtype:
base: Struct
name: crazyStruct
- name: "SomeArray"
parameters:
- name: a
type: Array
extendedtype:
base: Array
value:
base: Array
value:
base: Integer
- name: "SomeUnexportedField"
parameters:
- name: s
type: Struct
extendedtype:
base: Struct
name: simpleStruct
namedtypes:
crazyStruct:
base: Struct
name: crazyStruct
fields:
- field: I
base: Integer
- field: B
base: Boolean
simpleStruct:
base: Struct
name: simpleStruct
fields:
- field: i
base: Integer

View file

@ -1,23 +0,0 @@
name: "Notifications"
sourceurl: https://github.com/nspcc-dev/neo-go/
events:
- name: "! complicated name %$#"
parameters:
- name: ! complicated param @#$%
type: String
- name: "SomeMap"
parameters:
- name: m
type: Map
- name: "SomeStruct"
parameters:
- name: s
type: Struct
- name: "SomeArray"
parameters:
- name: a
type: Array
- name: "SomeUnexportedField"
parameters:
- name: s
type: Struct

View file

@ -1,33 +0,0 @@
package structs
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
)
func Main() {
runtime.Notify("! complicated name %$#", "str1")
}
func CrazyMap() {
runtime.Notify("SomeMap", map[int][]map[string][]interop.Hash160{})
}
func Struct() {
runtime.Notify("SomeStruct", struct {
I int
B bool
}{I: 123, B: true})
}
func Array() {
runtime.Notify("SomeArray", [][]int{})
}
// UnexportedField emits notification with unexported field that must be converted
// to exported in the resulting RPC binding.
func UnexportedField() {
runtime.Notify("SomeUnexportedField", struct {
i int
}{i: 123})
}

View file

@ -1,500 +0,0 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package structs contains RPC wrappers for Notifications contract.
package structs
import (
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"unicode/utf8"
)
// Hash contains contract hash.
var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0}
// ComplicatedNameEvent represents "! complicated name %$#" event emitted by the contract.
type ComplicatedNameEvent struct {
ComplicatedParam string
}
// SomeMapEvent represents "SomeMap" event emitted by the contract.
type SomeMapEvent struct {
M map[any]any
}
// SomeStructEvent represents "SomeStruct" event emitted by the contract.
type SomeStructEvent struct {
S []any
}
// SomeArrayEvent represents "SomeArray" event emitted by the contract.
type SomeArrayEvent struct {
A []any
}
// SomeUnexportedFieldEvent represents "SomeUnexportedField" event emitted by the contract.
type SomeUnexportedFieldEvent struct {
S []any
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// Contract implements all contract methods.
type Contract struct {
actor Actor
hash util.Uint160
}
// New creates an instance of Contract using Hash and the given Actor.
func New(actor Actor) *Contract {
var hash = Hash
return &Contract{actor, hash}
}
// Array creates a transaction invoking `array` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Array() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "array")
}
// ArrayTransaction creates a transaction invoking `array` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) ArrayTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "array")
}
// ArrayUnsigned creates a transaction invoking `array` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) ArrayUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "array", nil)
}
// CrazyMap creates a transaction invoking `crazyMap` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) CrazyMap() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "crazyMap")
}
// CrazyMapTransaction creates a transaction invoking `crazyMap` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) CrazyMapTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "crazyMap")
}
// CrazyMapUnsigned creates a transaction invoking `crazyMap` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) CrazyMapUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "crazyMap", nil)
}
// Main creates a transaction invoking `main` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Main() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "main")
}
// MainTransaction creates a transaction invoking `main` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) MainTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "main")
}
// MainUnsigned creates a transaction invoking `main` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) MainUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "main", nil)
}
// Struct creates a transaction invoking `struct` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Struct() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "struct")
}
// StructTransaction creates a transaction invoking `struct` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) StructTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "struct")
}
// StructUnsigned creates a transaction invoking `struct` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) StructUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "struct", nil)
}
// UnexportedField creates a transaction invoking `unexportedField` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) UnexportedField() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "unexportedField")
}
// UnexportedFieldTransaction creates a transaction invoking `unexportedField` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UnexportedFieldTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "unexportedField")
}
// UnexportedFieldUnsigned creates a transaction invoking `unexportedField` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UnexportedFieldUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "unexportedField", nil)
}
// ComplicatedNameEventsFromApplicationLog retrieves a set of all emitted events
// with "! complicated name %$#" name from the provided [result.ApplicationLog].
func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*ComplicatedNameEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*ComplicatedNameEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "! complicated name %$#" {
continue
}
event := new(ComplicatedNameEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize ComplicatedNameEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to ComplicatedNameEvent or
// returns an error if it's not possible to do to so.
func (e *ComplicatedNameEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.ComplicatedParam, err = func(item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
}(arr[index])
if err != nil {
return fmt.Errorf("field ComplicatedParam: %w", err)
}
return nil
}
// SomeMapEventsFromApplicationLog retrieves a set of all emitted events
// with "SomeMap" name from the provided [result.ApplicationLog].
func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*SomeMapEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "SomeMap" {
continue
}
event := new(SomeMapEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize SomeMapEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to SomeMapEvent or
// returns an error if it's not possible to do to so.
func (e *SomeMapEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.M, err = func(item stackitem.Item) (map[any]any, error) {
m, ok := item.Value().([]stackitem.MapElement)
if !ok {
return nil, fmt.Errorf("%s is not a map", item.Type().String())
}
res := make(map[any]any)
for i := range m {
k, err := m[i].Key.Value(), error(nil)
if err != nil {
return nil, fmt.Errorf("key %d: %w", i, err)
}
v, err := m[i].Value.Value(), error(nil)
if err != nil {
return nil, fmt.Errorf("value %d: %w", i, err)
}
res[k] = v
}
return res, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field M: %w", err)
}
return nil
}
// SomeStructEventsFromApplicationLog retrieves a set of all emitted events
// with "SomeStruct" name from the provided [result.ApplicationLog].
func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStructEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*SomeStructEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "SomeStruct" {
continue
}
event := new(SomeStructEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize SomeStructEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to SomeStructEvent or
// returns an error if it's not possible to do to so.
func (e *SomeStructEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.S, err = func(item stackitem.Item) ([]any, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([]any, len(arr))
for i := range res {
res[i], err = arr[i].Value(), error(nil)
if err != nil {
return nil, fmt.Errorf("item %d: %w", i, err)
}
}
return res, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field S: %w", err)
}
return nil
}
// SomeArrayEventsFromApplicationLog retrieves a set of all emitted events
// with "SomeArray" name from the provided [result.ApplicationLog].
func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArrayEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*SomeArrayEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "SomeArray" {
continue
}
event := new(SomeArrayEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize SomeArrayEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to SomeArrayEvent or
// returns an error if it's not possible to do to so.
func (e *SomeArrayEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.A, err = func(item stackitem.Item) ([]any, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([]any, len(arr))
for i := range res {
res[i], err = arr[i].Value(), error(nil)
if err != nil {
return nil, fmt.Errorf("item %d: %w", i, err)
}
}
return res, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field A: %w", err)
}
return nil
}
// SomeUnexportedFieldEventsFromApplicationLog retrieves a set of all emitted events
// with "SomeUnexportedField" name from the provided [result.ApplicationLog].
func SomeUnexportedFieldEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeUnexportedFieldEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*SomeUnexportedFieldEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "SomeUnexportedField" {
continue
}
event := new(SomeUnexportedFieldEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize SomeUnexportedFieldEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to SomeUnexportedFieldEvent or
// returns an error if it's not possible to do to so.
func (e *SomeUnexportedFieldEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.S, err = func(item stackitem.Item) ([]any, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([]any, len(arr))
for i := range res {
res[i], err = arr[i].Value(), error(nil)
if err != nil {
return nil, fmt.Errorf("item %d: %w", i, err)
}
}
return res, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field S: %w", err)
}
return nil
}

View file

@ -1,623 +0,0 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package structs contains RPC wrappers for Notifications contract.
package structs
import (
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
"unicode/utf8"
)
// Hash contains contract hash.
var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0}
// CrazyStruct is a contract-specific crazyStruct type used by its methods.
type CrazyStruct struct {
I *big.Int
B bool
}
// SimpleStruct is a contract-specific simpleStruct type used by its methods.
type SimpleStruct struct {
I *big.Int
}
// ComplicatedNameEvent represents "! complicated name %$#" event emitted by the contract.
type ComplicatedNameEvent struct {
ComplicatedParam string
}
// SomeMapEvent represents "SomeMap" event emitted by the contract.
type SomeMapEvent struct {
M map[*big.Int]map[string][]util.Uint160
}
// SomeStructEvent represents "SomeStruct" event emitted by the contract.
type SomeStructEvent struct {
S *CrazyStruct
}
// SomeArrayEvent represents "SomeArray" event emitted by the contract.
type SomeArrayEvent struct {
A [][]*big.Int
}
// SomeUnexportedFieldEvent represents "SomeUnexportedField" event emitted by the contract.
type SomeUnexportedFieldEvent struct {
S *SimpleStruct
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// Contract implements all contract methods.
type Contract struct {
actor Actor
hash util.Uint160
}
// New creates an instance of Contract using Hash and the given Actor.
func New(actor Actor) *Contract {
var hash = Hash
return &Contract{actor, hash}
}
// Array creates a transaction invoking `array` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Array() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "array")
}
// ArrayTransaction creates a transaction invoking `array` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) ArrayTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "array")
}
// ArrayUnsigned creates a transaction invoking `array` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) ArrayUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "array", nil)
}
// CrazyMap creates a transaction invoking `crazyMap` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) CrazyMap() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "crazyMap")
}
// CrazyMapTransaction creates a transaction invoking `crazyMap` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) CrazyMapTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "crazyMap")
}
// CrazyMapUnsigned creates a transaction invoking `crazyMap` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) CrazyMapUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "crazyMap", nil)
}
// Main creates a transaction invoking `main` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Main() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "main")
}
// MainTransaction creates a transaction invoking `main` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) MainTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "main")
}
// MainUnsigned creates a transaction invoking `main` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) MainUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "main", nil)
}
// Struct creates a transaction invoking `struct` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Struct() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "struct")
}
// StructTransaction creates a transaction invoking `struct` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) StructTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "struct")
}
// StructUnsigned creates a transaction invoking `struct` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) StructUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "struct", nil)
}
// UnexportedField creates a transaction invoking `unexportedField` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) UnexportedField() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "unexportedField")
}
// UnexportedFieldTransaction creates a transaction invoking `unexportedField` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UnexportedFieldTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "unexportedField")
}
// UnexportedFieldUnsigned creates a transaction invoking `unexportedField` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UnexportedFieldUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "unexportedField", nil)
}
// itemToCrazyStruct converts stack item into *CrazyStruct.
func itemToCrazyStruct(item stackitem.Item, err error) (*CrazyStruct, error) {
if err != nil {
return nil, err
}
var res = new(CrazyStruct)
err = res.FromStackItem(item)
return res, err
}
// FromStackItem retrieves fields of CrazyStruct from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
func (res *CrazyStruct) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 2 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
res.I, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field I: %w", err)
}
index++
res.B, err = arr[index].TryBool()
if err != nil {
return fmt.Errorf("field B: %w", err)
}
return nil
}
// itemToSimpleStruct converts stack item into *SimpleStruct.
func itemToSimpleStruct(item stackitem.Item, err error) (*SimpleStruct, error) {
if err != nil {
return nil, err
}
var res = new(SimpleStruct)
err = res.FromStackItem(item)
return res, err
}
// FromStackItem retrieves fields of SimpleStruct from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
func (res *SimpleStruct) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
res.I, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field I: %w", err)
}
return nil
}
// ComplicatedNameEventsFromApplicationLog retrieves a set of all emitted events
// with "! complicated name %$#" name from the provided [result.ApplicationLog].
func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*ComplicatedNameEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*ComplicatedNameEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "! complicated name %$#" {
continue
}
event := new(ComplicatedNameEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize ComplicatedNameEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to ComplicatedNameEvent or
// returns an error if it's not possible to do to so.
func (e *ComplicatedNameEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.ComplicatedParam, err = func(item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
}(arr[index])
if err != nil {
return fmt.Errorf("field ComplicatedParam: %w", err)
}
return nil
}
// SomeMapEventsFromApplicationLog retrieves a set of all emitted events
// with "SomeMap" name from the provided [result.ApplicationLog].
func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*SomeMapEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "SomeMap" {
continue
}
event := new(SomeMapEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize SomeMapEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to SomeMapEvent or
// returns an error if it's not possible to do to so.
func (e *SomeMapEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.M, err = func(item stackitem.Item) (map[*big.Int]map[string][]util.Uint160, error) {
m, ok := item.Value().([]stackitem.MapElement)
if !ok {
return nil, fmt.Errorf("%s is not a map", item.Type().String())
}
res := make(map[*big.Int]map[string][]util.Uint160)
for i := range m {
k, err := m[i].Key.TryInteger()
if err != nil {
return nil, fmt.Errorf("key %d: %w", i, err)
}
v, err := func(item stackitem.Item) (map[string][]util.Uint160, error) {
m, ok := item.Value().([]stackitem.MapElement)
if !ok {
return nil, fmt.Errorf("%s is not a map", item.Type().String())
}
res := make(map[string][]util.Uint160)
for i := range m {
k, err := func(item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
}(m[i].Key)
if err != nil {
return nil, fmt.Errorf("key %d: %w", i, err)
}
v, err := func(item stackitem.Item) ([]util.Uint160, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([]util.Uint160, len(arr))
for i := range res {
res[i], err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[i])
if err != nil {
return nil, fmt.Errorf("item %d: %w", i, err)
}
}
return res, nil
}(m[i].Value)
if err != nil {
return nil, fmt.Errorf("value %d: %w", i, err)
}
res[k] = v
}
return res, nil
}(m[i].Value)
if err != nil {
return nil, fmt.Errorf("value %d: %w", i, err)
}
res[k] = v
}
return res, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field M: %w", err)
}
return nil
}
// SomeStructEventsFromApplicationLog retrieves a set of all emitted events
// with "SomeStruct" name from the provided [result.ApplicationLog].
func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStructEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*SomeStructEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "SomeStruct" {
continue
}
event := new(SomeStructEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize SomeStructEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to SomeStructEvent or
// returns an error if it's not possible to do to so.
func (e *SomeStructEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.S, err = itemToCrazyStruct(arr[index], nil)
if err != nil {
return fmt.Errorf("field S: %w", err)
}
return nil
}
// SomeArrayEventsFromApplicationLog retrieves a set of all emitted events
// with "SomeArray" name from the provided [result.ApplicationLog].
func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArrayEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*SomeArrayEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "SomeArray" {
continue
}
event := new(SomeArrayEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize SomeArrayEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to SomeArrayEvent or
// returns an error if it's not possible to do to so.
func (e *SomeArrayEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.A, err = func(item stackitem.Item) ([][]*big.Int, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([][]*big.Int, len(arr))
for i := range res {
res[i], err = func(item stackitem.Item) ([]*big.Int, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([]*big.Int, len(arr))
for i := range res {
res[i], err = arr[i].TryInteger()
if err != nil {
return nil, fmt.Errorf("item %d: %w", i, err)
}
}
return res, nil
}(arr[i])
if err != nil {
return nil, fmt.Errorf("item %d: %w", i, err)
}
}
return res, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field A: %w", err)
}
return nil
}
// SomeUnexportedFieldEventsFromApplicationLog retrieves a set of all emitted events
// with "SomeUnexportedField" name from the provided [result.ApplicationLog].
func SomeUnexportedFieldEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeUnexportedFieldEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*SomeUnexportedFieldEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "SomeUnexportedField" {
continue
}
event := new(SomeUnexportedFieldEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize SomeUnexportedFieldEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to SomeUnexportedFieldEvent or
// returns an error if it's not possible to do to so.
func (e *SomeUnexportedFieldEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.S, err = itemToSimpleStruct(arr[index], nil)
if err != nil {
return fmt.Errorf("field S: %w", err)
}
return nil
}

View file

@ -1,636 +0,0 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package structs contains RPC wrappers for Notifications contract.
package structs
import (
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
"unicode/utf8"
)
// Hash contains contract hash.
var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0}
// Unnamed is a contract-specific unnamed type used by its methods.
type Unnamed struct {
I *big.Int
B bool
}
// UnnamedX is a contract-specific unnamedX type used by its methods.
type UnnamedX struct {
I *big.Int
}
// ComplicatedNameEvent represents "! complicated name %$#" event emitted by the contract.
type ComplicatedNameEvent struct {
ComplicatedParam string
}
// SomeMapEvent represents "SomeMap" event emitted by the contract.
type SomeMapEvent struct {
M map[*big.Int][]map[string][]util.Uint160
}
// SomeStructEvent represents "SomeStruct" event emitted by the contract.
type SomeStructEvent struct {
S *Unnamed
}
// SomeArrayEvent represents "SomeArray" event emitted by the contract.
type SomeArrayEvent struct {
A [][]*big.Int
}
// SomeUnexportedFieldEvent represents "SomeUnexportedField" event emitted by the contract.
type SomeUnexportedFieldEvent struct {
S *UnnamedX
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// Contract implements all contract methods.
type Contract struct {
actor Actor
hash util.Uint160
}
// New creates an instance of Contract using Hash and the given Actor.
func New(actor Actor) *Contract {
var hash = Hash
return &Contract{actor, hash}
}
// Array creates a transaction invoking `array` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Array() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "array")
}
// ArrayTransaction creates a transaction invoking `array` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) ArrayTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "array")
}
// ArrayUnsigned creates a transaction invoking `array` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) ArrayUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "array", nil)
}
// CrazyMap creates a transaction invoking `crazyMap` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) CrazyMap() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "crazyMap")
}
// CrazyMapTransaction creates a transaction invoking `crazyMap` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) CrazyMapTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "crazyMap")
}
// CrazyMapUnsigned creates a transaction invoking `crazyMap` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) CrazyMapUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "crazyMap", nil)
}
// Main creates a transaction invoking `main` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Main() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "main")
}
// MainTransaction creates a transaction invoking `main` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) MainTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "main")
}
// MainUnsigned creates a transaction invoking `main` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) MainUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "main", nil)
}
// Struct creates a transaction invoking `struct` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Struct() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "struct")
}
// StructTransaction creates a transaction invoking `struct` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) StructTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "struct")
}
// StructUnsigned creates a transaction invoking `struct` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) StructUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "struct", nil)
}
// UnexportedField creates a transaction invoking `unexportedField` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) UnexportedField() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "unexportedField")
}
// UnexportedFieldTransaction creates a transaction invoking `unexportedField` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UnexportedFieldTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "unexportedField")
}
// UnexportedFieldUnsigned creates a transaction invoking `unexportedField` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UnexportedFieldUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "unexportedField", nil)
}
// itemToUnnamed converts stack item into *Unnamed.
func itemToUnnamed(item stackitem.Item, err error) (*Unnamed, error) {
if err != nil {
return nil, err
}
var res = new(Unnamed)
err = res.FromStackItem(item)
return res, err
}
// FromStackItem retrieves fields of Unnamed from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
func (res *Unnamed) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 2 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
res.I, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field I: %w", err)
}
index++
res.B, err = arr[index].TryBool()
if err != nil {
return fmt.Errorf("field B: %w", err)
}
return nil
}
// itemToUnnamedX converts stack item into *UnnamedX.
func itemToUnnamedX(item stackitem.Item, err error) (*UnnamedX, error) {
if err != nil {
return nil, err
}
var res = new(UnnamedX)
err = res.FromStackItem(item)
return res, err
}
// FromStackItem retrieves fields of UnnamedX from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
func (res *UnnamedX) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
res.I, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field I: %w", err)
}
return nil
}
// ComplicatedNameEventsFromApplicationLog retrieves a set of all emitted events
// with "! complicated name %$#" name from the provided [result.ApplicationLog].
func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*ComplicatedNameEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*ComplicatedNameEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "! complicated name %$#" {
continue
}
event := new(ComplicatedNameEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize ComplicatedNameEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to ComplicatedNameEvent or
// returns an error if it's not possible to do to so.
func (e *ComplicatedNameEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.ComplicatedParam, err = func(item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
}(arr[index])
if err != nil {
return fmt.Errorf("field ComplicatedParam: %w", err)
}
return nil
}
// SomeMapEventsFromApplicationLog retrieves a set of all emitted events
// with "SomeMap" name from the provided [result.ApplicationLog].
func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*SomeMapEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "SomeMap" {
continue
}
event := new(SomeMapEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize SomeMapEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to SomeMapEvent or
// returns an error if it's not possible to do to so.
func (e *SomeMapEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.M, err = func(item stackitem.Item) (map[*big.Int][]map[string][]util.Uint160, error) {
m, ok := item.Value().([]stackitem.MapElement)
if !ok {
return nil, fmt.Errorf("%s is not a map", item.Type().String())
}
res := make(map[*big.Int][]map[string][]util.Uint160)
for i := range m {
k, err := m[i].Key.TryInteger()
if err != nil {
return nil, fmt.Errorf("key %d: %w", i, err)
}
v, err := func(item stackitem.Item) ([]map[string][]util.Uint160, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([]map[string][]util.Uint160, len(arr))
for i := range res {
res[i], err = func(item stackitem.Item) (map[string][]util.Uint160, error) {
m, ok := item.Value().([]stackitem.MapElement)
if !ok {
return nil, fmt.Errorf("%s is not a map", item.Type().String())
}
res := make(map[string][]util.Uint160)
for i := range m {
k, err := func(item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
}(m[i].Key)
if err != nil {
return nil, fmt.Errorf("key %d: %w", i, err)
}
v, err := func(item stackitem.Item) ([]util.Uint160, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([]util.Uint160, len(arr))
for i := range res {
res[i], err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[i])
if err != nil {
return nil, fmt.Errorf("item %d: %w", i, err)
}
}
return res, nil
}(m[i].Value)
if err != nil {
return nil, fmt.Errorf("value %d: %w", i, err)
}
res[k] = v
}
return res, nil
}(arr[i])
if err != nil {
return nil, fmt.Errorf("item %d: %w", i, err)
}
}
return res, nil
}(m[i].Value)
if err != nil {
return nil, fmt.Errorf("value %d: %w", i, err)
}
res[k] = v
}
return res, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field M: %w", err)
}
return nil
}
// SomeStructEventsFromApplicationLog retrieves a set of all emitted events
// with "SomeStruct" name from the provided [result.ApplicationLog].
func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStructEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*SomeStructEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "SomeStruct" {
continue
}
event := new(SomeStructEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize SomeStructEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to SomeStructEvent or
// returns an error if it's not possible to do to so.
func (e *SomeStructEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.S, err = itemToUnnamed(arr[index], nil)
if err != nil {
return fmt.Errorf("field S: %w", err)
}
return nil
}
// SomeArrayEventsFromApplicationLog retrieves a set of all emitted events
// with "SomeArray" name from the provided [result.ApplicationLog].
func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArrayEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*SomeArrayEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "SomeArray" {
continue
}
event := new(SomeArrayEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize SomeArrayEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to SomeArrayEvent or
// returns an error if it's not possible to do to so.
func (e *SomeArrayEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.A, err = func(item stackitem.Item) ([][]*big.Int, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([][]*big.Int, len(arr))
for i := range res {
res[i], err = func(item stackitem.Item) ([]*big.Int, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([]*big.Int, len(arr))
for i := range res {
res[i], err = arr[i].TryInteger()
if err != nil {
return nil, fmt.Errorf("item %d: %w", i, err)
}
}
return res, nil
}(arr[i])
if err != nil {
return nil, fmt.Errorf("item %d: %w", i, err)
}
}
return res, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field A: %w", err)
}
return nil
}
// SomeUnexportedFieldEventsFromApplicationLog retrieves a set of all emitted events
// with "SomeUnexportedField" name from the provided [result.ApplicationLog].
func SomeUnexportedFieldEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeUnexportedFieldEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*SomeUnexportedFieldEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "SomeUnexportedField" {
continue
}
event := new(SomeUnexportedFieldEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize SomeUnexportedFieldEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to SomeUnexportedFieldEvent or
// returns an error if it's not possible to do to so.
func (e *SomeUnexportedFieldEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.S, err = itemToUnnamedX(arr[index], nil)
if err != nil {
return fmt.Errorf("field S: %w", err)
}
return nil
}

View file

@ -1,3 +0,0 @@
name: "Types"
sourceurl: https://github.com/nspcc-dev/neo-go/
safemethods: ["contract", "block", "transaction", "struct"]

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more