Compare commits
1 commit
master
...
jsonpath-d
Author | SHA1 | Date | |
---|---|---|---|
|
e4ec4052d8 |
952 changed files with 33966 additions and 87620 deletions
121
.circleci/config.yml
Normal file
121
.circleci/config.yml
Normal 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]+/
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "1.0",
|
||||
"version": "3.0",
|
||||
"accounts": [
|
||||
{
|
||||
"address": "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn",
|
||||
|
@ -16,7 +16,7 @@
|
|||
"deployed": false
|
||||
},
|
||||
"lock": false,
|
||||
"isDefault": false
|
||||
"isdefault": false
|
||||
},
|
||||
{
|
||||
"address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq",
|
||||
|
@ -41,7 +41,7 @@
|
|||
"deployed": false
|
||||
},
|
||||
"lock": false,
|
||||
"isDefault": false
|
||||
"isdefault": false
|
||||
}
|
||||
],
|
||||
"scrypt": {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "1.0",
|
||||
"version": "3.0",
|
||||
"accounts": [
|
||||
{
|
||||
"address": "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn",
|
||||
|
@ -16,7 +16,7 @@
|
|||
"deployed": false
|
||||
},
|
||||
"lock": false,
|
||||
"isDefault": false
|
||||
"isdefault": false
|
||||
},
|
||||
{
|
||||
"address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq",
|
||||
|
@ -41,7 +41,7 @@
|
|||
"deployed": false
|
||||
},
|
||||
"lock": false,
|
||||
"isDefault": false
|
||||
"isdefault": false
|
||||
},
|
||||
{
|
||||
"address": "NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP",
|
||||
|
@ -58,7 +58,7 @@
|
|||
"deployed": false
|
||||
},
|
||||
"lock": false,
|
||||
"isDefault": false
|
||||
"isdefault": false
|
||||
}
|
||||
],
|
||||
"scrypt": {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "1.0",
|
||||
"version": "3.0",
|
||||
"accounts": [
|
||||
{
|
||||
"address": "NMUedC8TSV2rE17wGguSvPk9XcmHSaT275",
|
||||
|
@ -16,7 +16,7 @@
|
|||
"deployed": false
|
||||
},
|
||||
"lock": false,
|
||||
"isDefault": false
|
||||
"isdefault": false
|
||||
},
|
||||
{
|
||||
"address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq",
|
||||
|
@ -41,7 +41,7 @@
|
|||
"deployed": false
|
||||
},
|
||||
"lock": false,
|
||||
"isDefault": false
|
||||
"isdefault": false
|
||||
}
|
||||
],
|
||||
"scrypt": {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "1.0",
|
||||
"version": "3.0",
|
||||
"accounts": [
|
||||
{
|
||||
"address": "NdypBhqkz2CMMnwxBgvoC9X2XjKF5axgKo",
|
||||
|
@ -16,7 +16,7 @@
|
|||
"deployed": false
|
||||
},
|
||||
"lock": false,
|
||||
"isDefault": false
|
||||
"isdefault": false
|
||||
},
|
||||
{
|
||||
"address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq",
|
||||
|
@ -41,7 +41,7 @@
|
|||
"deployed": false
|
||||
},
|
||||
"lock": false,
|
||||
"isDefault": false
|
||||
"isdefault": false
|
||||
}
|
||||
],
|
||||
"scrypt": {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "1.0",
|
||||
"version": "3.0",
|
||||
"accounts": [
|
||||
{
|
||||
"address": "NPrB7BmTMYxf9UVroJp4RQExM9tqKmsHTz",
|
||||
|
@ -16,7 +16,7 @@
|
|||
"deployed": false
|
||||
},
|
||||
"lock": false,
|
||||
"isDefault": false
|
||||
"isdefault": false
|
||||
},
|
||||
{
|
||||
"address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq",
|
||||
|
@ -41,7 +41,7 @@
|
|||
"deployed": false
|
||||
},
|
||||
"lock": false,
|
||||
"isDefault": false
|
||||
"isdefault": false
|
||||
}
|
||||
],
|
||||
"scrypt": {
|
||||
|
|
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -1 +0,0 @@
|
|||
* @AnnaShaleva @roman-khimov
|
BIN
.github/logo_dark.png
vendored
BIN
.github/logo_dark.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
169
.github/workflows/build.yml
vendored
169
.github/workflows/build.yml
vendored
|
@ -8,145 +8,110 @@ on:
|
|||
paths-ignore:
|
||||
- 'scripts/**'
|
||||
- '**/*.md'
|
||||
push:
|
||||
# Build for the master branch.
|
||||
branches:
|
||||
- master
|
||||
release:
|
||||
# Publish released commit as Docker `latest` and `git_revision` images.
|
||||
types:
|
||||
- published
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ref:
|
||||
description: 'Ref to build CLI 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'
|
||||
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'
|
||||
|
||||
env:
|
||||
GO111MODULE: "on"
|
||||
|
||||
jobs:
|
||||
build_cli:
|
||||
name: Build CLI
|
||||
runs-on: ${{matrix.os.name}}
|
||||
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'
|
||||
build_cli_ubuntu:
|
||||
name: Build CLI (Ubuntu)
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- 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: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v2
|
||||
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
|
||||
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
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: neo-go-${{ matrix.os.bin-name }}-${{ matrix.arch }}
|
||||
path: ./bin/neo-go*
|
||||
name: neo-go-binaries-ubuntu
|
||||
path: ./bin/neo-go
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Attach binary to the release as an asset
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
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)
|
||||
build_cli_wsc:
|
||||
name: Build CLI (Windows Server Core)
|
||||
runs-on: windows-2022
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.event.inputs.ref }}
|
||||
fetch-depth: 0
|
||||
|
||||
# For proper `deps` make target execution.
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: 1.17
|
||||
|
||||
- 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
|
||||
- name: Restore Go modules from cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
path: /home/runner/go/pkg/mod
|
||||
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
|
||||
run: make image
|
||||
|
||||
- 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
|
||||
build_image_wsc:
|
||||
needs: build_cli_wsc
|
||||
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
67
.github/workflows/codeql-analysis.yml
vendored
Normal 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
|
11
.github/workflows/contribution_guidelines.yml
vendored
11
.github/workflows/contribution_guidelines.yml
vendored
|
@ -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
|
155
.github/workflows/publish_to_dockerhub.yml
vendored
Normal file
155
.github/workflows/publish_to_dockerhub.yml
vendored
Normal 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
120
.github/workflows/run_tests.yml
vendored
Normal 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 ./...
|
190
.github/workflows/tests.yml
vendored
190
.github/workflows/tests.yml
vendored
|
@ -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
19
.gitignore
vendored
|
@ -7,6 +7,9 @@
|
|||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Added by CoZ developers
|
||||
vendor/
|
||||
bin/
|
||||
|
@ -39,19 +42,3 @@ coverage.html
|
|||
# Compiler output
|
||||
examples/*/*.nef
|
||||
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
|
||||
|
|
|
@ -32,32 +32,20 @@ linters:
|
|||
- revive
|
||||
|
||||
# some default golangci-lint linters
|
||||
- deadcode
|
||||
- errcheck
|
||||
- gosimple
|
||||
- godot
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- typecheck
|
||||
- unused
|
||||
- varcheck
|
||||
|
||||
# extra linters
|
||||
# - exhaustive
|
||||
# - goconst
|
||||
# - goerr113
|
||||
# - gomnd
|
||||
# - nonamedreturns
|
||||
# - unparam
|
||||
- bidichk
|
||||
- bodyclose
|
||||
- contextcheck
|
||||
- decorder
|
||||
- durationcheck
|
||||
- errorlint
|
||||
- exportloopref
|
||||
- gofmt
|
||||
- misspell
|
||||
- predeclared
|
||||
- reassign
|
||||
- whitespace
|
||||
- goimports
|
||||
disable-all: true
|
||||
|
@ -69,7 +57,3 @@ issues:
|
|||
- EXC0003 # test/Test ... consider calling this
|
||||
- EXC0004 # govet
|
||||
- EXC0005 # C-style breaks
|
||||
exclude-rules:
|
||||
- linters:
|
||||
- revive
|
||||
text: "unused-parameter"
|
||||
|
|
16
.travis.yml
Normal file
16
.travis.yml
Normal 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
|
1368
CHANGELOG.md
1368
CHANGELOG.md
File diff suppressed because it is too large
Load diff
|
@ -5,9 +5,9 @@ follow the guidelines:
|
|||
|
||||
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.
|
||||
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. When optimizing something, write benchmarks and attach the results:
|
||||
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. 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 ./... >new // on your branch
|
||||
|
@ -15,11 +15,11 @@ follow the guidelines:
|
|||
```
|
||||
`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
|
||||
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
|
||||
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.
|
||||
1. **Have fun!**
|
||||
|
|
10
Dockerfile
10
Dockerfile
|
@ -1,9 +1,5 @@
|
|||
# Builder image
|
||||
# Keep go version in sync with Build GA job.
|
||||
FROM golang:1.22-alpine as builder
|
||||
|
||||
# Display go version for information purposes.
|
||||
RUN go version
|
||||
FROM golang:1-alpine as builder
|
||||
|
||||
RUN set -x \
|
||||
&& apk add --no-cache git make \
|
||||
|
@ -16,12 +12,12 @@ WORKDIR /neo-go
|
|||
ARG REPO=repository
|
||||
ARG VERSION=dev
|
||||
|
||||
RUN VERSION=$VERSION REPO=$REPO make build
|
||||
RUN make build
|
||||
|
||||
# Executable image
|
||||
FROM alpine
|
||||
|
||||
ARG VERSION=dev
|
||||
ARG VERSION
|
||||
LABEL version=$VERSION
|
||||
|
||||
WORKDIR /
|
||||
|
|
|
@ -1,6 +1,25 @@
|
|||
# Builder image
|
||||
# Keep go version in sync with Build GA job.
|
||||
FROM golang:1.22.0-windowsservercore-ltsc2022 as builder
|
||||
FROM mcr.microsoft.com/windows/servercore: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
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
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
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
76
Makefile
76
Makefile
|
@ -1,22 +1,18 @@
|
|||
BRANCH = "master"
|
||||
REPONAME = "neo-go"
|
||||
NETMODE ?= "privnet"
|
||||
BINARY=neo-go
|
||||
BINARY_PATH=./bin/$(BINARY)$(shell go env GOEXE)
|
||||
GO_VERSION ?= 1.20
|
||||
BINARY=./bin/neo-go$(shell go env GOEXE)
|
||||
DESTDIR = ""
|
||||
SYSCONFIGDIR = "/etc"
|
||||
BINDIR = "/usr/bin"
|
||||
SYSTEMDUNIT_DIR = "/lib/systemd/system"
|
||||
UNITWORKDIR = "/var/lib/neo-go"
|
||||
|
||||
IMAGE_SUFFIX="$(shell if [ "$(OS)" = Windows_NT ]; then echo "_WindowsServerCore"; fi)"
|
||||
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"
|
||||
DC_FILE=.docker/docker-compose.yml
|
||||
|
||||
GOOS ?= $(shell go env GOOS)
|
||||
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')"
|
||||
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
|
||||
# tracking for files
|
||||
.PHONY: build $(BINARY) deps image docker/$(BINARY) image-latest image-push image-push-latest clean-cluster \
|
||||
test vet lint fmt cover version gh-docker-vars
|
||||
.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
|
||||
|
||||
build: deps
|
||||
@echo "=> Building binary"
|
||||
@set -x \
|
||||
&& export GOGC=off \
|
||||
&& export CGO_ENABLED=0 \
|
||||
&& go build -trimpath -v -ldflags $(BUILD_FLAGS) -o ${BINARY_PATH} ./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)
|
||||
&& go build -trimpath -v -ldflags $(BUILD_FLAGS) -o ${BINARY} ./cli/main.go
|
||||
|
||||
neo-go.service: neo-go.service.template
|
||||
@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.privnet.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
|
||||
@echo "=> Preparing directories and configs"
|
||||
|
@ -66,40 +51,43 @@ postinst: install
|
|||
&& systemctl enable neo-go.service
|
||||
|
||||
image: deps
|
||||
@echo "=> Building image"
|
||||
@echo " Dockerfile: $(D_FILE)"
|
||||
@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) .
|
||||
@echo "=> Building image for Ubuntu"
|
||||
@docker build -t $(IMAGE_REPO):$(VERSION) --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
|
||||
@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) .
|
||||
|
||||
image-push:
|
||||
@echo "=> Publish image"
|
||||
@echo " Tag: $(IMAGE_REPO):$(VERSION)$(IMAGE_SUFFIX)"
|
||||
@docker push $(IMAGE_REPO):$(VERSION)$(IMAGE_SUFFIX)
|
||||
@echo "=> Publish image for Ubuntu"
|
||||
@docker push $(IMAGE_REPO):$(VERSION)
|
||||
|
||||
image-wsc-push:
|
||||
@echo "=> Publish image for Windows Server Core"
|
||||
@docker push $(IMAGE_REPO):$(VERSION)_WindowsServerCore
|
||||
|
||||
image-push-latest:
|
||||
@echo "=> Publish image for Ubuntu with 'latest' tag"
|
||||
@docker push $(IMAGE_REPO):latest
|
||||
|
||||
check-version:
|
||||
git fetch && (! git rev-list ${VERSION})
|
||||
|
||||
deps:
|
||||
@CGO_ENABLED=0 \
|
||||
go mod download
|
||||
@CGO_ENABLED=0 \
|
||||
go mod tidy -v
|
||||
|
||||
version:
|
||||
@echo $(VERSION)
|
||||
|
||||
gh-docker-vars:
|
||||
@echo "file=$(D_FILE)"
|
||||
@echo "version=$(VERSION)"
|
||||
@echo "repo=$(IMAGE_REPO)"
|
||||
@echo "suffix=$(IMAGE_SUFFIX)"
|
||||
push-tag:
|
||||
git checkout ${BRANCH}
|
||||
git pull origin ${BRANCH}
|
||||
git tag ${VERSION}
|
||||
git push origin ${VERSION}
|
||||
|
||||
test:
|
||||
@go test ./... -cover
|
||||
|
@ -117,20 +105,16 @@ cover:
|
|||
@go test -v -race ./... -coverprofile=coverage.txt -covermode=atomic -coverpkg=./pkg/...,./cli/...
|
||||
@go tool cover -html=coverage.txt -o coverage.html
|
||||
|
||||
# --- Ubuntu/Windows environment ---
|
||||
# --- Environment ---
|
||||
env_image:
|
||||
@echo "=> Building env image"
|
||||
@echo " Dockerfile: $(D_FILE)"
|
||||
@echo " Tag: $(ENV_IMAGE_TAG)"
|
||||
@docker build \
|
||||
-f $(D_FILE) \
|
||||
-t $(ENV_IMAGE_TAG) \
|
||||
-t env_neo_go_image \
|
||||
--build-arg REPO=$(REPO) \
|
||||
--build-arg VERSION=$(VERSION) .
|
||||
|
||||
env_up:
|
||||
@echo "=> Bootup environment"
|
||||
@echo " Docker-compose file: $(DC_FILE)"
|
||||
@docker-compose -f $(DC_FILE) up -d node_one node_two node_three node_four
|
||||
|
||||
env_single:
|
||||
|
|
97
README.md
97
README.md
|
@ -1,9 +1,5 @@
|
|||
<p align="center">
|
||||
<picture>
|
||||
<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>
|
||||
<img src="./.github/neo_color_dark_gopher.png" width="300px" alt="logo">
|
||||
</p>
|
||||
<p align="center">
|
||||
<b>Go</b> Node and SDK for the <a href="https://neo.org">Neo</a> blockchain.
|
||||
|
@ -12,7 +8,9 @@
|
|||
<hr />
|
||||
|
||||
[](https://codecov.io/gh/nspcc-dev/neo-go)
|
||||
[](https://github.com/nspcc-dev/neo-go/actions/workflows/tests.yml)
|
||||
[](https://circleci.com/gh/nspcc-dev/neo-go/tree/master)
|
||||
[](https://github.com/nspcc-dev/neo-go/actions/workflows/run_tests.yml)
|
||||
[](https://github.com/nspcc-dev/neo-go/actions/workflows/codeql-analysis.yml)
|
||||
[](https://goreportcard.com/report/github.com/nspcc-dev/neo-go)
|
||||
[](https://godoc.org/github.com/nspcc-dev/neo-go)
|
||||

|
||||
|
@ -20,21 +18,20 @@
|
|||
|
||||
# Overview
|
||||
|
||||
NeoGo is a complete platform for distributed application development built on
|
||||
top of and compatible with the [Neo project](https://github.com/neo-project).
|
||||
This includes, but not limited to (see documentation for more details):
|
||||
This project aims to be a full port of the original C# [Neo project](https://github.com/neo-project).
|
||||
A complete toolkit for the NEO blockchain, including:
|
||||
|
||||
- [Consensus node](docs/consensus.md)
|
||||
- [RPC node & client](docs/rpc.md)
|
||||
- [CLI tool](docs/cli.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)
|
||||
- [Oracle service](docs/oracle.md)
|
||||
- [State validation service](docs/stateroots.md)
|
||||
|
||||
The protocol implemented here is Neo N3-compatible, however you can also find
|
||||
an implementation of the Neo Legacy protocol in the [**master-2.x**
|
||||
This branch (**master**) is Neo N3-compatible. For the current
|
||||
Legacy-compatible version please refer to the [**master-2.x**
|
||||
branch](https://github.com/nspcc-dev/neo-go/tree/master-2.x) and releases
|
||||
before 0.80.0 (**0.7X.Y** track).
|
||||
|
||||
|
@ -51,28 +48,27 @@ NeoGo, `:latest` points to the latest release) or build yourself.
|
|||
|
||||
### 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
|
||||
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.
|
||||
The resulting binary is `bin/neo-go`.
|
||||
|
||||
#### Building on Windows
|
||||
|
||||
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`.
|
||||
|
||||
We also recommend you to switch the Windows Firewall off for further NeoGo node run.
|
||||
|
||||
## Running a node
|
||||
|
||||
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
|
||||
flags.
|
||||
|
||||
To start Neo node on a private network, use:
|
||||
To start Neo node on private network use:
|
||||
|
||||
```
|
||||
./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
|
||||
|
@ -98,15 +94,12 @@ Available network flags:
|
|||
- `--privnet, -p`
|
||||
- `--testnet, -t`
|
||||
|
||||
To run a consensus/committee node, refer to [consensus
|
||||
To run a consensus/committee node refer to [consensus
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
$ 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
|
||||
mode. After it ends, the node can be started normally.
|
||||
mode, after it ends the node can be started normally.
|
||||
|
||||
## Running a private network
|
||||
|
||||
|
@ -134,39 +128,37 @@ Refer to [consensus node documentation](docs/consensus.md).
|
|||
|
||||
## 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
|
||||
simple contracts that can be compiled/deployed/run using NeoGo compiler, SDK
|
||||
and a 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
|
||||
simple contracts that can be compiled/deployed/run using neo-go compiler, SDK
|
||||
and private network. For details on how Go code is translated to Neo VM
|
||||
bytecode and what you can and can not do in smart contract please refer to the
|
||||
[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.
|
||||
|
||||
## 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)
|
||||
file that is used by CLI commands to sign various things. CLI commands are not
|
||||
a direct part of the node, but rather a part of the NeoGo binary, their
|
||||
implementations use RPC to query data from the blockchain and perform any
|
||||
required actions. It's not required to open a wallet on an RPC node (unless
|
||||
your node provides some service for the network like consensus or oracle nodes
|
||||
do).
|
||||
file that is used by CLI commands to sign various things. There is no database
|
||||
behind it, the blockchain is the database and CLI commands use RPC to query
|
||||
data from it. At the same time it's not required to open the wallet on RPC
|
||||
node to perform various actions (unless your node is providing some service
|
||||
for the network like consensus or oracle nodes).
|
||||
|
||||
## Monitoring
|
||||
NeoGo provides [Prometheus](https://prometheus.io/docs/guides/go-application) and
|
||||
[Pprof](https://golang.org/pkg/net/http/pprof/) services that can be enabled
|
||||
in the node in order to provide additional monitoring and debugging data.
|
||||
# Developer notes
|
||||
Nodes have such features as [Prometheus](https://prometheus.io/docs/guides/go-application) and
|
||||
[Pprof](https://golang.org/pkg/net/http/pprof/) in order to have additional information about them for debugging.
|
||||
|
||||
Configuring any of the two services is easy, add the following section (`Pprof`
|
||||
instead of `Prometheus` if you need that) to the respective `config/protocol.*.yml`:
|
||||
How to configure Prometheus or Pprof:
|
||||
In `config/protocol.*.yml` there is
|
||||
```
|
||||
Prometheus:
|
||||
Enabled: true
|
||||
Addresses:
|
||||
- ":2112"
|
||||
Port: 2112
|
||||
```
|
||||
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
|
||||
[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.
|
||||
|
||||
# Contact
|
||||
|
||||
- [@AnnaShaleva](https://github.com/AnnaShaleva) 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
|
||||
|
||||
|
|
66
ROADMAP.md
66
ROADMAP.md
|
@ -1,71 +1,11 @@
|
|||
# Roadmap for neo-go
|
||||
|
||||
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.
|
||||
|
||||
## Versions 0.7X.Y (as needed)
|
||||
* Neo 2.0 support (bug fixes, minor functionality additions)
|
||||
|
||||
## Version 0.107.0 (~Jun-Jul 2024)
|
||||
* protocol updates
|
||||
* 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).
|
||||
## Version 1.0 (2022, TBD)
|
||||
* full NEO N3 support and useful extensions
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
131
cli/candidate_test.go
Normal 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")
|
||||
}
|
|
@ -9,7 +9,7 @@ import (
|
|||
"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/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/wallet"
|
||||
"github.com/urfave/cli"
|
||||
|
@ -24,145 +24,20 @@ const (
|
|||
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
|
||||
// from the specified offset.
|
||||
func GetSignersFromContext(ctx *cli.Context, offset int) ([]transaction.Signer, *cli.ExitError) {
|
||||
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
|
||||
for i, c := range args {
|
||||
if args.Present() && len(args) > offset {
|
||||
for i, c := range args[offset:] {
|
||||
cosigner, err := parseCosigner(c)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse signer #%d: %w", i, err)
|
||||
return nil, cli.NewExitError(fmt.Errorf("failed to parse signer #%d: %w", i, err), 1)
|
||||
}
|
||||
signers = append(signers, cosigner)
|
||||
}
|
||||
}
|
||||
return signers, nil
|
||||
}
|
||||
|
||||
|
@ -230,9 +105,9 @@ func parseCosigner(c string) (transaction.Signer, error) {
|
|||
}
|
||||
|
||||
// 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 (
|
||||
data any
|
||||
data interface{}
|
||||
offset int
|
||||
params []smartcontract.Parameter
|
||||
err error
|
||||
|
@ -256,15 +131,6 @@ func GetDataFromContext(ctx *cli.Context) (int, any, *cli.ExitError) {
|
|||
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
|
||||
// returns the number of handled words, the array itself and an error.
|
||||
// `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
|
||||
// accounts from the provided wallet.
|
||||
func GetSignersAccounts(senderAcc *wallet.Account, wall *wallet.Wallet, signers []transaction.Signer, accScope transaction.WitnessScope) ([]actor.SignerAccount, error) {
|
||||
signersAccounts := make([]actor.SignerAccount, 0, len(signers)+1)
|
||||
sender := senderAcc.ScriptHash()
|
||||
signersAccounts = append(signersAccounts, actor.SignerAccount{
|
||||
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)
|
||||
func GetSignersAccounts(wall *wallet.Wallet, signers []transaction.Signer) ([]client.SignerAccount, error) {
|
||||
signersAccounts := make([]client.SignerAccount, len(signers))
|
||||
for i := range signers {
|
||||
signerAcc := wall.GetAccount(signers[i].Account)
|
||||
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{
|
||||
Signer: s,
|
||||
signersAccounts[i] = client.SignerAccount{
|
||||
Signer: signers[i],
|
||||
Account: signerAcc,
|
||||
})
|
||||
}
|
||||
}
|
||||
return signersAccounts, nil
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package cmdargs
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -44,7 +45,7 @@ func TestParseCosigner(t *testing.T) {
|
|||
Scopes: transaction.CalledByEntry | transaction.CustomContracts,
|
||||
AllowedContracts: []util.Uint160{c1, c2},
|
||||
},
|
||||
acc.StringLE() + ":CustomGroups:" + priv.PublicKey().StringCompressed(): {
|
||||
acc.StringLE() + ":CustomGroups:" + hex.EncodeToString(priv.PublicKey().Bytes()): {
|
||||
Account: acc,
|
||||
Scopes: transaction.CustomGroups,
|
||||
AllowedGroups: keys.PublicKeys{priv.PublicKey()},
|
||||
|
|
File diff suppressed because it is too large
Load diff
106
cli/dump_test.go
Normal file
106
cli/dump_test.go
Normal 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")
|
||||
}
|
|
@ -1,25 +1,16 @@
|
|||
/*
|
||||
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
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/cli/app"
|
||||
"github.com/nspcc-dev/neo-go/cli/input"
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"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/encoding/address"
|
||||
"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/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/urfave/cli"
|
||||
"go.uber.org/zap"
|
||||
|
@ -41,40 +32,31 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
ValidatorWIF = "KxyjQ8eUa4FHt3Gvioyt1Wz29cTUrE4eTqX3yFSk1YFCsPL8uNsY"
|
||||
ValidatorAddr = "NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP"
|
||||
MultisigAddr = "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq"
|
||||
validatorWIF = "KxyjQ8eUa4FHt3Gvioyt1Wz29cTUrE4eTqX3yFSk1YFCsPL8uNsY"
|
||||
validatorAddr = "NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP"
|
||||
multisigAddr = "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq"
|
||||
|
||||
TestWalletPath = "../testdata/testwallet.json"
|
||||
TestWalletAccount = "Nfyz4KcsgYepRJw1W5C2uKCi6QWKf7v6gG"
|
||||
testWalletPath = "testdata/testwallet.json"
|
||||
testWalletAccount = "Nfyz4KcsgYepRJw1W5C2uKCi6QWKf7v6gG"
|
||||
|
||||
TestWalletMultiPath = "../testdata/testwallet_multi.json"
|
||||
TestWalletMultiAccount1 = "NgHcPxgEKZQV4QBedzyASJrgiANhJqBVLw"
|
||||
TestWalletMultiAccount2 = "NLvHRfKAifjio2z9HiwLo9ZnpRPHUbAHgH"
|
||||
TestWalletMultiAccount3 = "NcDfG8foJx79XSihcDDrx1df7cHAoJBfXj"
|
||||
|
||||
ValidatorWallet = "../testdata/wallet1_solo.json"
|
||||
ValidatorPass = "one"
|
||||
validatorWallet = "testdata/wallet1_solo.json"
|
||||
validatorPass = "one"
|
||||
)
|
||||
|
||||
var (
|
||||
ValidatorHash, _ = address.StringToUint160(ValidatorAddr)
|
||||
ValidatorPriv, _ = keys.NewPrivateKeyFromWIF(ValidatorWIF)
|
||||
|
||||
TestWalletMultiAccount1Hash, _ = address.StringToUint160(TestWalletMultiAccount1)
|
||||
TestWalletMultiAccount2Hash, _ = address.StringToUint160(TestWalletMultiAccount2)
|
||||
TestWalletMultiAccount3Hash, _ = address.StringToUint160(TestWalletMultiAccount3)
|
||||
validatorHash, _ = address.StringToUint160(validatorAddr)
|
||||
validatorPriv, _ = keys.NewPrivateKeyFromWIF(validatorWIF)
|
||||
)
|
||||
|
||||
// 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.
|
||||
type Executor struct {
|
||||
type executor struct {
|
||||
// CLI is a cli application to test.
|
||||
CLI *cli.App
|
||||
// Chain is a blockchain instance (can be empty).
|
||||
Chain *core.Blockchain
|
||||
// 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 *network.Server
|
||||
// Out contains command output.
|
||||
|
@ -138,8 +120,8 @@ func (w *ConcurrentBuffer) Reset() {
|
|||
w.buf.Reset()
|
||||
}
|
||||
|
||||
func NewTestChain(t *testing.T, f func(*config.Config), run bool) (*core.Blockchain, *rpcsrv.Server, *network.Server) {
|
||||
configPath := "../../config/protocol.unit_testnet.single.yml"
|
||||
func newTestChain(t *testing.T, f func(*config.Config), run bool) (*core.Blockchain, *server.Server, *network.Server) {
|
||||
configPath := "../config/protocol.unit_testnet.single.yml"
|
||||
cfg, err := config.LoadFile(configPath)
|
||||
require.NoError(t, err, "could not load config")
|
||||
if f != nil {
|
||||
|
@ -148,50 +130,46 @@ func NewTestChain(t *testing.T, f func(*config.Config), run bool) (*core.Blockch
|
|||
|
||||
memoryStore := storage.NewMemoryStore()
|
||||
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")
|
||||
|
||||
if run {
|
||||
go chain.Run()
|
||||
}
|
||||
|
||||
serverConfig, err := network.NewServerConfig(cfg)
|
||||
require.NoError(t, err)
|
||||
serverConfig.UserAgent = fmt.Sprintf(config.UserAgentFormat, "0.98.3-test")
|
||||
serverConfig := network.NewServerConfig(cfg)
|
||||
netSrv, err := network.NewServer(serverConfig, chain, chain.GetStateSyncModule(), zap.NewNop())
|
||||
require.NoError(t, err)
|
||||
cons, err := consensus.NewService(consensus.Config{
|
||||
Logger: zap.NewNop(),
|
||||
Broadcast: netSrv.BroadcastExtensible,
|
||||
Chain: chain,
|
||||
BlockQueue: netSrv.GetBlockQueue(),
|
||||
ProtocolConfiguration: cfg.ProtocolConfiguration,
|
||||
ProtocolConfiguration: chain.GetConfig(),
|
||||
RequestTx: netSrv.RequestTx,
|
||||
StopTxFlow: netSrv.StopTxFlow,
|
||||
Wallet: cfg.ApplicationConfiguration.Consensus.UnlockWallet,
|
||||
Wallet: serverConfig.Wallet,
|
||||
TimePerBlock: serverConfig.TimePerBlock,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
netSrv.AddConsensusService(cons, cons.OnPayload, cons.OnTransaction)
|
||||
netSrv.Start()
|
||||
netSrv.AddExtensibleHPService(cons, consensus.Category, cons.OnPayload, cons.OnTransaction)
|
||||
go netSrv.Start(make(chan error, 1))
|
||||
rpcServer := server.New(chain, cfg.ApplicationConfiguration.RPC, netSrv, nil, logger)
|
||||
errCh := make(chan error, 2)
|
||||
rpcServer := rpcsrv.New(chain, cfg.ApplicationConfiguration.RPC, netSrv, nil, logger, errCh)
|
||||
rpcServer.Start()
|
||||
rpcServer.Start(errCh)
|
||||
|
||||
return chain, &rpcServer, netSrv
|
||||
}
|
||||
|
||||
func NewExecutor(t *testing.T, needChain bool) *Executor {
|
||||
return NewExecutorWithConfig(t, needChain, true, nil)
|
||||
func newExecutor(t *testing.T, needChain bool) *executor {
|
||||
return newExecutorWithConfig(t, needChain, true, nil)
|
||||
}
|
||||
|
||||
func NewExecutorSuspended(t *testing.T) *Executor {
|
||||
return NewExecutorWithConfig(t, true, false, nil)
|
||||
func newExecutorSuspended(t *testing.T) *executor {
|
||||
return newExecutorWithConfig(t, true, false, nil)
|
||||
}
|
||||
|
||||
func NewExecutorWithConfig(t *testing.T, needChain, runChain bool, f func(*config.Config)) *Executor {
|
||||
e := &Executor{
|
||||
CLI: app.New(),
|
||||
func newExecutorWithConfig(t *testing.T, needChain, runChain bool, f func(*config.Config)) *executor {
|
||||
e := &executor{
|
||||
CLI: newApp(),
|
||||
Out: NewConcurrentBuffer(),
|
||||
Err: 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.ErrWriter = e.Err
|
||||
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() {
|
||||
e.Close(t)
|
||||
|
@ -207,10 +185,10 @@ func NewExecutorWithConfig(t *testing.T, needChain, runChain bool, f func(*confi
|
|||
return e
|
||||
}
|
||||
|
||||
func (e *Executor) Close(t *testing.T) {
|
||||
func (e *executor) Close(t *testing.T) {
|
||||
input.Terminal = nil
|
||||
if e.RPC != nil {
|
||||
e.RPC.Shutdown()
|
||||
require.NoError(t, e.RPC.Shutdown())
|
||||
}
|
||||
if e.NetSrv != nil {
|
||||
e.NetSrv.Shutdown()
|
||||
|
@ -223,7 +201,7 @@ func (e *Executor) Close(t *testing.T) {
|
|||
// GetTransaction returns tx with hash h after it has persisted.
|
||||
// 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.
|
||||
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 height uint32
|
||||
require.Eventually(t, func() bool {
|
||||
|
@ -234,22 +212,22 @@ func (e *Executor) GetTransaction(t *testing.T, h util.Uint256) (*transaction.Tr
|
|||
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')
|
||||
require.NoError(t, err)
|
||||
return strings.TrimSuffix(line, "\n")
|
||||
}
|
||||
|
||||
func (e *Executor) CheckNextLine(t *testing.T, expected string) {
|
||||
line := e.GetNextLine(t)
|
||||
e.CheckLine(t, line, expected)
|
||||
func (e *executor) checkNextLine(t *testing.T, expected string) {
|
||||
line := e.getNextLine(t)
|
||||
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)
|
||||
}
|
||||
|
||||
func (e *Executor) CheckEOF(t *testing.T) {
|
||||
func (e *executor) checkEOF(t *testing.T) {
|
||||
_, err := e.Out.ReadString('\n')
|
||||
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.
|
||||
func (e *Executor) RunWithError(t *testing.T, args ...string) {
|
||||
func (e *executor) RunWithError(t *testing.T, args ...string) {
|
||||
ch := setExitFunc()
|
||||
require.Error(t, e.run(args...))
|
||||
checkExit(t, ch, 1)
|
||||
}
|
||||
|
||||
// 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()
|
||||
require.NoError(t, e.run(args...))
|
||||
checkExit(t, ch, 0)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
func (e *executor) run(args ...string) error {
|
||||
e.Out.Reset()
|
||||
e.Err.Reset()
|
||||
input.Terminal = term.NewTerminal(input.ReadWriter{
|
||||
Reader: e.In,
|
||||
Writer: io.Discard,
|
||||
Writer: ioutil.Discard,
|
||||
}, "")
|
||||
err := e.CLI.Run(args)
|
||||
input.Terminal = nil
|
||||
|
@ -313,7 +277,7 @@ func (e *Executor) run(args ...string) error {
|
|||
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')
|
||||
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)
|
||||
require.NoError(t, err)
|
||||
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
|
||||
}
|
||||
|
||||
func (e *Executor) CheckAwaitableTxPersisted(t *testing.T, prefix ...string) (*transaction.Transaction, uint32) {
|
||||
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) {
|
||||
func generateKeys(t *testing.T, n int) ([]*keys.PrivateKey, keys.PublicKeys) {
|
||||
privs := make([]*keys.PrivateKey, n)
|
||||
pubs := make(keys.PublicKeys, n)
|
||||
for i := range privs {
|
||||
|
@ -350,44 +307,3 @@ func GenerateKeys(t *testing.T, n int) ([]*keys.PrivateKey, keys.PublicKeys) {
|
|||
}
|
||||
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
|
||||
}
|
|
@ -10,7 +10,7 @@ import (
|
|||
"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 {
|
||||
IsSet bool
|
||||
Value util.Uint160
|
||||
|
@ -28,12 +28,12 @@ var (
|
|||
_ cli.Flag = AddressFlag{}
|
||||
)
|
||||
|
||||
// String implements the fmt.Stringer interface.
|
||||
// String implements fmt.Stringer interface.
|
||||
func (a Address) String() string {
|
||||
return address.Uint160ToString(a.Value)
|
||||
}
|
||||
|
||||
// Set implements the flag.Value interface.
|
||||
// Set implements flag.Value interface.
|
||||
func (a *Address) Set(s string) error {
|
||||
addr, err := ParseAddress(s)
|
||||
if err != nil {
|
||||
|
@ -44,7 +44,7 @@ func (a *Address) Set(s string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Uint160 casts an address to Uint160.
|
||||
// Uint160 casts address to Uint160.
|
||||
func (a *Address) Uint160() (u util.Uint160) {
|
||||
if !a.IsSet {
|
||||
// It is a programmer error to call this method without
|
||||
|
@ -82,7 +82,7 @@ func (f AddressFlag) GetName() string {
|
|||
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.
|
||||
func (f AddressFlag) Apply(set *flag.FlagSet) {
|
||||
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) {
|
||||
const uint160size = 2 * util.Uint160Size
|
||||
switch len(s) {
|
||||
|
|
|
@ -2,7 +2,7 @@ package flags
|
|||
|
||||
import (
|
||||
"flag"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/random"
|
||||
|
@ -119,7 +119,7 @@ func TestAddressFlag_GetName(t *testing.T) {
|
|||
|
||||
func TestAddress(t *testing.T) {
|
||||
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.Apply(f)
|
||||
require.NoError(t, f.Parse([]string{"--addr", "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR"}))
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"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 {
|
||||
Value fixedn.Fixed8
|
||||
}
|
||||
|
@ -25,12 +25,12 @@ var (
|
|||
_ cli.Flag = Fixed8Flag{}
|
||||
)
|
||||
|
||||
// String implements the fmt.Stringer interface.
|
||||
// String implements fmt.Stringer interface.
|
||||
func (a Fixed8) String() string {
|
||||
return a.Value.String()
|
||||
}
|
||||
|
||||
// Set implements the flag.Value interface.
|
||||
// Set implements flag.Value interface.
|
||||
func (a *Fixed8) Set(s string) error {
|
||||
f, err := fixedn.Fixed8FromString(s)
|
||||
if err != nil {
|
||||
|
@ -40,7 +40,7 @@ func (a *Fixed8) Set(s string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Fixed8 casts the address to util.Fixed8.
|
||||
// Fixed8 casts address to util.Fixed8.
|
||||
func (a *Fixed8) Fixed8() fixedn.Fixed8 {
|
||||
return a.Value
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ func (f Fixed8Flag) GetName() string {
|
|||
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.
|
||||
func (f Fixed8Flag) Apply(set *flag.FlagSet) {
|
||||
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 {
|
||||
return ctx.Generic(name).(*Fixed8).Value
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package flags
|
|||
|
||||
import (
|
||||
"flag"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||
|
@ -55,7 +55,7 @@ func TestFixed8Flag_GetName(t *testing.T) {
|
|||
|
||||
func TestFixed8(t *testing.T) {
|
||||
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.Apply(f)
|
||||
require.NoError(t, f.Parse([]string{"--gas", "0.123"}))
|
||||
|
|
|
@ -21,7 +21,7 @@ type ReadWriter struct {
|
|||
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) {
|
||||
trm := Terminal
|
||||
if trm == nil {
|
||||
|
@ -46,16 +46,21 @@ func readLine(trm *term.Terminal, prompt string) (string, error) {
|
|||
return trm.ReadLine()
|
||||
}
|
||||
|
||||
// ReadPassword reads the user's password with prompt.
|
||||
// ReadPassword reads user password with prompt.
|
||||
func ReadPassword(prompt string) (string, error) {
|
||||
trm := Terminal
|
||||
if trm != nil {
|
||||
if trm == nil {
|
||||
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)
|
||||
}
|
||||
return readSecurePassword(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 {
|
||||
fmt.Fprintf(w, "Network fee: %s\n", fixedn.Fixed8(tx.NetworkFee))
|
||||
fmt.Fprintf(w, "System fee: %s\n", fixedn.Fixed8(tx.SystemFee))
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
27
cli/main.go
27
cli/main.go
|
@ -3,13 +3,36 @@ package main
|
|||
import (
|
||||
"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() {
|
||||
ctl := app.New()
|
||||
ctl := newApp()
|
||||
|
||||
if err := ctl.Run(os.Args); err != nil {
|
||||
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
12
cli/main_test.go
Normal 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)
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package wallet_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
@ -8,15 +9,13 @@ import (
|
|||
"path/filepath"
|
||||
"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/keys"
|
||||
"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/context"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
@ -24,9 +23,9 @@ import (
|
|||
// 1. Transfer funds to a created multisig address.
|
||||
// 2. Transfer from multisig to another account.
|
||||
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)
|
||||
require.NoError(t, err)
|
||||
multisigHash := hash.Hash160(script)
|
||||
|
@ -44,9 +43,9 @@ func TestSignMultisigTx(t *testing.T) {
|
|||
"--wallet", w,
|
||||
"--wif", wif,
|
||||
"--min", "2",
|
||||
pubs[0].StringCompressed(),
|
||||
pubs[1].StringCompressed(),
|
||||
pubs[2].StringCompressed())
|
||||
hex.EncodeToString(pubs[0].Bytes()),
|
||||
hex.EncodeToString(pubs[1].Bytes()),
|
||||
hex.EncodeToString(pubs[2].Bytes()))
|
||||
}
|
||||
addAccount(wallet1Path, privs[0].WIF())
|
||||
addAccount(wallet2Path, privs[1].WIF())
|
||||
|
@ -54,13 +53,13 @@ func TestSignMultisigTx(t *testing.T) {
|
|||
// Transfer funds to the multisig.
|
||||
e.In.WriteString("one\r")
|
||||
e.Run(t, "neo-go", "wallet", "nep17", "multitransfer",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
||||
"--wallet", testcli.ValidatorWallet,
|
||||
"--from", testcli.ValidatorAddr,
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", validatorWallet,
|
||||
"--from", validatorAddr,
|
||||
"--force",
|
||||
"NEO:"+multisigAddr+":4",
|
||||
"GAS:"+multisigAddr+":1")
|
||||
e.CheckTxPersisted(t)
|
||||
e.checkTxPersisted(t)
|
||||
|
||||
// Sign and transfer funds to another account.
|
||||
priv, err := keys.NewPrivateKey()
|
||||
|
@ -73,7 +72,7 @@ func TestSignMultisigTx(t *testing.T) {
|
|||
})
|
||||
e.In.WriteString("pass\r")
|
||||
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,
|
||||
"--to", priv.Address(), "--token", "NEO", "--amount", "1",
|
||||
"--out", txPath)
|
||||
|
@ -98,7 +97,7 @@ func TestSignMultisigTx(t *testing.T) {
|
|||
// invalid out
|
||||
e.In.WriteString("pass\r")
|
||||
e.RunWithError(t, "neo-go", "wallet", "sign",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", wallet2Path, "--address", multisigAddr,
|
||||
"--in", txPath, "--out", t.TempDir())
|
||||
|
||||
|
@ -117,7 +116,7 @@ func TestSignMultisigTx(t *testing.T) {
|
|||
})
|
||||
e.In.WriteString("pass\r")
|
||||
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,
|
||||
"--to", priv.Address(), "--token", "NEO", "--amount", "1",
|
||||
"--out", txPath)
|
||||
|
@ -145,63 +144,30 @@ func TestSignMultisigTx(t *testing.T) {
|
|||
|
||||
t.Run("no invoke", func(t *testing.T) {
|
||||
e.Run(t, "neo-go", "util", "txdump", txPath)
|
||||
e.CheckTxTestInvokeOutput(t, 11)
|
||||
e.CheckEOF(t)
|
||||
e.checkTxTestInvokeOutput(t, 11)
|
||||
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",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
txPath)
|
||||
e.CheckTxTestInvokeOutput(t, 11)
|
||||
e.checkTxTestInvokeOutput(t, 11)
|
||||
res := new(result.Invoke)
|
||||
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) {
|
||||
oldIn, err := os.ReadFile(txPath)
|
||||
require.NoError(t, err)
|
||||
e.In.WriteString("pass\r")
|
||||
e.Run(t, "neo-go", "wallet", "sign",
|
||||
"--wallet", wallet2Path, "--address", multisigAddr,
|
||||
"--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],
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", wallet2Path, "--address", multisigAddr,
|
||||
"--in", txPath, "--out", txPath)
|
||||
e.CheckTxPersisted(t)
|
||||
})
|
||||
e.checkTxPersisted(t)
|
||||
|
||||
t.Run("double-sign", func(t *testing.T) {
|
||||
e.In.WriteString("pass\r")
|
||||
e.RunWithError(t, "neo-go", "wallet", "sign",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", wallet2Path, "--address", multisigAddr,
|
||||
"--in", txPath, "--out", txPath)
|
||||
})
|
||||
|
@ -216,13 +182,13 @@ func TestSignMultisigTx(t *testing.T) {
|
|||
|
||||
e.In.WriteString("acc\rpass\rpass\r")
|
||||
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(),
|
||||
"--contract", h.StringLE())
|
||||
|
||||
e.In.WriteString("pass\r")
|
||||
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
|
||||
"--out", txPath,
|
||||
e.Chain.GoverningTokenHash().StringLE(), "transfer",
|
||||
|
@ -248,10 +214,10 @@ func TestSignMultisigTx(t *testing.T) {
|
|||
// Contract.
|
||||
e.In.WriteString("pass\r")
|
||||
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),
|
||||
"--in", txPath, "--out", txPath)
|
||||
tx, _ := e.CheckTxPersisted(t)
|
||||
tx, _ := e.checkTxPersisted(t)
|
||||
require.Equal(t, 3, len(tx.Signers))
|
||||
|
||||
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 {
|
||||
return testcli.DeployContract(t, e, "../smartcontract/testdata/verify.go", "../smartcontract/testdata/verify.yml", testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass)
|
||||
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+`)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package nep_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -7,14 +7,12 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"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/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
|
@ -28,15 +26,12 @@ import (
|
|||
const (
|
||||
// nftOwnerAddr is the owner of NFT-ND HASHY token (../examples/nft-nd/nft.go).
|
||||
nftOwnerAddr = "NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB"
|
||||
nftOwnerWallet = "../../examples/my_wallet.json"
|
||||
nftOwnerWallet = "../examples/my_wallet.json"
|
||||
nftOwnerPass = "qwerty"
|
||||
|
||||
// Keep contract NEFs consistent between runs.
|
||||
_ = versionutil.TestVersion
|
||||
)
|
||||
|
||||
func TestNEP11Import(t *testing.T) {
|
||||
e := testcli.NewExecutor(t, true)
|
||||
e := newExecutor(t, true)
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
walletPath := filepath.Join(tmpDir, "walletForImport.json")
|
||||
|
@ -51,15 +46,12 @@ func TestNEP11Import(t *testing.T) {
|
|||
|
||||
args := []string{
|
||||
"neo-go", "wallet", "nep11", "import",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||
"--wallet", walletPath,
|
||||
}
|
||||
// missing token hash
|
||||
e.RunWithError(t, args...)
|
||||
|
||||
// excessive parameters
|
||||
e.RunWithError(t, append(args, "--token", nnsContractHash.StringLE(), "something")...)
|
||||
|
||||
// good: non-divisible
|
||||
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())...)
|
||||
|
||||
checkInfo := func(t *testing.T, h util.Uint160, name string, symbol string, decimals int) {
|
||||
e.CheckNextLine(t, "^Name:\\s*"+name)
|
||||
e.CheckNextLine(t, "^Symbol:\\s*"+symbol)
|
||||
e.CheckNextLine(t, "^Hash:\\s*"+h.StringLE())
|
||||
e.CheckNextLine(t, "^Decimals:\\s*"+strconv.Itoa(decimals))
|
||||
e.CheckNextLine(t, "^Address:\\s*"+address.Uint160ToString(h))
|
||||
e.CheckNextLine(t, "^Standard:\\s*"+string(manifest.NEP11StandardName))
|
||||
e.checkNextLine(t, "^Name:\\s*"+name)
|
||||
e.checkNextLine(t, "^Symbol:\\s*"+symbol)
|
||||
e.checkNextLine(t, "^Hash:\\s*"+h.StringLE())
|
||||
e.checkNextLine(t, "^Decimals:\\s*"+strconv.Itoa(decimals))
|
||||
e.checkNextLine(t, "^Address:\\s*"+address.Uint160ToString(h))
|
||||
e.checkNextLine(t, "^Standard:\\s*"+string(manifest.NEP11StandardName))
|
||||
}
|
||||
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) {
|
||||
e.Run(t, "neo-go", "wallet", "nep11", "info",
|
||||
"--wallet", walletPath, "--token", nnsContractHash.StringLE())
|
||||
|
@ -94,14 +82,12 @@ func TestNEP11Import(t *testing.T) {
|
|||
e.Run(t, "neo-go", "wallet", "nep11", "info",
|
||||
"--wallet", walletPath)
|
||||
checkInfo(t, nnsContractHash, "NameService", "NNS", 0)
|
||||
e.CheckNextLine(t, "")
|
||||
e.checkNextLine(t, "")
|
||||
checkInfo(t, nfsContractHash, "NeoFS Object NFT", "NFSO", 2)
|
||||
})
|
||||
})
|
||||
|
||||
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.Run(t, "neo-go", "wallet", "nep11", "remove",
|
||||
"--wallet", walletPath, "--token", nnsContractHash.StringLE())
|
||||
|
@ -114,27 +100,27 @@ func TestNEP11Import(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
|
||||
e := testcli.NewExecutor(t, true)
|
||||
e := newExecutor(t, true)
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
// 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)
|
||||
wall := filepath.Join(tmpDir, "my_wallet.json")
|
||||
err = os.WriteFile(wall, bytesRead, 0755)
|
||||
err = ioutil.WriteFile(wall, bytesRead, 0755)
|
||||
require.NoError(t, err)
|
||||
|
||||
// transfer funds to contract owner
|
||||
e.In.WriteString("one\r")
|
||||
e.Run(t, "neo-go", "wallet", "nep17", "transfer",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
||||
"--wallet", testcli.ValidatorWallet,
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", validatorWallet,
|
||||
"--to", nftOwnerAddr,
|
||||
"--token", "GAS",
|
||||
"--amount", "10000",
|
||||
"--force",
|
||||
"--from", testcli.ValidatorAddr)
|
||||
e.CheckTxPersisted(t)
|
||||
"--from", validatorAddr)
|
||||
e.checkTxPersisted(t)
|
||||
|
||||
// deploy NFT HASHY contract
|
||||
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
|
||||
e.In.WriteString(nftOwnerPass + "\r")
|
||||
e.Run(t, "neo-go", "wallet", "nep17", "transfer",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", wall,
|
||||
"--to", h.StringLE(),
|
||||
"--token", "GAS",
|
||||
"--amount", "10",
|
||||
"--force",
|
||||
"--from", nftOwnerAddr)
|
||||
txMint, _ := e.CheckTxPersisted(t)
|
||||
txMint, _ := e.checkTxPersisted(t)
|
||||
|
||||
// get NFT ID from AER
|
||||
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)
|
||||
var hashBeforeTransfer = e.Chain.CurrentHeaderHash()
|
||||
|
||||
// check the balance
|
||||
cmdCheckBalance := []string{"neo-go", "wallet", "nep11", "balance",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||
"--wallet", wall,
|
||||
"--address", nftOwnerAddr}
|
||||
checkBalanceResult := func(t *testing.T, acc string, ids ...[]byte) {
|
||||
e.CheckNextLine(t, "^\\s*Account\\s+"+acc)
|
||||
e.CheckNextLine(t, "^\\s*HASHY:\\s+HASHY NFT \\("+h.StringLE()+"\\)")
|
||||
|
||||
// Hashes can be ordered in any way, so make a regexp for them.
|
||||
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)
|
||||
checkBalanceResult := func(t *testing.T, acc string, amount string) {
|
||||
e.checkNextLine(t, "^\\s*Account\\s+"+acc)
|
||||
e.checkNextLine(t, "^\\s*HASHY:\\s+HASHY NFT \\("+h.StringLE()+"\\)")
|
||||
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+amount+"$")
|
||||
e.checkEOF(t)
|
||||
}
|
||||
// balance check: by symbol, token is not imported
|
||||
e.Run(t, append(cmdCheckBalance, "--token", "HASHY")...)
|
||||
checkBalanceResult(t, nftOwnerAddr, tokenID)
|
||||
// balance check: excessive parameters
|
||||
e.RunWithError(t, append(cmdCheckBalance, "--token", h.StringLE(), "neo-go")...)
|
||||
e.RunWithError(t, append(cmdCheckBalance, "--token", "HASHY")...)
|
||||
// balance check: by hash, ok
|
||||
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
|
||||
checkBalanceResult(t, nftOwnerAddr, tokenID)
|
||||
checkBalanceResult(t, nftOwnerAddr, "1")
|
||||
|
||||
// import token
|
||||
e.Run(t, "neo-go", "wallet", "nep11", "import",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", wall,
|
||||
"--token", h.StringLE())
|
||||
|
||||
// balance check: by symbol, ok
|
||||
e.Run(t, append(cmdCheckBalance, "--token", "HASHY")...)
|
||||
checkBalanceResult(t, nftOwnerAddr, tokenID)
|
||||
checkBalanceResult(t, nftOwnerAddr, "1")
|
||||
|
||||
// balance check: all accounts
|
||||
e.Run(t, "neo-go", "wallet", "nep11", "balance",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", wall,
|
||||
"--token", h.StringLE())
|
||||
checkBalanceResult(t, nftOwnerAddr, tokenID)
|
||||
checkBalanceResult(t, nftOwnerAddr, "1")
|
||||
|
||||
// remove token from wallet
|
||||
e.In.WriteString("y\r")
|
||||
|
@ -227,7 +194,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
|
|||
|
||||
// ownerOf: missing contract hash
|
||||
cmdOwnerOf := []string{"neo-go", "wallet", "nep11", "ownerOf",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||
}
|
||||
e.RunWithError(t, cmdOwnerOf...)
|
||||
cmdOwnerOf = append(cmdOwnerOf, "--token", h.StringLE())
|
||||
|
@ -238,11 +205,11 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
|
|||
|
||||
// ownerOf: good
|
||||
e.Run(t, cmdOwnerOf...)
|
||||
e.CheckNextLine(t, nftOwnerAddr)
|
||||
e.checkNextLine(t, nftOwnerAddr)
|
||||
|
||||
// tokensOf: missing contract hash
|
||||
cmdTokensOf := []string{"neo-go", "wallet", "nep11", "tokensOf",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||
}
|
||||
e.RunWithError(t, cmdTokensOf...)
|
||||
cmdTokensOf = append(cmdTokensOf, "--token", h.StringLE())
|
||||
|
@ -253,12 +220,12 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
|
|||
|
||||
// tokensOf: good
|
||||
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
|
||||
cmdProperties := []string{
|
||||
"neo-go", "wallet", "nep11", "properties",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||
}
|
||||
e.RunWithError(t, cmdProperties...)
|
||||
cmdProperties = append(cmdProperties, "--token", h.StringLE())
|
||||
|
@ -269,7 +236,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
|
|||
|
||||
// properties: ok
|
||||
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
|
||||
tokenID1 := mint(t)
|
||||
|
@ -279,32 +246,30 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
|
|||
fst, snd = snd, fst
|
||||
}
|
||||
|
||||
require.Equal(t, hex.EncodeToString(fst), e.GetNextLine(t))
|
||||
require.Equal(t, hex.EncodeToString(snd), e.GetNextLine(t))
|
||||
require.Equal(t, hex.EncodeToString(fst), e.getNextLine(t))
|
||||
require.Equal(t, hex.EncodeToString(snd), e.getNextLine(t))
|
||||
|
||||
// tokens: missing contract hash
|
||||
cmdTokens := []string{"neo-go", "wallet", "nep11", "tokens",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||
}
|
||||
e.RunWithError(t, cmdTokens...)
|
||||
cmdTokens = append(cmdTokens, "--token", h.StringLE())
|
||||
|
||||
// tokens: excessive parameters
|
||||
e.RunWithError(t, append(cmdTokens, "additional")...)
|
||||
// tokens: good, several tokens
|
||||
e.Run(t, cmdTokens...)
|
||||
require.Equal(t, hex.EncodeToString(fst), e.GetNextLine(t))
|
||||
require.Equal(t, hex.EncodeToString(snd), e.GetNextLine(t))
|
||||
require.Equal(t, hex.EncodeToString(fst), e.getNextLine(t))
|
||||
require.Equal(t, hex.EncodeToString(snd), e.getNextLine(t))
|
||||
|
||||
// balance check: several tokens, ok
|
||||
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
|
||||
checkBalanceResult(t, nftOwnerAddr, tokenID, tokenID1)
|
||||
checkBalanceResult(t, nftOwnerAddr, "2")
|
||||
|
||||
cmdTransfer := []string{
|
||||
"neo-go", "wallet", "nep11", "transfer",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||
"--wallet", wall,
|
||||
"--to", testcli.ValidatorAddr,
|
||||
"--to", validatorAddr,
|
||||
"--from", nftOwnerAddr,
|
||||
"--force",
|
||||
}
|
||||
|
@ -322,25 +287,17 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
|
|||
// transfer: good
|
||||
e.In.WriteString(nftOwnerPass + "\r")
|
||||
e.Run(t, append(cmdTransfer, "--id", hex.EncodeToString(tokenID))...)
|
||||
e.CheckTxPersisted(t)
|
||||
e.checkTxPersisted(t)
|
||||
|
||||
// check balance after transfer
|
||||
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
|
||||
checkBalanceResult(t, nftOwnerAddr, 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)
|
||||
checkBalanceResult(t, nftOwnerAddr, "1") // tokenID1
|
||||
|
||||
// transfer: good, to NEP-11-Payable contract, with data
|
||||
verifyH := deployVerifyContract(t, e)
|
||||
cmdTransfer = []string{
|
||||
"neo-go", "wallet", "nep11", "transfer",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||
"--wallet", wall,
|
||||
"--to", verifyH.StringLE(),
|
||||
"--from", nftOwnerAddr,
|
||||
|
@ -351,7 +308,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
|
|||
}
|
||||
e.In.WriteString(nftOwnerPass + "\r")
|
||||
e.Run(t, cmdTransfer...)
|
||||
tx, _ := e.CheckTxPersisted(t)
|
||||
tx, _ := e.checkTxPersisted(t)
|
||||
// check OnNEP11Payment event
|
||||
aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
||||
require.NoError(t, err)
|
||||
|
@ -362,7 +319,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
|
|||
ScriptHash: verifyH,
|
||||
Name: "OnNEP11Payment",
|
||||
Item: stackitem.NewArray([]stackitem.Item{
|
||||
stackitem.NewByteArray(nftOwnerHash.BytesBE()),
|
||||
stackitem.NewBuffer(nftOwnerHash.BytesBE()),
|
||||
stackitem.NewBigInteger(big.NewInt(1)),
|
||||
stackitem.NewByteArray(tokenID1),
|
||||
stackitem.NewByteArray([]byte("some_data")),
|
||||
|
@ -371,36 +328,18 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
|
|||
|
||||
// check balance after transfer
|
||||
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
|
||||
checkBalanceResult(t, nftOwnerAddr)
|
||||
|
||||
// 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))
|
||||
checkBalanceResult(t, nftOwnerAddr, "0")
|
||||
}
|
||||
|
||||
func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
|
||||
e := testcli.NewExecutor(t, true)
|
||||
e := newExecutor(t, true)
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
// 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)
|
||||
wall := filepath.Join(tmpDir, "my_wallet.json")
|
||||
err = os.WriteFile(wall, bytesRead, 0755)
|
||||
err = ioutil.WriteFile(wall, bytesRead, 0755)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 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 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",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", wall,
|
||||
"--to", h.StringLE(),
|
||||
"--token", "GAS",
|
||||
"--amount", "10",
|
||||
"--force",
|
||||
"--from", testcli.ValidatorAddr,
|
||||
"--from", validatorAddr,
|
||||
"--", "[", "hash256:"+containerID.StringLE(), "hash256:"+objectID.StringLE(), "]",
|
||||
)
|
||||
txMint, _ := e.CheckTxPersisted(t)
|
||||
txMint, _ := e.checkTxPersisted(t)
|
||||
|
||||
// get NFT ID from AER
|
||||
aer, err := e.Chain.GetAppExecResults(txMint.Hash(), trigger.Application)
|
||||
|
@ -444,66 +383,55 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
|
|||
|
||||
// check 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(),
|
||||
"--id", hex.EncodeToString(token1ID))
|
||||
jProps := e.GetNextLine(t)
|
||||
jProps := e.getNextLine(t)
|
||||
props := make(map[string]string)
|
||||
require.NoError(t, json.Unmarshal([]byte(jProps), &props))
|
||||
require.Equal(t, base64.StdEncoding.EncodeToString(container1ID.BytesBE()), props["containerID"])
|
||||
require.Equal(t, base64.StdEncoding.EncodeToString(object1ID.BytesBE()), props["objectID"])
|
||||
e.CheckEOF(t)
|
||||
|
||||
type idAmount struct {
|
||||
id string
|
||||
amount string
|
||||
}
|
||||
e.checkEOF(t)
|
||||
|
||||
// check the balance
|
||||
cmdCheckBalance := []string{"neo-go", "wallet", "nep11", "balance",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||
"--wallet", wall,
|
||||
"--address", testcli.ValidatorAddr}
|
||||
checkBalanceResult := func(t *testing.T, acc string, objs ...idAmount) {
|
||||
e.CheckNextLine(t, "^\\s*Account\\s+"+acc)
|
||||
e.CheckNextLine(t, "^\\s*NFSO:\\s+NeoFS Object NFT \\("+h.StringLE()+"\\)")
|
||||
|
||||
for _, o := range objs {
|
||||
e.CheckNextLine(t, "^\\s*Token: "+o.id+"\\s*$")
|
||||
e.CheckNextLine(t, "^\\s*Amount: "+o.amount+"\\s*$")
|
||||
e.CheckNextLine(t, "^\\s*Updated: [0-9]+\\s*$")
|
||||
"--address", validatorAddr}
|
||||
checkBalanceResult := func(t *testing.T, acc string, amount string, id []byte) {
|
||||
e.checkNextLine(t, "^\\s*Account\\s+"+acc)
|
||||
if id == nil {
|
||||
e.checkNextLine(t, "^\\s*NFSO:\\s+NeoFS Object NFT \\("+h.StringLE()+"\\)")
|
||||
} else {
|
||||
e.checkNextLine(t, "^\\s*NFSO:\\s+NeoFS Object NFT \\("+h.StringLE()+", "+hex.EncodeToString(id)+"\\)")
|
||||
}
|
||||
e.CheckEOF(t)
|
||||
}
|
||||
tokz := []idAmount{
|
||||
{hex.EncodeToString(token1ID), "1"},
|
||||
{hex.EncodeToString(token2ID), "1"},
|
||||
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+amount+"$")
|
||||
e.checkEOF(t)
|
||||
}
|
||||
// balance check: by symbol, token is not imported
|
||||
e.Run(t, append(cmdCheckBalance, "--token", "NFSO")...)
|
||||
checkBalanceResult(t, testcli.ValidatorAddr, tokz...)
|
||||
e.RunWithError(t, append(cmdCheckBalance, "--token", "NFSO")...)
|
||||
|
||||
// overall NFSO balance check: by hash, ok
|
||||
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
|
||||
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
|
||||
e.Run(t, "neo-go", "wallet", "nep11", "import",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", wall,
|
||||
"--token", h.StringLE())
|
||||
|
||||
// overall balance check: by symbol, ok
|
||||
e.Run(t, append(cmdCheckBalance, "--token", "NFSO")...)
|
||||
checkBalanceResult(t, testcli.ValidatorAddr, tokz...)
|
||||
checkBalanceResult(t, validatorAddr, "2", nil)
|
||||
|
||||
// particular balance check: by symbol, ok
|
||||
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
|
||||
e.In.WriteString("y\r")
|
||||
|
@ -512,7 +440,7 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
|
|||
|
||||
// ownerOfD: missing contract hash
|
||||
cmdOwnerOf := []string{"neo-go", "wallet", "nep11", "ownerOfD",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||
}
|
||||
e.RunWithError(t, cmdOwnerOf...)
|
||||
cmdOwnerOf = append(cmdOwnerOf, "--token", h.StringLE())
|
||||
|
@ -523,29 +451,29 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
|
|||
|
||||
// ownerOfD: good
|
||||
e.Run(t, cmdOwnerOf...)
|
||||
e.CheckNextLine(t, testcli.ValidatorAddr)
|
||||
e.checkNextLine(t, validatorAddr)
|
||||
|
||||
// tokensOf: missing contract hash
|
||||
cmdTokensOf := []string{"neo-go", "wallet", "nep11", "tokensOf",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||
}
|
||||
e.RunWithError(t, cmdTokensOf...)
|
||||
cmdTokensOf = append(cmdTokensOf, "--token", h.StringLE())
|
||||
|
||||
// tokensOf: missing owner address
|
||||
e.RunWithError(t, cmdTokensOf...)
|
||||
cmdTokensOf = append(cmdTokensOf, "--address", testcli.ValidatorAddr)
|
||||
cmdTokensOf = append(cmdTokensOf, "--address", validatorAddr)
|
||||
|
||||
// tokensOf: good
|
||||
e.Run(t, cmdTokensOf...)
|
||||
require.Equal(t, hex.EncodeToString(token1ID), e.GetNextLine(t))
|
||||
require.Equal(t, hex.EncodeToString(token2ID), e.GetNextLine(t))
|
||||
e.CheckEOF(t)
|
||||
require.Equal(t, hex.EncodeToString(token1ID), e.getNextLine(t))
|
||||
require.Equal(t, hex.EncodeToString(token2ID), e.getNextLine(t))
|
||||
e.checkEOF(t)
|
||||
|
||||
// properties: no contract
|
||||
cmdProperties := []string{
|
||||
"neo-go", "wallet", "nep11", "properties",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||
}
|
||||
e.RunWithError(t, cmdProperties...)
|
||||
cmdProperties = append(cmdProperties, "--token", h.StringLE())
|
||||
|
@ -554,17 +482,14 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
|
|||
e.RunWithError(t, cmdProperties...)
|
||||
cmdProperties = append(cmdProperties, "--id", hex.EncodeToString(token2ID))
|
||||
|
||||
// properties: additional parameter
|
||||
e.RunWithError(t, append(cmdProperties, "additiona")...)
|
||||
|
||||
// properties: ok
|
||||
e.Run(t, cmdProperties...)
|
||||
jProps = e.GetNextLine(t)
|
||||
jProps = e.getNextLine(t)
|
||||
props = make(map[string]string)
|
||||
require.NoError(t, json.Unmarshal([]byte(jProps), &props))
|
||||
require.Equal(t, base64.StdEncoding.EncodeToString(container2ID.BytesBE()), props["containerID"])
|
||||
require.Equal(t, base64.StdEncoding.EncodeToString(object2ID.BytesBE()), props["objectID"])
|
||||
e.CheckEOF(t)
|
||||
e.checkEOF(t)
|
||||
|
||||
// tokensOf: good, several tokens
|
||||
e.Run(t, cmdTokensOf...)
|
||||
|
@ -573,75 +498,75 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
|
|||
fst, snd = snd, fst
|
||||
}
|
||||
|
||||
require.Equal(t, hex.EncodeToString(fst), e.GetNextLine(t))
|
||||
require.Equal(t, hex.EncodeToString(snd), e.GetNextLine(t))
|
||||
require.Equal(t, hex.EncodeToString(fst), e.getNextLine(t))
|
||||
require.Equal(t, hex.EncodeToString(snd), e.getNextLine(t))
|
||||
|
||||
// tokens: missing contract hash
|
||||
cmdTokens := []string{"neo-go", "wallet", "nep11", "tokens",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||
}
|
||||
e.RunWithError(t, cmdTokens...)
|
||||
cmdTokens = append(cmdTokens, "--token", h.StringLE())
|
||||
|
||||
// tokens: good, several tokens
|
||||
e.Run(t, cmdTokens...)
|
||||
require.Equal(t, hex.EncodeToString(fst), e.GetNextLine(t))
|
||||
require.Equal(t, hex.EncodeToString(snd), e.GetNextLine(t))
|
||||
require.Equal(t, hex.EncodeToString(fst), e.getNextLine(t))
|
||||
require.Equal(t, hex.EncodeToString(snd), e.getNextLine(t))
|
||||
|
||||
// balance check: several tokens, ok
|
||||
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
|
||||
checkBalanceResult(t, testcli.ValidatorAddr, tokz...)
|
||||
checkBalanceResult(t, validatorAddr, "2", nil)
|
||||
|
||||
cmdTransfer := []string{
|
||||
"neo-go", "wallet", "nep11", "transfer",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||
"--wallet", wall,
|
||||
"--to", nftOwnerAddr,
|
||||
"--from", testcli.ValidatorAddr,
|
||||
"--from", validatorAddr,
|
||||
"--force",
|
||||
}
|
||||
|
||||
// transfer: unimported token with symbol id specified
|
||||
e.In.WriteString(testcli.ValidatorPass + "\r")
|
||||
e.In.WriteString(validatorPass + "\r")
|
||||
e.RunWithError(t, append(cmdTransfer,
|
||||
"--token", "NFSO")...)
|
||||
cmdTransfer = append(cmdTransfer, "--token", h.StringLE())
|
||||
|
||||
// transfer: no id specified
|
||||
e.In.WriteString(testcli.ValidatorPass + "\r")
|
||||
e.In.WriteString(validatorPass + "\r")
|
||||
e.RunWithError(t, cmdTransfer...)
|
||||
|
||||
// transfer: good
|
||||
e.In.WriteString(testcli.ValidatorPass + "\r")
|
||||
e.In.WriteString(validatorPass + "\r")
|
||||
e.Run(t, append(cmdTransfer, "--id", hex.EncodeToString(token1ID))...)
|
||||
e.CheckTxPersisted(t)
|
||||
e.checkTxPersisted(t)
|
||||
|
||||
// check balance after transfer
|
||||
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
|
||||
verifyH := deployVerifyContract(t, e)
|
||||
cmdTransfer = []string{
|
||||
"neo-go", "wallet", "nep11", "transfer",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||
"--wallet", wall,
|
||||
"--to", verifyH.StringLE(),
|
||||
"--from", testcli.ValidatorAddr,
|
||||
"--from", validatorAddr,
|
||||
"--token", h.StringLE(),
|
||||
"--id", hex.EncodeToString(token2ID),
|
||||
"--amount", "0.25",
|
||||
"--force",
|
||||
"string:some_data",
|
||||
}
|
||||
e.In.WriteString(testcli.ValidatorPass + "\r")
|
||||
e.In.WriteString(validatorPass + "\r")
|
||||
e.Run(t, cmdTransfer...)
|
||||
tx, _ := e.CheckTxPersisted(t)
|
||||
tx, _ := e.checkTxPersisted(t)
|
||||
// check OnNEP11Payment event
|
||||
aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(aer[0].Events))
|
||||
validatorHash, err := address.StringToUint160(testcli.ValidatorAddr)
|
||||
validatorHash, err := address.StringToUint160(validatorAddr)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, state.NotificationEvent{
|
||||
ScriptHash: verifyH,
|
||||
|
@ -656,22 +581,17 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
|
|||
|
||||
// check balance after transfer
|
||||
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
|
||||
tokz[1].amount = "0.75"
|
||||
checkBalanceResult(t, testcli.ValidatorAddr, tokz[1])
|
||||
checkBalanceResult(t, validatorAddr, "0.75", nil)
|
||||
}
|
||||
|
||||
func deployNFSContract(t *testing.T, e *testcli.Executor) util.Uint160 {
|
||||
return testcli.DeployContract(t, e, "../../examples/nft-d/nft.go", "../../examples/nft-d/nft.yml", testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass)
|
||||
func deployNFSContract(t *testing.T, e *executor) util.Uint160 {
|
||||
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 {
|
||||
return testcli.DeployContract(t, e, "../../examples/nft-nd/nft.go", "../../examples/nft-nd/nft.yml", nftOwnerWallet, nftOwnerAddr, nftOwnerPass)
|
||||
func deployNFTContract(t *testing.T, e *executor) util.Uint160 {
|
||||
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 {
|
||||
return testcli.DeployContract(t, e, "../../examples/nft-nd-nns/", "../../examples/nft-nd-nns/nns.yml", testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.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)
|
||||
func deployNNSContract(t *testing.T, e *executor) util.Uint160 {
|
||||
return deployContract(t, e, "../examples/nft-nd-nns/", "../examples/nft-nd-nns/nns.yml", validatorWallet, validatorAddr, validatorPass)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package nep_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
@ -8,7 +8,6 @@ import (
|
|||
"strings"
|
||||
"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/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||
|
@ -18,39 +17,21 @@ import (
|
|||
)
|
||||
|
||||
func TestNEP17Balance(t *testing.T) {
|
||||
e := testcli.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)
|
||||
|
||||
e := newExecutor(t, true)
|
||||
cmdbalance := []string{"neo-go", "wallet", "nep17", "balance"}
|
||||
cmdbase := append(cmdbalance,
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
||||
"--wallet", testcli.TestWalletMultiPath,
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", validatorWallet,
|
||||
)
|
||||
cmd := append(cmdbase, "--address", testcli.TestWalletMultiAccount1)
|
||||
t.Run("excessive parameters", func(t *testing.T) {
|
||||
e.RunWithError(t, append(cmd, "--token", "NEO", "gas")...)
|
||||
})
|
||||
cmd := append(cmdbase, "--address", validatorAddr)
|
||||
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) {
|
||||
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1)
|
||||
e.CheckNextLine(t, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
|
||||
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+b.String()+"$")
|
||||
e.CheckNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
|
||||
e.CheckEOF(t)
|
||||
e.checkNextLine(t, "^\\s*Account\\s+"+validatorAddr)
|
||||
e.checkNextLine(t, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
|
||||
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+b.String()+"$")
|
||||
e.checkNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
|
||||
e.checkEOF(t)
|
||||
}
|
||||
t.Run("Alias", func(t *testing.T) {
|
||||
e.Run(t, append(cmd, "--token", "NEO")...)
|
||||
|
@ -63,55 +44,66 @@ func TestNEP17Balance(t *testing.T) {
|
|||
})
|
||||
t.Run("GAS", func(t *testing.T) {
|
||||
e.Run(t, append(cmd, "--token", "GAS")...)
|
||||
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1)
|
||||
e.CheckNextLine(t, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
|
||||
b := e.Chain.GetUtilityTokenBalance(testcli.TestWalletMultiAccount1Hash)
|
||||
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(b.Int64()).String()+"$")
|
||||
e.checkNextLine(t, "^\\s*Account\\s+"+validatorAddr)
|
||||
e.checkNextLine(t, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
|
||||
b := e.Chain.GetUtilityTokenBalance(validatorHash)
|
||||
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(b.Int64()).String()+"$")
|
||||
})
|
||||
t.Run("zero balance of known token", func(t *testing.T) {
|
||||
e.Run(t, append(cmdbase, []string{"--token", "NEO", "--address", testcli.TestWalletMultiAccount2}...)...)
|
||||
e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount2)
|
||||
e.CheckNextLine(t, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
|
||||
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(0).String()+"$")
|
||||
e.CheckNextLine(t, "^\\s*Updated:")
|
||||
e.CheckEOF(t)
|
||||
e.Run(t, append(cmdbase, []string{"--token", "NEO"}...)...)
|
||||
addr1, err := address.StringToUint160("Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn")
|
||||
require.NoError(t, err)
|
||||
e.checkNextLine(t, "^Account "+address.Uint160ToString(addr1))
|
||||
e.checkNextLine(t, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
|
||||
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) {
|
||||
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.
|
||||
for i := 0; i < 2; i++ {
|
||||
line := e.GetNextLine(t)
|
||||
line := e.getNextLine(t)
|
||||
if strings.Contains(line, "GAS") {
|
||||
e.CheckLine(t, line, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
|
||||
balance := e.Chain.GetUtilityTokenBalance(testcli.TestWalletMultiAccount1Hash)
|
||||
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(balance.Int64()).String()+"$")
|
||||
e.CheckNextLine(t, "^\\s*Updated:")
|
||||
e.checkLine(t, line, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
|
||||
balance = e.Chain.GetUtilityTokenBalance(addr3)
|
||||
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(balance.Int64()).String()+"$")
|
||||
e.checkNextLine(t, "^\\s*Updated:")
|
||||
} else {
|
||||
balance, index := e.Chain.GetGoverningTokenBalance(testcli.TestWalletMultiAccount1Hash)
|
||||
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*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
|
||||
balance, index := e.Chain.GetGoverningTokenBalance(validatorHash)
|
||||
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*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, "^Account "+testcli.TestWalletMultiAccount3)
|
||||
e.CheckNextLine(t, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
|
||||
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)
|
||||
e.checkNextLine(t, "^\\s*$")
|
||||
addr4, err := address.StringToUint160("NQ3nAdFQXzemHC9uvr4af2Ysap6aZJpqgN") // deployed verify.go contract
|
||||
require.NoError(t, err)
|
||||
e.checkNextLine(t, "^Account "+address.Uint160ToString(addr4))
|
||||
e.checkEOF(t)
|
||||
})
|
||||
t.Run("Bad token", func(t *testing.T) {
|
||||
e.Run(t, append(cmd, "--token", "kek")...)
|
||||
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1)
|
||||
e.CheckNextLine(t, `^\s*Can't find data for "kek" token\s*`)
|
||||
e.CheckEOF(t)
|
||||
e.checkNextLine(t, "^\\s*Account\\s+"+validatorAddr)
|
||||
e.checkEOF(t)
|
||||
})
|
||||
t.Run("Bad wallet", func(t *testing.T) {
|
||||
e.RunWithError(t, append(cmdbalance, "--wallet", "/dev/null")...)
|
||||
|
@ -119,18 +111,19 @@ func TestNEP17Balance(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)
|
||||
defer w.Close()
|
||||
|
||||
e := testcli.NewExecutor(t, true)
|
||||
e := newExecutor(t, true)
|
||||
args := []string{
|
||||
"neo-go", "wallet", "nep17", "transfer",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
||||
"--wallet", testcli.ValidatorWallet,
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||
"--wallet", validatorWallet,
|
||||
"--to", w.Accounts[0].Address,
|
||||
"--token", "NEO",
|
||||
"--amount", "1",
|
||||
"--from", testcli.ValidatorAddr,
|
||||
"--from", validatorAddr,
|
||||
}
|
||||
|
||||
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("Y\r")
|
||||
e.Run(t, args...)
|
||||
e.CheckNextLine(t, `^Network fee:\s*(\d|\.)+`)
|
||||
e.CheckNextLine(t, `^System fee:\s*(\d|\.)+`)
|
||||
e.CheckNextLine(t, `^Total fee:\s*(\d|\.)+`)
|
||||
e.CheckTxPersisted(t)
|
||||
e.checkNextLine(t, `^Network fee:\s*(\d|\.)+`)
|
||||
e.checkNextLine(t, `^System fee:\s*(\d|\.)+`)
|
||||
e.checkNextLine(t, `^Total fee:\s*(\d|\.)+`)
|
||||
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)
|
||||
require.Equal(t, big.NewInt(1), b)
|
||||
|
||||
t.Run("with force", func(t *testing.T) {
|
||||
e.In.WriteString("one\r")
|
||||
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)
|
||||
require.Equal(t, big.NewInt(2), b)
|
||||
})
|
||||
|
@ -185,20 +181,22 @@ func TestNEP17Transfer(t *testing.T) {
|
|||
t.Run("default address", func(t *testing.T) {
|
||||
e.In.WriteString("one\r")
|
||||
e.Run(t, "neo-go", "wallet", "nep17", "multitransfer",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
||||
"--wallet", testcli.ValidatorWallet,
|
||||
"--from", testcli.ValidatorAddr,
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", validatorWallet,
|
||||
"--from", validatorAddr,
|
||||
"--force",
|
||||
"NEO:"+validatorDefault+":42",
|
||||
"GAS:"+validatorDefault+":7")
|
||||
e.CheckTxPersisted(t)
|
||||
e.checkTxPersisted(t)
|
||||
|
||||
args := args[:len(args)-2] // cut '--from' argument
|
||||
args = append(args, "--force")
|
||||
e.In.WriteString("one\r")
|
||||
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)
|
||||
require.Equal(t, big.NewInt(3), b)
|
||||
|
||||
|
@ -211,70 +209,63 @@ func TestNEP17Transfer(t *testing.T) {
|
|||
t.Run("with signers", func(t *testing.T) {
|
||||
e.In.WriteString("one\r")
|
||||
e.Run(t, "neo-go", "wallet", "nep17", "multitransfer",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
||||
"--wallet", testcli.ValidatorWallet,
|
||||
"--from", testcli.ValidatorAddr,
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", validatorWallet,
|
||||
"--from", validatorAddr,
|
||||
"--force",
|
||||
"NEO:"+validatorDefault+":42",
|
||||
"GAS:"+validatorDefault+":7",
|
||||
"--", testcli.ValidatorAddr+":Global")
|
||||
e.CheckTxPersisted(t)
|
||||
"--", validatorAddr+":Global")
|
||||
e.checkTxPersisted(t)
|
||||
})
|
||||
|
||||
validTil := e.Chain.BlockHeight() + 100
|
||||
cmd := []string{
|
||||
"neo-go", "wallet", "nep17", "transfer",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
||||
"--wallet", testcli.ValidatorWallet,
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||
"--wallet", validatorWallet,
|
||||
"--to", address.Uint160ToString(e.Chain.GetNotaryContractScriptHash()),
|
||||
"--token", "GAS",
|
||||
"--amount", "1",
|
||||
"--from", validatorAddr,
|
||||
"--force",
|
||||
"--from", testcli.ValidatorAddr}
|
||||
|
||||
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)), "]")
|
||||
"[", validatorAddr, strconv.Itoa(int(validTil)), "]"}
|
||||
|
||||
t.Run("with data", func(t *testing.T) {
|
||||
e.In.WriteString("one\r")
|
||||
e.Run(t, cmd...)
|
||||
e.CheckTxPersisted(t)
|
||||
e.checkTxPersisted(t)
|
||||
})
|
||||
|
||||
t.Run("with data and signers", func(t *testing.T) {
|
||||
t.Run("invalid sender's scope", func(t *testing.T) {
|
||||
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) {
|
||||
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.CheckTxPersisted(t)
|
||||
e.Run(t, append(cmd, "--", validatorAddr+":Global")...) // CalledByEntry is enough, but it's the default value, so check something else
|
||||
e.checkTxPersisted(t)
|
||||
})
|
||||
t.Run("several signers", func(t *testing.T) {
|
||||
e.In.WriteString("one\r")
|
||||
e.Run(t, append(cmd, "--", testcli.ValidatorAddr, hVerify.StringLE())...)
|
||||
e.CheckTxPersisted(t)
|
||||
e.Run(t, append(cmd, "--", validatorAddr, hVerify.StringLE())...)
|
||||
e.checkTxPersisted(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)
|
||||
require.NoError(t, err)
|
||||
args := []string{
|
||||
"neo-go", "wallet", "nep17", "multitransfer",
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
||||
"--wallet", testcli.ValidatorWallet,
|
||||
"--from", testcli.ValidatorAddr,
|
||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||
"--wallet", validatorWallet,
|
||||
"--from", validatorAddr,
|
||||
"--force",
|
||||
"NEO:" + privs[0].Address() + ":42",
|
||||
"GAS:" + privs[1].Address() + ":7",
|
||||
|
@ -285,7 +276,7 @@ func TestNEP17MultiTransfer(t *testing.T) {
|
|||
t.Run("no cosigners", func(t *testing.T) {
|
||||
e.In.WriteString("one\r")
|
||||
e.Run(t, args...)
|
||||
e.CheckTxPersisted(t)
|
||||
e.checkTxPersisted(t)
|
||||
|
||||
b, _ := e.Chain.GetGoverningTokenBalance(privs[0].GetScriptHash())
|
||||
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) {
|
||||
e.In.WriteString("one\r")
|
||||
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) {
|
||||
e.In.WriteString("one\r")
|
||||
e.Run(t, append(args,
|
||||
"--", testcli.ValidatorAddr+":Global")...)
|
||||
e.CheckTxPersisted(t)
|
||||
"--", validatorAddr+":Global")...)
|
||||
e.checkTxPersisted(t)
|
||||
})
|
||||
t.Run("Several cosigners", func(t *testing.T) {
|
||||
e.In.WriteString("one\r")
|
||||
e.Run(t, append(args,
|
||||
"--", testcli.ValidatorAddr, hVerify.StringLE())...)
|
||||
e.CheckTxPersisted(t)
|
||||
"--", validatorAddr, hVerify.StringLE())...)
|
||||
e.checkTxPersisted(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNEP17ImportToken(t *testing.T) {
|
||||
e := testcli.NewExecutor(t, true)
|
||||
e := newExecutor(t, true)
|
||||
tmpDir := t.TempDir()
|
||||
walletPath := filepath.Join(tmpDir, "walletForImport.json")
|
||||
|
||||
|
@ -328,42 +319,33 @@ func TestNEP17ImportToken(t *testing.T) {
|
|||
|
||||
// missing token hash
|
||||
e.RunWithError(t, "neo-go", "wallet", "nep17", "import",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--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",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", walletPath,
|
||||
"--token", gasContractHash.StringLE())
|
||||
e.Run(t, "neo-go", "wallet", "nep17", "import",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", walletPath,
|
||||
"--token", address.Uint160ToString(neoContractHash)) // try address instead of sh
|
||||
|
||||
// not a NEP-17 token
|
||||
e.RunWithError(t, "neo-go", "wallet", "nep17", "import",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", walletPath,
|
||||
"--token", nnsContractHash.StringLE())
|
||||
|
||||
t.Run("Info", func(t *testing.T) {
|
||||
checkGASInfo := func(t *testing.T) {
|
||||
e.CheckNextLine(t, "^Name:\\s*GasToken")
|
||||
e.CheckNextLine(t, "^Symbol:\\s*GAS")
|
||||
e.CheckNextLine(t, "^Hash:\\s*"+gasContractHash.StringLE())
|
||||
e.CheckNextLine(t, "^Decimals:\\s*8")
|
||||
e.CheckNextLine(t, "^Address:\\s*"+address.Uint160ToString(gasContractHash))
|
||||
e.CheckNextLine(t, "^Standard:\\s*"+string(manifest.NEP17StandardName))
|
||||
e.checkNextLine(t, "^Name:\\s*GasToken")
|
||||
e.checkNextLine(t, "^Symbol:\\s*GAS")
|
||||
e.checkNextLine(t, "^Hash:\\s*"+gasContractHash.StringLE())
|
||||
e.checkNextLine(t, "^Decimals:\\s*8")
|
||||
e.checkNextLine(t, "^Address:\\s*"+address.Uint160ToString(gasContractHash))
|
||||
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) {
|
||||
e.Run(t, "neo-go", "wallet", "nep17", "info",
|
||||
"--wallet", walletPath, "--token", gasContractHash.StringLE())
|
||||
|
@ -375,16 +357,14 @@ func TestNEP17ImportToken(t *testing.T) {
|
|||
checkGASInfo(t)
|
||||
_, err := e.Out.ReadString('\n')
|
||||
require.NoError(t, err)
|
||||
e.CheckNextLine(t, "^Name:\\s*NeoToken")
|
||||
e.CheckNextLine(t, "^Symbol:\\s*NEO")
|
||||
e.CheckNextLine(t, "^Hash:\\s*"+neoContractHash.StringLE())
|
||||
e.CheckNextLine(t, "^Decimals:\\s*0")
|
||||
e.CheckNextLine(t, "^Address:\\s*"+address.Uint160ToString(neoContractHash))
|
||||
e.CheckNextLine(t, "^Standard:\\s*"+string(manifest.NEP17StandardName))
|
||||
e.checkNextLine(t, "^Name:\\s*NeoToken")
|
||||
e.checkNextLine(t, "^Symbol:\\s*NEO")
|
||||
e.checkNextLine(t, "^Hash:\\s*"+neoContractHash.StringLE())
|
||||
e.checkNextLine(t, "^Decimals:\\s*0")
|
||||
e.checkNextLine(t, "^Address:\\s*"+address.Uint160ToString(neoContractHash))
|
||||
e.checkNextLine(t, "^Standard:\\s*"+string(manifest.NEP17StandardName))
|
||||
})
|
||||
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.Run(t, "neo-go", "wallet", "nep17", "remove",
|
||||
"--wallet", walletPath, "--token", neoContractHash.StringLE())
|
|
@ -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
|
||||
}
|
|
@ -6,61 +6,26 @@ package options
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"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/core/transaction"
|
||||
"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/nspcc-dev/neo-go/pkg/rpc/client"
|
||||
"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 = 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
|
||||
)
|
||||
const DefaultTimeout = 10 * 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.
|
||||
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
|
||||
// (privnet/mainnet/testnet).
|
||||
var Network = []cli.Flag{
|
||||
cli.BoolFlag{Name: "privnet, p", Usage: "use private network configuration (if --config-file option is not specified)"},
|
||||
cli.BoolFlag{Name: "mainnet, m", Usage: "use mainnet network configuration (if --config-file option is not specified)"},
|
||||
cli.BoolFlag{Name: "testnet, t", Usage: "use testnet network configuration (if --config-file option is not specified)"},
|
||||
cli.BoolFlag{Name: "privnet, p"},
|
||||
cli.BoolFlag{Name: "mainnet, m"},
|
||||
cli.BoolFlag{Name: "testnet, t"},
|
||||
cli.BoolFlag{Name: "unittest", Hidden: true},
|
||||
}
|
||||
|
||||
|
@ -72,47 +37,11 @@ var RPC = []cli.Flag{
|
|||
},
|
||||
cli.DurationFlag{
|
||||
Name: "timeout, s",
|
||||
Value: DefaultTimeout,
|
||||
Usage: "Timeout for the operation",
|
||||
Usage: "Timeout for the operation (10 seconds by default)",
|
||||
},
|
||||
}
|
||||
|
||||
// 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 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
|
||||
// defaults to PrivNet if no flags are given.
|
||||
|
@ -130,25 +59,22 @@ func GetNetwork(ctx *cli.Context) netmode.Magic {
|
|||
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()) {
|
||||
dur := ctx.Duration("timeout")
|
||||
if dur == 0 {
|
||||
dur = DefaultTimeout
|
||||
}
|
||||
if !ctx.IsSet("timeout") && ctx.Bool("await") {
|
||||
dur = DefaultAwaitableTimeout
|
||||
}
|
||||
return context.WithTimeout(context.Background(), dur)
|
||||
}
|
||||
|
||||
// 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)
|
||||
if len(endpoint) == 0 {
|
||||
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 {
|
||||
return nil, cli.NewExitError(err, 1)
|
||||
}
|
||||
|
@ -158,253 +84,3 @@ func GetRPCClient(gctx context.Context, ctx *cli.Context) (*rpcclient.Client, cl
|
|||
}
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
package options_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/cli/app"
|
||||
"github.com/nspcc-dev/neo-go/cli/options"
|
||||
"github.com/nspcc-dev/neo-go/internal/testcli"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func TestGetRPCClient(t *testing.T) {
|
||||
e := testcli.NewExecutor(t, true)
|
||||
e := newExecutor(t, true)
|
||||
|
||||
t.Run("no endpoint", func(t *testing.T) {
|
||||
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
|
||||
ctx := cli.NewContext(app.New(), set, nil)
|
||||
ctx := cli.NewContext(cli.NewApp(), set, nil)
|
||||
gctx, _ := options.GetTimeoutContext(ctx)
|
||||
_, ec := options.GetRPCClient(gctx, ctx)
|
||||
require.Equal(t, 1, ec.ExitCode())
|
||||
|
@ -24,8 +22,8 @@ func TestGetRPCClient(t *testing.T) {
|
|||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
|
||||
set.String(options.RPCEndpointFlag, "http://"+e.RPC.Addresses()[0], "")
|
||||
ctx := cli.NewContext(app.New(), set, nil)
|
||||
set.String(options.RPCEndpointFlag, "http://"+e.RPC.Addr, "")
|
||||
ctx := cli.NewContext(cli.NewApp(), set, nil)
|
||||
gctx, _ := options.GetTimeoutContext(ctx)
|
||||
_, ec := options.GetRPCClient(gctx, ctx)
|
||||
require.Nil(t, ec)
|
|
@ -3,31 +3,40 @@ package paramcontext
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"io/ioutil"
|
||||
|
||||
"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/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/context"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
)
|
||||
|
||||
// InitAndSave creates an incompletely signed transaction which can be used
|
||||
// as an input to `multisig sign`. If a wallet.Account is given and can sign,
|
||||
// it's signed as well using it.
|
||||
// validUntilBlockIncrement is the number of extra blocks to add to an exported transaction.
|
||||
const validUntilBlockIncrement = 50
|
||||
|
||||
// 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 {
|
||||
scCtx := context.NewParameterContext(context.TransactionType, net, tx)
|
||||
if acc != nil && acc.CanSign() {
|
||||
sign := acc.SignHashable(net, tx)
|
||||
if err := scCtx.AddSignature(acc.ScriptHash(), acc.Contract, acc.PublicKey(), sign); err != nil {
|
||||
return fmt.Errorf("can't add signature: %w", err)
|
||||
// avoid fast transaction expiration
|
||||
tx.ValidUntilBlock += validUntilBlockIncrement
|
||||
priv := acc.PrivateKey()
|
||||
pub := priv.PublicKey()
|
||||
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)
|
||||
}
|
||||
|
||||
// Read reads the parameter context from the file.
|
||||
// Read reads parameter context from file.
|
||||
func Read(filename string) (*context.ParameterContext, error) {
|
||||
data, err := os.ReadFile(filename)
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
// Save writes the parameter context to the file.
|
||||
// Save writes parameter context to file.
|
||||
func Save(c *context.ParameterContext, filename string) error {
|
||||
if data, err := json.Marshal(c); err != nil {
|
||||
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 nil
|
||||
|
|
|
@ -3,25 +3,25 @@ package query
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"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/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/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/neorpc/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neo"
|
||||
"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/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/vm/stackitem"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
|
@ -40,35 +40,30 @@ func NewCommands() []cli.Command {
|
|||
{
|
||||
Name: "candidates",
|
||||
Usage: "Get candidates and votes",
|
||||
UsageText: "neo-go query candidates -r endpoint [-s timeout]",
|
||||
Action: queryCandidates,
|
||||
Flags: options.RPC,
|
||||
},
|
||||
{
|
||||
Name: "committee",
|
||||
Usage: "Get committee list",
|
||||
UsageText: "neo-go query committee -r endpoint [-s timeout]",
|
||||
Action: queryCommittee,
|
||||
Flags: options.RPC,
|
||||
},
|
||||
{
|
||||
Name: "height",
|
||||
Usage: "Get node height",
|
||||
UsageText: "neo-go query height -r endpoint [-s timeout]",
|
||||
Action: queryHeight,
|
||||
Flags: options.RPC,
|
||||
},
|
||||
{
|
||||
Name: "tx",
|
||||
Usage: "Query transaction status",
|
||||
UsageText: "neo-go query tx <hash> -r endpoint [-s timeout] [-v]",
|
||||
Action: queryTx,
|
||||
Flags: queryTxFlags,
|
||||
},
|
||||
{
|
||||
Name: "voter",
|
||||
Usage: "Print NEO holder account state",
|
||||
UsageText: "neo-go query voter <address> -r endpoint [-s timeout]",
|
||||
Action: queryVoter,
|
||||
Flags: options.RPC,
|
||||
},
|
||||
|
@ -80,8 +75,6 @@ func queryTx(ctx *cli.Context) error {
|
|||
args := ctx.Args()
|
||||
if len(args) == 0 {
|
||||
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"))
|
||||
|
@ -110,10 +103,7 @@ func queryTx(ctx *cli.Context) error {
|
|||
}
|
||||
}
|
||||
|
||||
err = DumpApplicationLog(ctx, res, &txOut.Transaction, &txOut.TransactionMetadata, ctx.Bool("verbose"))
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
DumpApplicationLog(ctx, res, &txOut.Transaction, &txOut.TransactionMetadata, ctx.Bool("verbose"))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -122,58 +112,52 @@ func DumpApplicationLog(
|
|||
res *result.ApplicationLog,
|
||||
tx *transaction.Transaction,
|
||||
txMeta *result.TransactionMetadata,
|
||||
verbose bool) error {
|
||||
var buf []byte
|
||||
verbose bool) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
buf = fmt.Appendf(buf, "Hash:\t%s\n", tx.Hash().StringLE())
|
||||
buf = fmt.Appendf(buf, "OnChain:\t%t\n", res != nil)
|
||||
// Ignore the errors below because `Write` to buffer doesn't return error.
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
buf = fmt.Appendf(buf, "Success:\tunknown (no execution data)\n")
|
||||
_, _ = tw.Write([]byte("Success:\tunknown (no execution data)\n"))
|
||||
} 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 {
|
||||
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())
|
||||
buf = fmt.Appendf(buf, "NetworkFee:\t%s GAS\n", fixedn.Fixed8(tx.NetworkFee).String())
|
||||
buf = fmt.Appendf(buf, "Script:\t%s\n", base64.StdEncoding.EncodeToString(tx.Script))
|
||||
_, _ = tw.Write([]byte("SystemFee:\t" + fixedn.Fixed8(tx.SystemFee).String() + " GAS\n"))
|
||||
_, _ = tw.Write([]byte("NetworkFee:\t" + fixedn.Fixed8(tx.NetworkFee).String() + " GAS\n"))
|
||||
_, _ = tw.Write([]byte("Script:\t" + base64.StdEncoding.EncodeToString(tx.Script) + "\n"))
|
||||
v := vm.New()
|
||||
v.Load(tx.Script)
|
||||
opts := bytes.NewBuffer(nil)
|
||||
v.PrintOps(opts)
|
||||
buf = append(buf, opts.Bytes()...)
|
||||
v.PrintOps(tw)
|
||||
if res != nil {
|
||||
for _, e := range res.Executions {
|
||||
if e.VMState != vmstate.Halt {
|
||||
buf = fmt.Appendf(buf, "Exception:\t%s\n", e.FaultException)
|
||||
if e.VMState != vm.HaltState {
|
||||
_, _ = tw.Write([]byte("Exception:\t" + e.FaultException + "\n"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tw := tabwriter.NewWriter(ctx.App.Writer, 0, 4, 4, '\t', 0)
|
||||
_, err := tw.Write(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tw.Flush()
|
||||
_ = tw.Flush()
|
||||
fmt.Fprint(ctx.App.Writer, buf.String())
|
||||
}
|
||||
|
||||
func queryCandidates(ctx *cli.Context) error {
|
||||
var err error
|
||||
|
||||
if err := cmdargs.EnsureNone(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gctx, cancel := options.GetTimeoutContext(ctx)
|
||||
defer cancel()
|
||||
|
||||
|
@ -182,7 +166,7 @@ func queryCandidates(ctx *cli.Context) error {
|
|||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
vals, err := c.GetCandidates()
|
||||
vals, err := c.GetNextBlockValidators()
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
@ -200,26 +184,20 @@ func queryCandidates(ctx *cli.Context) error {
|
|||
}
|
||||
return vals[i].PublicKey.Cmp(&vals[j].PublicKey) == -1
|
||||
})
|
||||
var res []byte
|
||||
res = fmt.Appendf(res, "Key\tVotes\tCommittee\tConsensus\n")
|
||||
buf := bytes.NewBuffer(nil)
|
||||
tw := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0)
|
||||
_, _ = tw.Write([]byte("Key\tVotes\tCommittee\tConsensus\n"))
|
||||
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)
|
||||
_, err = tw.Write(res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tw.Flush()
|
||||
_ = tw.Flush()
|
||||
fmt.Fprint(ctx.App.Writer, buf.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
func queryCommittee(ctx *cli.Context) error {
|
||||
var err error
|
||||
|
||||
if err := cmdargs.EnsureNone(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gctx, cancel := options.GetTimeoutContext(ctx)
|
||||
defer cancel()
|
||||
|
||||
|
@ -234,7 +212,7 @@ func queryCommittee(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
for _, k := range comm {
|
||||
fmt.Fprintln(ctx.App.Writer, k.StringCompressed())
|
||||
fmt.Fprintln(ctx.App.Writer, hex.EncodeToString(k.Bytes()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -242,10 +220,6 @@ func queryCommittee(ctx *cli.Context) error {
|
|||
func queryHeight(ctx *cli.Context) error {
|
||||
var err error
|
||||
|
||||
if err := cmdargs.EnsureNone(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gctx, cancel := options.GetTimeoutContext(ctx)
|
||||
defer cancel()
|
||||
|
||||
|
@ -274,8 +248,6 @@ func queryVoter(ctx *cli.Context) error {
|
|||
args := ctx.Args()
|
||||
if len(args) == 0 {
|
||||
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])
|
||||
|
@ -290,22 +262,39 @@ func queryVoter(ctx *cli.Context) error {
|
|||
return exitErr
|
||||
}
|
||||
|
||||
neoToken := neo.NewReader(invoker.New(c, nil))
|
||||
|
||||
st, err := neoToken.GetAccountState(addr)
|
||||
neoHash, err := c.GetNativeContractHash(nativenames.Neo)
|
||||
if err != nil {
|
||||
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 {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
if st == nil {
|
||||
st = new(state.NEOBalance)
|
||||
if res.State != "HALT" {
|
||||
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 {
|
||||
return cli.NewExitError(fmt.Errorf("failed to get decimals: %w", err), 1)
|
||||
}
|
||||
voted := "null"
|
||||
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, "\tAmount : %s\n", fixedn.ToString(&st.Balance, int(dec)))
|
||||
|
|
|
@ -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
149
cli/query_test.go
Normal 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)
|
||||
}
|
|
@ -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")...)
|
||||
}
|
|
@ -3,11 +3,11 @@ package server
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage/dboper"
|
||||
)
|
||||
|
||||
type dump []blockDump
|
||||
|
@ -15,7 +15,7 @@ type dump []blockDump
|
|||
type blockDump struct {
|
||||
Block uint32 `json:"block"`
|
||||
Size int `json:"size"`
|
||||
Storage []dboper.Operation `json:"storage"`
|
||||
Storage []storage.Operation `json:"storage"`
|
||||
}
|
||||
|
||||
func newDump() *dump {
|
||||
|
@ -63,7 +63,7 @@ func (d *dump) tryPersist(prefix string, index uint32) error {
|
|||
}
|
||||
|
||||
func readFile(path string) (*dump, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -4,15 +4,14 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/cli/cmdargs"
|
||||
"github.com/nspcc-dev/neo-go/cli/options"
|
||||
"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/core"
|
||||
"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/io"
|
||||
"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/oracle"
|
||||
"github.com/nspcc-dev/neo-go/pkg/services/rpcsrv"
|
||||
"github.com/nspcc-dev/neo-go/pkg/services/stateroot"
|
||||
"github.com/urfave/cli"
|
||||
"go.uber.org/zap"
|
||||
"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.
|
||||
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...)
|
||||
var cfgWithCountFlags = make([]cli.Flag, len(cfgFlags))
|
||||
copy(cfgWithCountFlags, cfgFlags)
|
||||
cfgFlags = append(cfgFlags, options.Debug)
|
||||
|
||||
cfgWithCountFlags = append(cfgWithCountFlags,
|
||||
cli.UintFlag{
|
||||
Name: "count, c",
|
||||
|
@ -74,18 +81,10 @@ func NewCommands() []cli.Command {
|
|||
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{
|
||||
{
|
||||
Name: "node",
|
||||
Usage: "start a NeoGo node",
|
||||
UsageText: "neo-go node [--config-path path] [-d] [-p/-m/-t] [--config-file file]",
|
||||
Usage: "start a NEO node",
|
||||
Action: startServer,
|
||||
Flags: cfgFlags,
|
||||
},
|
||||
|
@ -96,24 +95,15 @@ func NewCommands() []cli.Command {
|
|||
{
|
||||
Name: "dump",
|
||||
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,
|
||||
Flags: cfgCountOutFlags,
|
||||
},
|
||||
{
|
||||
Name: "restore",
|
||||
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,
|
||||
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())
|
||||
stop := make(chan os.Signal, 1)
|
||||
signal.Notify(stop, os.Interrupt)
|
||||
signal.Notify(stop, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-stop
|
||||
cancel()
|
||||
|
@ -131,36 +120,113 @@ func newGraceContext() context.Context {
|
|||
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) {
|
||||
chain, _, err := initBlockChain(cfg, log)
|
||||
chain, err := initBlockChain(cfg, log)
|
||||
if err != nil {
|
||||
return nil, nil, nil, cli.NewExitError(err, 1)
|
||||
}
|
||||
configureAddresses(&cfg.ApplicationConfiguration)
|
||||
prometheus := metrics.NewPrometheusService(cfg.ApplicationConfiguration.Prometheus, log)
|
||||
pprof := metrics.NewPprofService(cfg.ApplicationConfiguration.Pprof, log)
|
||||
|
||||
go chain.Run()
|
||||
err = prometheus.Start()
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
go prometheus.Start()
|
||||
go pprof.Start()
|
||||
|
||||
return chain, prometheus, pprof, nil
|
||||
}
|
||||
|
||||
func dumpDB(ctx *cli.Context) error {
|
||||
if err := cmdargs.EnsureNone(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
cfg, err := options.GetConfigFromContext(ctx)
|
||||
cfg, err := getConfigFromContext(ctx)
|
||||
if err != nil {
|
||||
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 {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
@ -197,9 +263,6 @@ func dumpDB(ctx *cli.Context) error {
|
|||
if count == 0 {
|
||||
count = chainCount - start
|
||||
}
|
||||
if start != 0 {
|
||||
writer.WriteU32LE(start)
|
||||
}
|
||||
writer.WriteU32LE(count)
|
||||
err = chaindump.Dump(chain, writer, start, count)
|
||||
if err != nil {
|
||||
|
@ -209,14 +272,11 @@ func dumpDB(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
func restoreDB(ctx *cli.Context) error {
|
||||
if err := cmdargs.EnsureNone(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
cfg, err := options.GetConfigFromContext(ctx)
|
||||
cfg, err := getConfigFromContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log, _, logCloser, err := options.HandleLoggingParams(ctx.Bool("debug"), cfg.ApplicationConfiguration)
|
||||
log, logCloser, err := handleLoggingParams(ctx, cfg.ApplicationConfiguration)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
@ -237,7 +297,7 @@ func restoreDB(ctx *cli.Context) error {
|
|||
|
||||
dumpDir := ctx.String("dump")
|
||||
if dumpDir != "" {
|
||||
cfg.ApplicationConfiguration.SaveStorageBatch = true
|
||||
cfg.ProtocolConfiguration.SaveStorageBatch = true
|
||||
}
|
||||
|
||||
chain, prometheus, pprof, err := initBCWithMetrics(cfg, log)
|
||||
|
@ -325,54 +385,14 @@ func restoreDB(ctx *cli.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func resetDB(ctx *cli.Context) error {
|
||||
if err := cmdargs.EnsureNone(ctx); err != nil {
|
||||
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 {
|
||||
func mkOracle(config network.ServerConfig, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (*oracle.Oracle, error) {
|
||||
if !config.OracleCfg.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
orcCfg := oracle.Config{
|
||||
Log: log,
|
||||
Network: magic,
|
||||
MainCfg: config,
|
||||
Network: config.Net,
|
||||
MainCfg: config.OracleCfg,
|
||||
Chain: chain,
|
||||
OnTransaction: serv.RelayTxn,
|
||||
}
|
||||
|
@ -385,44 +405,41 @@ func mkOracle(config config.OracleConfiguration, magic netmode.Magic, chain *cor
|
|||
return orc, nil
|
||||
}
|
||||
|
||||
func mkConsensus(config config.Consensus, tpb time.Duration, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (consensus.Service, error) {
|
||||
if !config.Enabled {
|
||||
func mkConsensus(config network.ServerConfig, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (consensus.Service, error) {
|
||||
if config.Wallet == nil {
|
||||
return nil, nil
|
||||
}
|
||||
srv, err := consensus.NewService(consensus.Config{
|
||||
Logger: log,
|
||||
Broadcast: serv.BroadcastExtensible,
|
||||
Chain: chain,
|
||||
BlockQueue: serv.GetBlockQueue(),
|
||||
ProtocolConfiguration: chain.GetConfig().ProtocolConfiguration,
|
||||
ProtocolConfiguration: chain.GetConfig(),
|
||||
RequestTx: serv.RequestTx,
|
||||
StopTxFlow: serv.StopTxFlow,
|
||||
Wallet: config.UnlockWallet,
|
||||
TimePerBlock: tpb,
|
||||
Wallet: config.Wallet,
|
||||
TimePerBlock: config.TimePerBlock,
|
||||
})
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
func mkP2PNotary(config config.P2PNotary, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (*notary.Notary, error) {
|
||||
if !config.Enabled {
|
||||
func mkP2PNotary(config network.ServerConfig, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (*notary.Notary, error) {
|
||||
if !config.P2PNotaryCfg.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
if !chain.P2PSigExtensionsEnabled() {
|
||||
return nil, errors.New("P2PSigExtensions are disabled, but Notary service is enabled")
|
||||
}
|
||||
cfg := notary.Config{
|
||||
MainCfg: config,
|
||||
MainCfg: config.P2PNotaryCfg,
|
||||
Chain: chain,
|
||||
Log: log,
|
||||
}
|
||||
n, err := notary.NewNotary(cfg, serv.Net, serv.GetNotaryPool(), func(tx *transaction.Transaction) error {
|
||||
err := serv.RelayTxn(tx)
|
||||
if err != nil && !errors.Is(err, core.ErrAlreadyExists) && !errors.Is(err, core.ErrAlreadyInPool) {
|
||||
if err := serv.RelayTxn(tx); err != nil {
|
||||
return fmt.Errorf("can't relay completed notary transaction: hash %s, error: %w", tx.Hash().StringLE(), err)
|
||||
}
|
||||
return nil
|
||||
|
@ -436,16 +453,11 @@ func mkP2PNotary(config config.P2PNotary, chain *core.Blockchain, serv *network.
|
|||
}
|
||||
|
||||
func startServer(ctx *cli.Context) error {
|
||||
if err := cmdargs.EnsureNone(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg, err := options.GetConfigFromContext(ctx)
|
||||
cfg, err := getConfigFromContext(ctx)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
var logDebug = ctx.Bool("debug")
|
||||
log, logLevel, logCloser, err := options.HandleLoggingParams(logDebug, cfg.ApplicationConfiguration)
|
||||
log, logCloser, err := handleLoggingParams(ctx, cfg.ApplicationConfiguration)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
@ -456,10 +468,7 @@ func startServer(ctx *cli.Context) error {
|
|||
grace, cancel := context.WithCancel(newGraceContext())
|
||||
defer cancel()
|
||||
|
||||
serverConfig, err := network.NewServerConfig(cfg)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
serverConfig := network.NewServerConfig(cfg)
|
||||
|
||||
chain, prometheus, pprof, err := initBCWithMetrics(cfg, log)
|
||||
if err != nil {
|
||||
|
@ -482,34 +491,26 @@ func startServer(ctx *cli.Context) error {
|
|||
}
|
||||
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 {
|
||||
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 {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
p2pNotary, err := mkP2PNotary(cfg.ApplicationConfiguration.P2PNotary, chain, serv, log)
|
||||
_, err = mkP2PNotary(serverConfig, chain, serv, log)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
rpcServer := server.New(chain, cfg.ApplicationConfiguration.RPC, serv, oracleSrv, log)
|
||||
errChan := make(chan error)
|
||||
rpcServer := rpcsrv.New(chain, cfg.ApplicationConfiguration.RPC, serv, oracleSrv, log, errChan)
|
||||
serv.AddService(&rpcServer)
|
||||
|
||||
serv.Start()
|
||||
if !cfg.ApplicationConfiguration.RPC.StartWhenSynchronized {
|
||||
// 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()
|
||||
}
|
||||
go serv.Start(errChan)
|
||||
rpcServer.Start(errChan)
|
||||
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, sighup)
|
||||
signal.Notify(sigCh, sigusr1)
|
||||
signal.Notify(sigCh, sigusr2)
|
||||
sighupCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sighupCh, syscall.SIGHUP)
|
||||
|
||||
fmt.Fprintln(ctx.App.Writer, Logo())
|
||||
fmt.Fprintln(ctx.App.Writer, serv.UserAgent)
|
||||
|
@ -522,119 +523,24 @@ Main:
|
|||
case err := <-errChan:
|
||||
shutdownErr = fmt.Errorf("server error: %w", err)
|
||||
cancel()
|
||||
case sig := <-sigCh:
|
||||
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.
|
||||
}
|
||||
}
|
||||
case sig := <-sighupCh:
|
||||
switch sig {
|
||||
case sighup:
|
||||
if newLogLevel != zapcore.InvalidLevel {
|
||||
logLevel.SetLevel(newLogLevel)
|
||||
log.Warn("using new logging level", zap.Stringer("level", newLogLevel))
|
||||
case syscall.SIGHUP:
|
||||
log.Info("SIGHUP received, restarting rpc-server")
|
||||
serverErr := rpcServer.Shutdown()
|
||||
if serverErr != nil {
|
||||
errChan <- fmt.Errorf("error while restarting rpc-server: %w", serverErr)
|
||||
break
|
||||
}
|
||||
serv.DelService(&rpcServer)
|
||||
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()
|
||||
rpcServer = server.New(chain, cfg.ApplicationConfiguration.RPC, serv, oracleSrv, log)
|
||||
rpcServer.Start(errChan)
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
cfg = cfgnew
|
||||
case <-grace.Done():
|
||||
signal.Stop(sigCh)
|
||||
signal.Stop(sighupCh)
|
||||
serv.Shutdown()
|
||||
if serverErr := rpcServer.Shutdown(); serverErr != nil {
|
||||
shutdownErr = fmt.Errorf("error on shutdown: %w", serverErr)
|
||||
}
|
||||
break Main
|
||||
}
|
||||
}
|
||||
|
@ -646,29 +552,39 @@ Main:
|
|||
return nil
|
||||
}
|
||||
|
||||
// configureAddresses sets up addresses for RPC, Prometheus and Pprof depending from the provided config.
|
||||
// In case RPC or Prometheus or Pprof Address provided each of them will use it.
|
||||
// In case global Address (of the node) provided and RPC/Prometheus/Pprof don't have configured addresses they will
|
||||
// use global one. So Node and RPC and Prometheus and Pprof will run on one address.
|
||||
func configureAddresses(cfg *config.ApplicationConfiguration) {
|
||||
if cfg.Address != "" {
|
||||
if cfg.RPC.Address == "" {
|
||||
cfg.RPC.Address = cfg.Address
|
||||
}
|
||||
if cfg.Prometheus.Address == "" {
|
||||
cfg.Prometheus.Address = cfg.Address
|
||||
}
|
||||
if cfg.Pprof.Address == "" {
|
||||
cfg.Pprof.Address = cfg.Address
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// initBlockChain initializes BlockChain with preselected DB.
|
||||
func initBlockChain(cfg config.Config, log *zap.Logger) (*core.Blockchain, storage.Store, error) {
|
||||
func initBlockChain(cfg config.Config, log *zap.Logger) (*core.Blockchain, error) {
|
||||
store, err := storage.NewStore(cfg.ApplicationConfiguration.DBConfiguration)
|
||||
if err != nil {
|
||||
return nil, nil, cli.NewExitError(fmt.Errorf("could not initialize storage: %w", err), 1)
|
||||
return nil, cli.NewExitError(fmt.Errorf("could not initialize storage: %w", err), 1)
|
||||
}
|
||||
|
||||
chain, err := core.NewBlockchain(store, cfg.Blockchain(), log)
|
||||
chain, err := core.NewBlockchain(store, cfg.ProtocolConfiguration, log)
|
||||
if err != nil {
|
||||
errText := "could not initialize blockchain: %w"
|
||||
errArgs := []any{err}
|
||||
closeErr := store.Close()
|
||||
if closeErr != nil {
|
||||
errText += "; failed to close the DB: %w"
|
||||
errArgs = append(errArgs, closeErr)
|
||||
return nil, cli.NewExitError(fmt.Errorf("could not initialize blockchain: %w", err), 1)
|
||||
}
|
||||
return chain, nil
|
||||
}
|
||||
|
||||
return nil, nil, cli.NewExitError(fmt.Errorf(errText, errArgs...), 1)
|
||||
}
|
||||
return chain, store, nil
|
||||
}
|
||||
|
||||
// Logo returns NeoGo logo.
|
||||
// Logo returns Neo-Go logo.
|
||||
func Logo() string {
|
||||
return `
|
||||
_ ____________ __________
|
||||
|
|
|
@ -3,20 +3,20 @@ package server
|
|||
import (
|
||||
"encoding/binary"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"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/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/urfave/cli"
|
||||
"gopkg.in/yaml.v3"
|
||||
"go.uber.org/zap"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// serverTestWD is the default working directory for server tests.
|
||||
|
@ -31,58 +31,13 @@ func init() {
|
|||
}
|
||||
|
||||
func TestGetConfigFromContext(t *testing.T) {
|
||||
t.Run("config-path", func(t *testing.T) {
|
||||
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
|
||||
set.String("config-path", "../../config", "")
|
||||
set.Bool("testnet", true, "")
|
||||
ctx := cli.NewContext(cli.NewApp(), set, nil)
|
||||
cfg, err := options.GetConfigFromContext(ctx)
|
||||
cfg, err := getConfigFromContext(ctx)
|
||||
require.NoError(t, err)
|
||||
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) {
|
||||
|
@ -91,75 +46,50 @@ func TestHandleLoggingParams(t *testing.T) {
|
|||
|
||||
t.Run("logdir is a file", func(t *testing.T) {
|
||||
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{
|
||||
LogPath: filepath.Join(logfile, "file.log"),
|
||||
}
|
||||
_, lvl, closer, err := options.HandleLoggingParams(false, cfg)
|
||||
_, closer, err := handleLoggingParams(ctx, cfg)
|
||||
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)
|
||||
})
|
||||
|
||||
t.Run("default", func(t *testing.T) {
|
||||
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
|
||||
ctx := cli.NewContext(cli.NewApp(), set, nil)
|
||||
cfg := config.ApplicationConfiguration{
|
||||
LogPath: testLog,
|
||||
}
|
||||
logger, lvl, closer, err := options.HandleLoggingParams(false, cfg)
|
||||
require.NotNil(t, lvl)
|
||||
logger, closer, err := handleLoggingParams(ctx, cfg)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
if closer != nil {
|
||||
require.NoError(t, closer())
|
||||
}
|
||||
})
|
||||
require.Equal(t, zapcore.InfoLevel, lvl.Level())
|
||||
require.True(t, logger.Core().Enabled(zapcore.InfoLevel))
|
||||
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))
|
||||
require.True(t, logger.Core().Enabled(zap.InfoLevel))
|
||||
require.False(t, logger.Core().Enabled(zap.DebugLevel))
|
||||
})
|
||||
|
||||
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{
|
||||
LogPath: testLog,
|
||||
}
|
||||
logger, lvl, closer, err := options.HandleLoggingParams(true, cfg)
|
||||
logger, closer, err := handleLoggingParams(ctx, cfg)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
if closer != nil {
|
||||
require.NoError(t, closer())
|
||||
}
|
||||
})
|
||||
require.Equal(t, zapcore.DebugLevel, lvl.Level())
|
||||
require.True(t, logger.Core().Enabled(zapcore.InfoLevel))
|
||||
require.True(t, logger.Core().Enabled(zapcore.DebugLevel))
|
||||
require.True(t, logger.Core().Enabled(zap.InfoLevel))
|
||||
require.True(t, logger.Core().Enabled(zap.DebugLevel))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -174,9 +104,9 @@ func TestInitBCWithMetrics(t *testing.T) {
|
|||
set.Bool("testnet", true, "")
|
||||
set.Bool("debug", true, "")
|
||||
ctx := cli.NewContext(cli.NewApp(), set, nil)
|
||||
cfg, err := options.GetConfigFromContext(ctx)
|
||||
cfg, err := getConfigFromContext(ctx)
|
||||
require.NoError(t, err)
|
||||
logger, _, closer, err := options.HandleLoggingParams(true, cfg.ApplicationConfiguration)
|
||||
logger, closer, err := handleLoggingParams(ctx, cfg.ApplicationConfiguration)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
if closer != nil {
|
||||
|
@ -266,7 +196,7 @@ func TestRestoreDB(t *testing.T) {
|
|||
t.Run("invalid logger path", func(t *testing.T) {
|
||||
badCfgDir := t.TempDir()
|
||||
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"))
|
||||
require.NoError(t, err, "could not load config")
|
||||
cfg.ApplicationConfiguration.LogPath = filepath.Join(logfile, "file.log")
|
||||
|
@ -274,7 +204,7 @@ func TestRestoreDB(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
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
|
||||
require.Error(t, restoreDB(ctx))
|
||||
|
@ -290,7 +220,7 @@ func TestRestoreDB(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
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
|
||||
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) {
|
||||
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))
|
||||
*in = inPath
|
||||
require.Error(t, restoreDB(ctx))
|
||||
|
@ -317,10 +247,10 @@ func TestRestoreDB(t *testing.T) {
|
|||
})
|
||||
t.Run("corrupted in: corrupted block", func(t *testing.T) {
|
||||
inPath := filepath.Join(t.TempDir(), "file3.acc")
|
||||
b, err := os.ReadFile(testDump)
|
||||
b, err := ioutil.ReadFile(testDump)
|
||||
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
|
||||
require.NoError(t, os.WriteFile(inPath, b, os.ModePerm))
|
||||
require.NoError(t, ioutil.WriteFile(inPath, b, os.ModePerm))
|
||||
*in = inPath
|
||||
require.Error(t, restoreDB(ctx))
|
||||
|
||||
|
@ -328,12 +258,12 @@ func TestRestoreDB(t *testing.T) {
|
|||
})
|
||||
t.Run("incremental dump", func(t *testing.T) {
|
||||
inPath := filepath.Join(t.TempDir(), "file1_incremental.acc")
|
||||
b, err := os.ReadFile(testDump)
|
||||
b, err := ioutil.ReadFile(testDump)
|
||||
require.NoError(t, err)
|
||||
start := make([]byte, 4)
|
||||
t.Run("good", func(t *testing.T) {
|
||||
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))
|
||||
*in = inPath
|
||||
*incremental = true
|
||||
|
@ -342,7 +272,7 @@ func TestRestoreDB(t *testing.T) {
|
|||
})
|
||||
t.Run("dump is too high", func(t *testing.T) {
|
||||
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))
|
||||
*in = inPath
|
||||
*incremental = true
|
||||
|
@ -358,35 +288,74 @@ func TestRestoreDB(t *testing.T) {
|
|||
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) {
|
||||
t.Run("bad storage", func(t *testing.T) {
|
||||
_, _, err := initBlockChain(config.Config{}, nil)
|
||||
_, err := initBlockChain(config.Config{}, nil)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("empty logger", func(t *testing.T) {
|
||||
_, _, err := initBlockChain(config.Config{
|
||||
_, err := initBlockChain(config.Config{
|
||||
ApplicationConfiguration: config.ApplicationConfiguration{
|
||||
DBConfiguration: dbconfig.DBConfiguration{
|
||||
Type: dbconfig.InMemoryDB,
|
||||
DBConfiguration: storage.DBConfiguration{
|
||||
Type: "inmemory",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
//go:build !windows
|
||||
|
||||
package server
|
||||
|
||||
import "syscall"
|
||||
|
||||
const (
|
||||
sighup = syscall.SIGHUP
|
||||
sigusr1 = syscall.SIGUSR1
|
||||
sigusr2 = syscall.SIGUSR2
|
||||
)
|
|
@ -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)
|
||||
)
|
96
cli/server/testdata/protocol.testnet.windows.yml
vendored
96
cli/server/testdata/protocol.testnet.windows.yml
vendored
|
@ -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"
|
|
@ -1,8 +1,8 @@
|
|||
package server_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
@ -11,29 +11,27 @@ import (
|
|||
"time"
|
||||
|
||||
"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/core/storage/dbconfig"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v3"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func TestServerStart(t *testing.T) {
|
||||
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")
|
||||
ptr := &goodCfg
|
||||
saveCfg := func(t *testing.T, f func(cfg *config.Config)) string {
|
||||
cfg := *ptr
|
||||
chainPath := filepath.Join(t.TempDir(), "neogotestchain")
|
||||
cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB
|
||||
cfg.ApplicationConfiguration.DBConfiguration.Type = "leveldb"
|
||||
cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath = chainPath
|
||||
f(&cfg)
|
||||
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))
|
||||
require.NoError(t, ioutil.WriteFile(cfgPath, out, os.ModePerm))
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, os.Remove(cfgPath))
|
||||
})
|
||||
|
@ -41,7 +39,7 @@ func TestServerStart(t *testing.T) {
|
|||
}
|
||||
|
||||
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) {
|
||||
e.RunWithError(t, baseCmd...)
|
||||
|
@ -49,7 +47,7 @@ func TestServerStart(t *testing.T) {
|
|||
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))
|
||||
require.NoError(t, ioutil.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm))
|
||||
saveCfg(t, func(cfg *config.Config) {
|
||||
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) {
|
||||
saveCfg(t, func(cfg *config.Config) {
|
||||
cfg.ApplicationConfiguration.Consensus.Enabled = true
|
||||
cfg.ApplicationConfiguration.Consensus.UnlockWallet.Path = "bad_consensus_wallet.json"
|
||||
cfg.ApplicationConfiguration.UnlockWallet.Path = "bad_consensus_wallet.json"
|
||||
})
|
||||
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.
|
||||
// Also, windows doesn't support SIGHUP and SIGINT.
|
||||
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) {
|
||||
saveCfg(t, func(cfg *config.Config) {})
|
||||
|
||||
go func() {
|
||||
e.Run(t, baseCmd...)
|
||||
}()
|
||||
|
@ -114,7 +109,7 @@ func TestServerStart(t *testing.T) {
|
|||
var line string
|
||||
require.Eventually(t, func() bool {
|
||||
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)
|
||||
}
|
||||
return err == nil
|
||||
|
@ -123,11 +118,11 @@ func TestServerStart(t *testing.T) {
|
|||
for _, expected := range lines {
|
||||
// It should be regexp, so escape all backslashes.
|
||||
expected = strings.ReplaceAll(expected, `\`, `\\`)
|
||||
e.CheckLine(t, line, expected)
|
||||
line = e.GetNextLine(t)
|
||||
e.checkLine(t, line, expected)
|
||||
line = e.getNextLine(t)
|
||||
}
|
||||
e.CheckNextLine(t, "")
|
||||
e.CheckEOF(t)
|
||||
e.checkNextLine(t, "")
|
||||
e.checkEOF(t)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -2,91 +2,51 @@ package smartcontract
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"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/rpcbinding"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/urfave/cli"
|
||||
"gopkg.in/yaml.v3"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var generatorFlags = []cli.Flag{
|
||||
var generateWrapperCmd = cli.Command{
|
||||
Name: "generate-wrapper",
|
||||
Usage: "generate wrapper to use in other contracts",
|
||||
UsageText: "neo-go contract generate-wrapper --manifest manifest.json --out file.go",
|
||||
Description: ``,
|
||||
Action: contractGenerateWrapper,
|
||||
Flags: []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",
|
||||
Usage: "Output of the compiled contract",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "hash",
|
||||
Usage: "Smart-contract hash. If not passed, the wrapper will be designed for dynamic hash usage",
|
||||
Usage: "Smart-contract hash",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var generateWrapperCmd = cli.Command{
|
||||
Name: "generate-wrapper",
|
||||
Usage: "generate wrapper to use in other contracts",
|
||||
UsageText: "neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]",
|
||||
Description: `Generates a Go wrapper to use it in other smart contracts. If the
|
||||
--hash flag is provided, CALLT instruction is used for the target contract
|
||||
invocation as an optimization of the wrapper contract code. If omitted, the
|
||||
generated wrapper will be designed for dynamic hash usage, allowing
|
||||
the hash to be specified at runtime.
|
||||
`,
|
||||
Action: contractGenerateWrapper,
|
||||
Flags: generatorFlags,
|
||||
}
|
||||
|
||||
var generateRPCWrapperCmd = cli.Command{
|
||||
Name: "generate-rpcwrapper",
|
||||
Usage: "generate RPC wrapper to use for data reads",
|
||||
UsageText: "neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]",
|
||||
Action: contractGenerateRPCWrapper,
|
||||
Flags: generatorFlags,
|
||||
}
|
||||
|
||||
// contractGenerateWrapper deploys contract.
|
||||
func contractGenerateWrapper(ctx *cli.Context) error {
|
||||
return contractGenerateSomething(ctx, binding.Generate)
|
||||
}
|
||||
|
||||
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)
|
||||
m, _, err := readManifest(ctx.String("manifest"))
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("can't read contract manifest: %w", err), 1)
|
||||
}
|
||||
|
||||
cfg := binding.NewConfig()
|
||||
if cfgPath := ctx.String("config"); cfgPath != "" {
|
||||
bs, err := os.ReadFile(cfgPath)
|
||||
bs, err := ioutil.ReadFile(cfgPath)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
f, err := os.Create(ctx.String("out"))
|
||||
|
@ -106,7 +72,7 @@ func contractGenerateSomething(ctx *cli.Context, cb func(binding.Config) error)
|
|||
|
||||
cfg.Output = f
|
||||
|
||||
err = cb(cfg)
|
||||
err = binding.Generate(cfg)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("error during generation: %w", err), 1)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
package smartcontract
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
@ -46,15 +45,6 @@ func TestGenerate(t *testing.T) {
|
|||
ReturnType: smartcontract.IntegerType,
|
||||
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{
|
||||
Name: "justExecute",
|
||||
Parameters: []manifest.Parameter{
|
||||
|
@ -77,6 +67,13 @@ func TestGenerate(t *testing.T) {
|
|||
},
|
||||
ReturnType: smartcontract.BoolType,
|
||||
},
|
||||
manifest.Method{
|
||||
Name: "emptyName",
|
||||
Parameters: []manifest.Parameter{
|
||||
manifest.NewParameter("", smartcontract.MapType),
|
||||
},
|
||||
ReturnType: smartcontract.AnyType,
|
||||
},
|
||||
manifest.Method{
|
||||
Name: "searchStorage",
|
||||
Parameters: []manifest.Parameter{
|
||||
|
@ -118,7 +115,7 @@ func TestGenerate(t *testing.T) {
|
|||
|
||||
rawManifest, err := json.Marshal(m)
|
||||
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{
|
||||
0x04, 0x08, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x00, 0x01,
|
||||
|
@ -142,7 +139,7 @@ callflags:
|
|||
doSomething: ReadStates
|
||||
`
|
||||
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",
|
||||
"--manifest", manifestFile,
|
||||
|
@ -151,9 +148,7 @@ callflags:
|
|||
"--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.
|
||||
|
||||
// Package wrapper contains wrappers for MyContract contract.
|
||||
const expected = `// Package wrapper contains wrappers for MyContract contract.
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
|
@ -174,8 +169,8 @@ func Sum(first int, second int) int {
|
|||
return neogointernal.CallWithToken(Hash, "sum", int(contract.All), first, second).(int)
|
||||
}
|
||||
|
||||
// Sum2 invokes ` + "`sum`" + ` method of contract.
|
||||
func Sum2(first int, second int, third int) int {
|
||||
// Sum_3 invokes ` + "`sum`" + ` method of contract.
|
||||
func Sum_3(first int, second int, third int) 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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func JustExecute(arr []any) {
|
||||
func JustExecute(arr []interface{}) {
|
||||
neogointernal.CallWithTokenNoRet(Hash, "justExecute", int(contract.All), arr)
|
||||
}
|
||||
|
||||
|
@ -200,10 +190,15 @@ func GetPublicKey() interop.PublicKey {
|
|||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func SearchStorage(ctx storage.Context) 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.
|
||||
func DoSomething(bytes []byte, str string) any {
|
||||
return neogointernal.CallWithToken(Hash, "doSomething", int(contract.ReadStates), bytes, str).(any)
|
||||
func DoSomething(bytes []byte, str string) interface{} {
|
||||
return neogointernal.CallWithToken(Hash, "doSomething", int(contract.ReadStates), bytes, str).(interface{})
|
||||
}
|
||||
|
||||
// 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.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) {
|
||||
|
@ -335,7 +237,6 @@ func TestGenerateValidPackageName(t *testing.T) {
|
|||
Name: "get",
|
||||
Parameters: []manifest.Parameter{},
|
||||
ReturnType: smartcontract.IntegerType,
|
||||
Safe: true,
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -344,25 +245,23 @@ func TestGenerateValidPackageName(t *testing.T) {
|
|||
|
||||
rawManifest, err := json.Marshal(m)
|
||||
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{
|
||||
0x04, 0x08, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x00, 0x01,
|
||||
0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF, 0x03, 0x04,
|
||||
}
|
||||
app := cli.NewApp()
|
||||
app.Commands = []cli.Command{generateWrapperCmd, generateRPCWrapperCmd}
|
||||
app.Commands = []cli.Command{generateWrapperCmd}
|
||||
require.NoError(t, app.Run([]string{"", "generate-wrapper",
|
||||
"--manifest", manifestFile,
|
||||
"--out", outFile,
|
||||
"--hash", "0x" + h.StringLE(),
|
||||
"--hash", h.StringLE(),
|
||||
}))
|
||||
|
||||
data, err := os.ReadFile(outFile)
|
||||
data, err := ioutil.ReadFile(outFile)
|
||||
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.
|
||||
|
||||
// Package myspacecontract contains wrappers for My space contract contract.
|
||||
require.Equal(t, `// Package myspacecontract contains wrappers for My space contract contract.
|
||||
package myspacecontract
|
||||
|
||||
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.
|
||||
func Get() int {
|
||||
return neogointernal.CallWithToken(Hash, "get", int(contract.ReadOnly)).(int)
|
||||
return neogointernal.CallWithToken(Hash, "get", int(contract.All)).(int)
|
||||
}
|
||||
`, 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) {
|
||||
|
@ -557,45 +289,31 @@ func TestGenerate_Errors(t *testing.T) {
|
|||
err := app.Run(append([]string{"", "generate-wrapper"}, args...))
|
||||
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) {
|
||||
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) {
|
||||
checkError(t, "can't read contract manifest", "--manifest", "notexists", "--hash", util.Uint160{}.StringLE(), "--out", "zzz")
|
||||
})
|
||||
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")
|
||||
checkError(t, "can't read contract manifest", "--manifest", "notexists")
|
||||
})
|
||||
t.Run("invalid manifest", func(t *testing.T) {
|
||||
manifestFile := filepath.Join(t.TempDir(), "invalid.json")
|
||||
m := manifest.NewManifest("MyContract") // no methods
|
||||
rawManifest, err := json.Marshal(m)
|
||||
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")
|
||||
require.NoError(t, ioutil.WriteFile(manifestFile, []byte("[]"), os.ModePerm))
|
||||
checkError(t, "", "--manifest", manifestFile)
|
||||
})
|
||||
|
||||
manifestFile := filepath.Join(t.TempDir(), "manifest.json")
|
||||
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)
|
||||
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) {
|
||||
checkError(t, "can't read config file",
|
||||
"--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) {
|
||||
rawCfg := `package: wrapper
|
||||
|
@ -603,110 +321,10 @@ callflags:
|
|||
someFunc: ReadSometimes
|
||||
`
|
||||
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",
|
||||
"--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(),
|
||||
"--config", cfgPath, "--out", "zzz")
|
||||
})
|
||||
}
|
||||
|
||||
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`")
|
||||
})
|
||||
"--config", cfgPath)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -4,22 +4,34 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/cli/cmdargs"
|
||||
"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/smartcontract/manifest"
|
||||
"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"
|
||||
)
|
||||
|
||||
func manifestAddGroup(ctx *cli.Context) error {
|
||||
if err := cmdargs.EnsureNone(ctx); err != nil {
|
||||
return err
|
||||
walletPath := ctx.String("wallet")
|
||||
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"))
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("invalid sender: %w", err), 1)
|
||||
|
@ -31,23 +43,22 @@ func manifestAddGroup(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
mPath := ctx.String("manifest")
|
||||
m, _, err := readManifest(mPath, util.Uint160{})
|
||||
m, _, err := readManifest(mPath)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("can't read contract manifest: %w", err), 1)
|
||||
}
|
||||
|
||||
h := state.CreateContractHash(sender, nf.Checksum, m.Name)
|
||||
|
||||
gAcc, w, err := options.GetAccFromContext(ctx)
|
||||
gAcc, err := getUnlockedAccount(w, addr)
|
||||
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
|
||||
|
||||
sig := gAcc.PrivateKey().Sign(h.BytesBE())
|
||||
pub := gAcc.PublicKey()
|
||||
pub := gAcc.PrivateKey().PublicKey()
|
||||
for i := range m.Groups {
|
||||
if m.Groups[i].PublicKey.Equal(pub) {
|
||||
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)
|
||||
}
|
||||
|
||||
err = os.WriteFile(mPath, rawM, os.ModePerm)
|
||||
err = ioutil.WriteFile(mPath, rawM, os.ModePerm)
|
||||
if err != nil {
|
||||
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")
|
||||
}
|
||||
|
||||
f, err := os.ReadFile(filename)
|
||||
f, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -92,15 +103,12 @@ func readNEFFile(filename string) (*nef.File, []byte, error) {
|
|||
return &nefFile, f, nil
|
||||
}
|
||||
|
||||
// readManifest unmarshalls manifest got from the provided filename and checks
|
||||
// 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) {
|
||||
func readManifest(filename string) (*manifest.Manifest, []byte, error) {
|
||||
if len(filename) == 0 {
|
||||
return nil, nil, errNoManifestFile
|
||||
}
|
||||
|
||||
manifestBytes, err := os.ReadFile(filename)
|
||||
manifestBytes, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -110,8 +118,5 @@ func readManifest(filename string, hash util.Uint160) (*manifest.Manifest, []byt
|
|||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package smartcontract
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"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/util"
|
||||
"gopkg.in/yaml.v3"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type permission manifest.Permission
|
||||
|
@ -18,41 +19,39 @@ const (
|
|||
permMethodKey = "methods"
|
||||
)
|
||||
|
||||
func (p permission) MarshalYAML() (any, error) {
|
||||
m := yaml.Node{Kind: yaml.MappingNode}
|
||||
func (p permission) MarshalYAML() (interface{}, error) {
|
||||
m := make(yaml.MapSlice, 0, 2)
|
||||
switch p.Contract.Type {
|
||||
case manifest.PermissionWildcard:
|
||||
case manifest.PermissionHash:
|
||||
m.Content = append(m.Content,
|
||||
&yaml.Node{Kind: yaml.ScalarNode, Value: permHashKey},
|
||||
&yaml.Node{Kind: yaml.ScalarNode, Value: p.Contract.Value.(util.Uint160).StringLE()})
|
||||
m = append(m, yaml.MapItem{
|
||||
Key: permHashKey,
|
||||
Value: p.Contract.Value.(util.Uint160).StringLE(),
|
||||
})
|
||||
case manifest.PermissionGroup:
|
||||
m.Content = append(m.Content,
|
||||
&yaml.Node{Kind: yaml.ScalarNode, Value: permGroupKey},
|
||||
&yaml.Node{Kind: yaml.ScalarNode, Value: p.Contract.Value.(*keys.PublicKey).StringCompressed()})
|
||||
bs := p.Contract.Value.(*keys.PublicKey).Bytes()
|
||||
m = append(m, yaml.MapItem{
|
||||
Key: permGroupKey,
|
||||
Value: hex.EncodeToString(bs),
|
||||
})
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid permission type: %d", p.Contract.Type)
|
||||
}
|
||||
|
||||
var val any = "*"
|
||||
var val interface{} = "*"
|
||||
if !p.Methods.IsWildcard() {
|
||||
val = p.Methods.Value
|
||||
}
|
||||
|
||||
n := &yaml.Node{Kind: yaml.ScalarNode}
|
||||
err := n.Encode(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.Content = append(m.Content,
|
||||
&yaml.Node{Kind: yaml.ScalarNode, Value: permMethodKey},
|
||||
n)
|
||||
|
||||
m = append(m, yaml.MapItem{
|
||||
Key: permMethodKey,
|
||||
Value: val,
|
||||
})
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (p *permission) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var m map[string]any
|
||||
func (p *permission) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var m map[string]interface{}
|
||||
if err := unmarshal(&m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -64,7 +63,7 @@ func (p *permission) UnmarshalYAML(unmarshal func(any) error) error {
|
|||
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]
|
||||
vg, ok2 := m[permGroupKey]
|
||||
switch {
|
||||
|
@ -102,7 +101,7 @@ func (p *permission) fillType(m map[string]any) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *permission) fillMethods(m map[string]any) error {
|
||||
func (p *permission) fillMethods(m map[string]interface{}) error {
|
||||
methods, ok := m[permMethodKey]
|
||||
if !ok {
|
||||
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
|
||||
return nil
|
||||
}
|
||||
case []any:
|
||||
case []interface{}:
|
||||
ms := make([]string, len(mt))
|
||||
for i := range mt {
|
||||
ms[i], ok = mt[i].(string)
|
||||
|
|
|
@ -5,48 +5,68 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/cli/cmdargs"
|
||||
"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/txctx"
|
||||
"github.com/nspcc-dev/neo-go/cli/paramcontext"
|
||||
"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/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||
"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/rpcclient/management"
|
||||
"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/rpc/client"
|
||||
"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/binding"
|
||||
"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/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"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 (
|
||||
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")
|
||||
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")
|
||||
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")
|
||||
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")
|
||||
|
||||
walletFlag = cli.StringFlag{
|
||||
Name: "wallet, w",
|
||||
Usage: "wallet to use to get the key for transaction signing",
|
||||
}
|
||||
addressFlag = flags.AddressFlag{
|
||||
Name: addressFlagName,
|
||||
Name: "address, a",
|
||||
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
|
||||
|
@ -68,7 +88,7 @@ func init() {
|
|||
}
|
||||
|
||||
// RuntimeNotify sends runtime notification with "Hello world!" name
|
||||
func RuntimeNotify(args []any) {
|
||||
func RuntimeNotify(args []interface{}) {
|
||||
runtime.Notify(notificationName, args)
|
||||
}`
|
||||
)
|
||||
|
@ -80,20 +100,16 @@ func NewCommands() []cli.Command {
|
|||
Name: "in, i",
|
||||
Usage: "Input location of the .nef file that needs to be invoked",
|
||||
},
|
||||
options.Historic,
|
||||
}
|
||||
testInvokeScriptFlags = append(testInvokeScriptFlags, options.RPC...)
|
||||
testInvokeFunctionFlags := []cli.Flag{options.Historic}
|
||||
testInvokeFunctionFlags = append(testInvokeFunctionFlags, options.RPC...)
|
||||
invokeFunctionFlags := []cli.Flag{
|
||||
walletFlag,
|
||||
addressFlag,
|
||||
txctx.GasFlag,
|
||||
txctx.SysGasFlag,
|
||||
txctx.OutFlag,
|
||||
txctx.ForceFlag,
|
||||
txctx.AwaitFlag,
|
||||
gasFlag,
|
||||
sysGasFlag,
|
||||
outFlag,
|
||||
forceFlag,
|
||||
}
|
||||
invokeFunctionFlags = append(invokeFunctionFlags, options.Wallet...)
|
||||
invokeFunctionFlags = append(invokeFunctionFlags, options.RPC...)
|
||||
deployFlags := append(invokeFunctionFlags, []cli.Flag{
|
||||
cli.StringFlag{
|
||||
|
@ -105,24 +121,6 @@ func NewCommands() []cli.Command {
|
|||
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{{
|
||||
Name: "contract",
|
||||
Usage: "compile - debug - deploy smart contracts",
|
||||
|
@ -130,20 +128,11 @@ func NewCommands() []cli.Command {
|
|||
{
|
||||
Name: "compile",
|
||||
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,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
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{
|
||||
Name: "out, o",
|
||||
|
@ -177,42 +166,30 @@ func NewCommands() []cli.Command {
|
|||
Name: "no-permissions",
|
||||
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",
|
||||
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
|
||||
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
|
||||
--await flag is specified, it waits for the transaction to be included
|
||||
in a block.
|
||||
parameter is an optional parameter to be passed to '_deploy' method.
|
||||
`,
|
||||
Action: contractDeploy,
|
||||
Flags: deployFlags,
|
||||
},
|
||||
generateWrapperCmd,
|
||||
generateRPCWrapperCmd,
|
||||
{
|
||||
Name: "invokefunction",
|
||||
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,
|
||||
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,
|
||||
specify it via signers parameter. See testinvokefunction documentation for
|
||||
the details about parameters. It differs from testinvokefunction in that this
|
||||
command sends an invocation transaction to the network. When --await flag is
|
||||
specified, it waits for the transaction to be included in a block.
|
||||
command sends an invocation transaction to the network.
|
||||
`,
|
||||
Action: invokeFunction,
|
||||
Flags: invokeFunctionFlags,
|
||||
|
@ -220,7 +197,7 @@ func NewCommands() []cli.Command {
|
|||
{
|
||||
Name: "testinvokefunction",
|
||||
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,
|
||||
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
|
||||
|
@ -230,17 +207,117 @@ func NewCommands() []cli.Command {
|
|||
follow the regular convention of smart contract arguments (method string and
|
||||
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,
|
||||
Flags: testInvokeFunctionFlags,
|
||||
Flags: options.RPC,
|
||||
},
|
||||
{
|
||||
Name: "testinvokescript",
|
||||
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
|
||||
signers not included sender by default. See testinvokefunction documentation
|
||||
for the details about parameters.
|
||||
|
@ -251,7 +328,6 @@ func NewCommands() []cli.Command {
|
|||
{
|
||||
Name: "init",
|
||||
Usage: "initialize a new smart-contract in a directory with boiler plate code",
|
||||
UsageText: "neo-go contract init -n name [--skip-details]",
|
||||
Action: initSmartContract,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
|
@ -267,7 +343,6 @@ func NewCommands() []cli.Command {
|
|||
{
|
||||
Name: "inspect",
|
||||
Usage: "creates a user readable dump of the program instructions",
|
||||
UsageText: "neo-go contract inspect -i file [-c]",
|
||||
Action: inspect,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
|
@ -283,7 +358,6 @@ func NewCommands() []cli.Command {
|
|||
{
|
||||
Name: "calc-hash",
|
||||
Usage: "calculates hash of a contract after deployment",
|
||||
UsageText: "neo-go contract calc-hash -i nef -m manifest -s address",
|
||||
Action: calcHash,
|
||||
Flags: []cli.Flag{
|
||||
flags.AddressFlag{
|
||||
|
@ -307,9 +381,26 @@ func NewCommands() []cli.Command {
|
|||
{
|
||||
Name: "add-group",
|
||||
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,
|
||||
Flags: manifestAddGroupFlags,
|
||||
Flags: []cli.Flag{
|
||||
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.
|
||||
func initSmartContract(ctx *cli.Context) error {
|
||||
if err := cmdargs.EnsureNone(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
contractName := ctx.String("name")
|
||||
if contractName == "" {
|
||||
return cli.NewExitError(errNoSmartContractName, 1)
|
||||
|
@ -346,26 +434,24 @@ func initSmartContract(ctx *cli.Context) error {
|
|||
SourceURL: "http://example.com/",
|
||||
SupportedStandards: []string{},
|
||||
SafeMethods: []string{},
|
||||
Events: []compiler.HybridEvent{
|
||||
Events: []manifest.Event{
|
||||
{
|
||||
Name: "Hello world!",
|
||||
Parameters: []compiler.HybridParameter{
|
||||
Parameters: []manifest.Parameter{
|
||||
{
|
||||
Parameter: manifest.Parameter{
|
||||
Name: "args",
|
||||
Type: smartcontract.ArrayType,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Permissions: []permission{permission(*manifest.NewPermission(manifest.PermissionWildcard))},
|
||||
}
|
||||
b, err := yaml.Marshal(m)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -375,18 +461,15 @@ func initSmartContract(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
gm := []byte("module " + contractName + `
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -396,9 +479,6 @@ require (
|
|||
}
|
||||
|
||||
func contractCompile(ctx *cli.Context) error {
|
||||
if err := cmdargs.EnsureNone(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
src := ctx.String("in")
|
||||
if len(src) == 0 {
|
||||
return cli.NewExitError(errNoInput, 1)
|
||||
|
@ -406,48 +486,19 @@ func contractCompile(ctx *cli.Context) error {
|
|||
manifestFile := ctx.String("manifest")
|
||||
confFile := ctx.String("config")
|
||||
debugFile := ctx.String("debug")
|
||||
out := ctx.String("out")
|
||||
bindings := ctx.String("bindings")
|
||||
if len(confFile) == 0 && (len(manifestFile) != 0 || len(debugFile) != 0 || len(bindings) != 0) {
|
||||
if len(confFile) == 0 && (len(manifestFile) != 0 || len(debugFile) != 0) {
|
||||
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{
|
||||
Outfile: out,
|
||||
Outfile: ctx.String("out"),
|
||||
|
||||
DebugInfo: debugFile,
|
||||
ManifestFile: manifestFile,
|
||||
BindingsFile: bindings,
|
||||
|
||||
NoStandardCheck: ctx.Bool("no-standards"),
|
||||
NoEventsCheck: ctx.Bool("no-events"),
|
||||
NoPermissionsCheck: ctx.Bool("no-permissions"),
|
||||
|
||||
GuessEventTypes: ctx.Bool("guess-eventtypes"),
|
||||
}
|
||||
|
||||
if len(confFile) != 0 {
|
||||
|
@ -458,7 +509,6 @@ func contractCompile(ctx *cli.Context) error {
|
|||
o.Name = conf.Name
|
||||
o.SourceURL = conf.SourceURL
|
||||
o.ContractEvents = conf.Events
|
||||
o.DeclaredNamedTypes = conf.NamedTypes
|
||||
o.ContractSupportedStandards = conf.SupportedStandards
|
||||
o.Permissions = make([]manifest.Permission, len(conf.Permissions))
|
||||
for i := range conf.Permissions {
|
||||
|
@ -480,9 +530,6 @@ func contractCompile(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)
|
||||
if !sender.IsSet {
|
||||
return cli.NewExitError("sender is not set", 1)
|
||||
|
@ -496,7 +543,7 @@ func calcHash(ctx *cli.Context) error {
|
|||
if mpath == "" {
|
||||
return cli.NewExitError(errors.New("no manifest file provided"), 1)
|
||||
}
|
||||
f, err := os.ReadFile(p)
|
||||
f, err := ioutil.ReadFile(p)
|
||||
if err != nil {
|
||||
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 {
|
||||
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 {
|
||||
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
|
||||
exitErr *cli.ExitError
|
||||
operation string
|
||||
params []any
|
||||
params = make([]smartcontract.Parameter, 0)
|
||||
paramsStart = 1
|
||||
scParams []smartcontract.Parameter
|
||||
cosigners []transaction.Signer
|
||||
cosignersOffset = 0
|
||||
)
|
||||
|
@ -552,14 +598,10 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
|
|||
paramsStart++
|
||||
|
||||
if len(args) > paramsStart {
|
||||
cosignersOffset, scParams, err = cmdargs.ParseParams(args[paramsStart:], true)
|
||||
cosignersOffset, params, err = cmdargs.ParseParams(args[paramsStart:], true)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
params = make([]any, len(scParams))
|
||||
for i := range scParams {
|
||||
params[i] = scParams[i]
|
||||
}
|
||||
}
|
||||
|
||||
cosignersStart := paramsStart + cosignersOffset
|
||||
|
@ -573,84 +615,112 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
|
|||
w *wallet.Wallet
|
||||
)
|
||||
if signAndPush {
|
||||
acc, w, err = options.GetAccFromContext(ctx)
|
||||
acc, w, err = getAccFromContext(ctx)
|
||||
if err != nil {
|
||||
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 (
|
||||
err error
|
||||
signersAccounts []actor.SignerAccount
|
||||
gas, sysgas fixedn.Fixed8
|
||||
cosignersAccounts []client.SignerAccount
|
||||
resp *result.Invoke
|
||||
sender util.Uint160
|
||||
signAndPush = acc != nil
|
||||
inv *invoker.Invoker
|
||||
act *actor.Actor
|
||||
)
|
||||
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 {
|
||||
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)
|
||||
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 {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
if resp.State != "HALT" {
|
||||
errText := fmt.Sprintf("Warning: %s VM state returned from the RPC node: %s", resp.State, resp.FaultException)
|
||||
if !signAndPush {
|
||||
return cli.NewExitError(errText, 1)
|
||||
return sender, err
|
||||
}
|
||||
|
||||
action := "send"
|
||||
process := "Sending"
|
||||
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" {
|
||||
errText := fmt.Sprintf("Warning: %s VM state returned from the RPC node: %s\n", resp.State, resp.FaultException)
|
||||
action := "save"
|
||||
process := "Saving"
|
||||
if signAndPush {
|
||||
if out != "" {
|
||||
action = "save"
|
||||
process = "Saving"
|
||||
action += "and send"
|
||||
process += "and sending"
|
||||
} else {
|
||||
action = "send"
|
||||
process = "Sending"
|
||||
}
|
||||
}
|
||||
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, "", " ")
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
return sender, cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
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)
|
||||
|
||||
return sender, nil
|
||||
}
|
||||
|
||||
func testInvokeScript(ctx *cli.Context) error {
|
||||
|
@ -659,7 +729,7 @@ func testInvokeScript(ctx *cli.Context) error {
|
|||
return cli.NewExitError(errNoInput, 1)
|
||||
}
|
||||
|
||||
b, err := os.ReadFile(src)
|
||||
b, err := ioutil.ReadFile(src)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
@ -676,12 +746,12 @@ func testInvokeScript(ctx *cli.Context) error {
|
|||
gctx, cancel := options.GetTimeoutContext(ctx)
|
||||
defer cancel()
|
||||
|
||||
_, inv, err := options.GetRPCWithInvoker(gctx, ctx, signers)
|
||||
c, err := options.GetRPCClient(gctx, ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := inv.Run(nefFile.Script)
|
||||
resp, err := c.InvokeScript(nefFile.Script, signers)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
@ -702,16 +772,12 @@ type ProjectConfig struct {
|
|||
SourceURL string
|
||||
SafeMethods []string
|
||||
SupportedStandards []string
|
||||
Events []compiler.HybridEvent
|
||||
Events []manifest.Event
|
||||
Permissions []permission
|
||||
Overloads map[string]string `yaml:"overloads,omitempty"`
|
||||
NamedTypes map[string]binding.ExtendedType `yaml:"namedtypes,omitempty"`
|
||||
}
|
||||
|
||||
func inspect(ctx *cli.Context) error {
|
||||
if err := cmdargs.EnsureNone(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
in := ctx.String("in")
|
||||
compile := ctx.Bool("compile")
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
f, err := os.ReadFile(in)
|
||||
f, err := ioutil.ReadFile(in)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
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.
|
||||
func contractDeploy(ctx *cli.Context) error {
|
||||
nefFile, f, err := readNEFFile(ctx.String("in"))
|
||||
|
@ -751,13 +863,21 @@ func contractDeploy(ctx *cli.Context) error {
|
|||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
m, manifestBytes, err := readManifest(ctx.String("manifest"), util.Uint160{})
|
||||
m, manifestBytes, err := readManifest(ctx.String("manifest"))
|
||||
if err != nil {
|
||||
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)
|
||||
if err != nil {
|
||||
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])
|
||||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
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 {
|
||||
return extErr
|
||||
}
|
||||
|
@ -799,7 +930,7 @@ func contractDeploy(ctx *cli.Context) error {
|
|||
// ParseContractConfig reads contract configuration file (.yaml) and returns unmarshalled ProjectConfig.
|
||||
func ParseContractConfig(confFile string) (ProjectConfig, error) {
|
||||
conf := ProjectConfig{}
|
||||
confBytes, err := os.ReadFile(confFile)
|
||||
confBytes, err := ioutil.ReadFile(confFile)
|
||||
if err != nil {
|
||||
return conf, cli.NewExitError(err, 1)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package smartcontract
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
|
@ -10,7 +12,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/cli"
|
||||
"gopkg.in/yaml.v3"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func TestInitSmartContract(t *testing.T) {
|
||||
|
@ -29,13 +31,13 @@ func TestInitSmartContract(t *testing.T) {
|
|||
dirInfo, err := os.Stat(contractName)
|
||||
require.NoError(t, err)
|
||||
require.True(t, dirInfo.IsDir())
|
||||
files, err := os.ReadDir(contractName)
|
||||
files, err := ioutil.ReadDir(contractName)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 3, len(files))
|
||||
require.Equal(t, "go.mod", files[0].Name())
|
||||
require.Equal(t, "main.go", files[1].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.Equal(t,
|
||||
`package `+contractName+`
|
||||
|
@ -50,13 +52,14 @@ func init() {
|
|||
}
|
||||
|
||||
// RuntimeNotify sends runtime notification with "Hello world!" name
|
||||
func RuntimeNotify(args []any) {
|
||||
func RuntimeNotify(args []interface{}) {
|
||||
runtime.Notify(notificationName, args)
|
||||
}`, string(main))
|
||||
|
||||
manifest, err := os.ReadFile(contractName + "/" + files[2].Name())
|
||||
manifest, err := ioutil.ReadFile(contractName + "/" + files[2].Name())
|
||||
require.NoError(t, err)
|
||||
expected := `name: testContract
|
||||
require.Equal(t,
|
||||
`name: testContract
|
||||
sourceurl: http://example.com/
|
||||
safemethods: []
|
||||
supportedstandards: []
|
||||
|
@ -67,8 +70,7 @@ events:
|
|||
type: Array
|
||||
permissions:
|
||||
- methods: '*'
|
||||
`
|
||||
require.Equal(t, expected, string(manifest))
|
||||
`, string(manifest))
|
||||
}
|
||||
|
||||
func testPermissionMarshal(t *testing.T, p *manifest.Permission, expected string) {
|
||||
|
@ -108,7 +110,7 @@ func TestPermissionMarshal(t *testing.T) {
|
|||
p.Methods.Add("abc")
|
||||
p.Methods.Add("lamao")
|
||||
testPermissionMarshal(t, p,
|
||||
"group: "+priv.PublicKey().StringCompressed()+"\n"+
|
||||
"group: "+hex.EncodeToString(priv.PublicKey().Bytes())+"\n"+
|
||||
"methods:\n- abc\n- lamao\n")
|
||||
})
|
||||
}
|
||||
|
@ -117,7 +119,7 @@ func TestPermissionUnmarshalInvalid(t *testing.T) {
|
|||
priv, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
pub := priv.PublicKey().StringCompressed()
|
||||
pub := hex.EncodeToString(priv.PublicKey().Bytes())
|
||||
u160 := random.Uint160().StringLE()
|
||||
testCases := []string{
|
||||
"hash: []\nmethods: '*'\n", // invalid hash type
|
||||
|
|
52
cli/smartcontract/testdata/gas/gas.go
vendored
52
cli/smartcontract/testdata/gas/gas.go
vendored
|
@ -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}
|
||||
}
|
|
@ -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}
|
511
cli/smartcontract/testdata/nameservice/nns.go
vendored
511
cli/smartcontract/testdata/nameservice/nns.go
vendored
|
@ -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
|
||||
}
|
|
@ -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" : []
|
||||
}
|
339
cli/smartcontract/testdata/nex/nex.go
vendored
339
cli/smartcontract/testdata/nex/nex.go
vendored
|
@ -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
|
||||
}
|
275
cli/smartcontract/testdata/nex/nex.manifest.json
vendored
275
cli/smartcontract/testdata/nex/nex.manifest.json
vendored
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
63
cli/smartcontract/testdata/nonepiter/iter.go
vendored
63
cli/smartcontract/testdata/nonepiter/iter.go
vendored
|
@ -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))
|
||||
}
|
|
@ -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" : {}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package invalid1
|
||||
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
|
||||
func Main() {
|
||||
runtime.Notify("Non declared event")
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
name: Test undeclared event
|
|
@ -1,7 +0,0 @@
|
|||
package invalid2
|
||||
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
|
||||
func Main() {
|
||||
runtime.Notify("SomeEvent", "p1", "p2")
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
name: Test undeclared event
|
||||
events:
|
||||
- name: SomeEvent
|
||||
parameters:
|
||||
- name: p1
|
||||
type: String
|
|
@ -1,7 +0,0 @@
|
|||
package invalid3
|
||||
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
|
||||
func Main() {
|
||||
runtime.Notify("SomeEvent", "p1", 5)
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
name: Test undeclared event
|
||||
events:
|
||||
- name: SomeEvent
|
||||
parameters:
|
||||
- name: p1
|
||||
type: String
|
||||
- name: p2
|
||||
type: String
|
|
@ -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"})
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
name: Test undeclared event
|
||||
events:
|
||||
- name: SomeEvent
|
||||
parameters:
|
||||
- name: p1
|
||||
type: Array
|
|
@ -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}
|
||||
}
|
|
@ -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
|
|
@ -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})
|
||||
}
|
|
@ -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
|
|
@ -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})
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
name: Test duplicating autogenerated event fields
|
||||
events:
|
||||
- name: SomeEvent
|
||||
parameters:
|
||||
- name: p1
|
||||
type: Struct
|
|
@ -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
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
name: Test duplicating struct fields
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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})
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
Loading…
Add table
Reference in a new issue