Compare commits

..

20 commits

Author SHA1 Message Date
Anna Shaleva
abd332f764 nns: add Renew event
Port https://github.com/neo-project/non-native-contracts/pull/31.
2022-09-16 11:43:48 +03:00
Anna Shaleva
748a7d3eea nns: add SetAdmin event
Port https://github.com/neo-project/non-native-contracts/pull/11.
2022-09-16 11:37:33 +03:00
Anna Shaleva
9eed7c856e nns: accept expiration period in renew
Port https://github.com/neo-project/non-native-contracts/pull/13.
2022-09-16 11:31:53 +03:00
Anna Shaleva
dea82ac4dd nns: make domain registration price dependant on length
Port 8ac2b80734.
2022-09-16 11:27:58 +03:00
Anna Shaleva
14c9858df2 nns: use MillisecondsInSecond constant where appropriate 2022-09-16 11:21:03 +03:00
Anna Shaleva
d8e0a02a86 nns: keep isAvailable in sync with register
If conflicting records '*.domain' are present on new domain
registration, then `isAvailable` should return false for this
domain. Ref.
f25296b17a.
2022-09-16 11:20:59 +03:00
Anna Shaleva
d5b1c0e429 nns: check for conflicting records on domain registration
Port f25296b17a.
2022-09-16 11:04:58 +03:00
Anna Shaleva
db9cea5ecc nns: allow arbitrary level domains
Port
d10da892d9.
2022-09-16 11:04:55 +03:00
Anna Shaleva
ea934b8e30 nns: support SOA records
Port 7f1ec13ae5
and f4762c1b56.
2022-09-16 10:59:23 +03:00
Anna Shaleva
8790602f69 nns: ensure records with the same type are not repeated
Port https://github.com/nspcc-dev/neofs-contract/pull/170.
2022-09-09 19:36:16 +03:00
Anna Shaleva
c9050cef4b nns: allow multiple records of the same type
Except for the CNAME records. Port
6ea4573ef8
and
f4762c1b56.
2022-09-09 19:36:13 +03:00
Anna Shaleva
c296f8804c nns: add test for getAllRecords 2022-09-09 19:35:54 +03:00
Anna Shaleva
4543de0923 *: update basic test chain
Apply new NNS rules.
2022-09-08 14:19:39 +03:00
Anna Shaleva
d77b35c385 nns: add admin to properties
See 14f43ba8cf/src/NameService/NameService.cs (L69).
2022-09-08 14:19:39 +03:00
Anna Shaleva
225152f2d7 nns: allow to resolve FQDN
Port 4041924a75.
2022-09-08 14:19:39 +03:00
Anna Shaleva
baf24d1c66 nns: check domain expiration for read functions
Port 432c02a369.
2022-09-08 14:19:39 +03:00
Anna Shaleva
017a6b9bc1 nns: require admin signature for subdomain registration
Port
14fc086291.
2022-09-08 14:19:39 +03:00
Anna Shaleva
5cb2a1219c nns: replace root with TLD
Port
4b86891d57.
2022-09-08 14:19:39 +03:00
Anna Shaleva
c11481b119 nns: allow hyphen in domain names
Port https://github.com/nspcc-dev/neofs-contract/pull/183.
2022-09-08 14:19:39 +03:00
Anna Shaleva
bd3722041a nns: adjust maxDomainNameFragmentLength
Port https://github.com/nspcc-dev/neofs-contract/pull/238.
2022-09-08 14:19:39 +03:00
731 changed files with 15672 additions and 50922 deletions

138
.circleci/config.yml Normal file
View file

@ -0,0 +1,138 @@
version: 2.1
executors:
go1_17:
docker:
- image: cimg/go:1.17
go1_18:
docker:
- image: cimg/go:1.18
go1_19:
docker:
- image: cimg/go:1.19
commands:
gomod:
steps:
- restore_cache:
keys: [deps-v2-]
- run:
name: Download go module dependencies
command: go mod download
- save_cache:
key: deps-v2-{{ checksum "go.sum" }}-{{ checksum "go.sum" }}
paths: [/home/circleci/go/pkg/mod]
jobs:
lint:
working_directory: /home/circleci/go/src/github.com/nspcc-dev/neo-go
# TODO: temp workaround, need to upgrade to go1_18 after https://github.com/golangci/golangci-lint/issues/2649 is resolved.
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.44.2
make lint
test_1_17:
working_directory: /home/circleci/go/src/github.com/nspcc-dev/neo-go
executor: go1_17
steps:
- checkout
- run: git submodule sync
- run: git submodule update --init
- gomod
- run:
command: go test -v -race ./...
no_output_timeout: 15m
test_1_18:
working_directory: /home/circleci/go/src/github.com/nspcc-dev/neo-go
executor: go1_18
steps:
- checkout
- run: git submodule sync
- run: git submodule update --init
- gomod
- run:
command: go test -v -race ./...
no_output_timeout: 15m
test_1_19:
working_directory: /home/circleci/go/src/github.com/nspcc-dev/neo-go
executor: go1_19
steps:
- checkout
- run: git submodule sync
- run: git submodule update --init
- gomod
- run:
command: go test -v -race ./...
no_output_timeout: 15m
build_cli:
working_directory: /home/circleci/go/src/github.com/nspcc-dev/neo-go
executor: go1_19
steps:
- checkout
- gomod
- run: make build
- store_artifacts:
path: bin
destination: /
build_image:
working_directory: /home/circleci/go/src/github.com/nspcc-dev/neo-go
executor: go1_19
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_17:
filters:
tags:
only: v/[0-9]+\.[0-9]+\.[0-9]+/
- test_1_18:
filters:
tags:
only: v/[0-9]+\.[0-9]+\.[0-9]+/
- test_1_19:
filters:
tags:
only: v/[0-9]+\.[0-9]+\.[0-9]+/
- build_cli:
filters:
tags:
only: v/[0-9]+\.[0-9]+\.[0-9]+/
- build_image:
requires:
- build_cli
filters:
tags:
only: v/[0-9]+\.[0-9]+\.[0-9]+/

View file

@ -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": {
@ -52,4 +52,4 @@
"extra": {
"Tokens": null
}
}
}

View file

@ -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": {
@ -69,4 +69,4 @@
"extra": {
"Tokens": null
}
}
}

View file

@ -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": {
@ -52,4 +52,4 @@
"extra": {
"Tokens": null
}
}
}

View file

@ -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": {
@ -52,4 +52,4 @@
"extra": {
"Tokens": null
}
}
}

View file

@ -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": {
@ -52,4 +52,4 @@
"extra": {
"Tokens": null
}
}
}

1
.github/CODEOWNERS vendored
View file

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

BIN
.github/logo_dark.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

View file

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View file

@ -34,83 +34,78 @@ on:
jobs:
build_cli:
name: Build CLI
runs-on: ${{matrix.os.name}}
runs-on: ${{matrix.os}}
strategy:
matrix:
os: [{ name: ubuntu-22.04, bin-name: linux }, { name: windows-2022, bin-name: windows }, { name: macos-12, bin-name: darwin }]
os: [ubuntu-20.04, windows-2022, macos-12]
arch: [amd64, arm64]
exclude:
- os: { name: windows-2022, bin-name: windows }
- os: windows-2022
arch: 'arm64'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
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@v3
with:
go-version: '1.22'
go-version: 1.19
cache: true
- 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 }}
name: neo-go-${{ matrix.os }}-${{ matrix.arch }}
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
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.ref }}
fetch-depth: 0
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v2
- 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
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Set vars
id: setvars
run: make gh-docker-vars >> $GITHUB_OUTPUT
run: make gh-docker-vars
- 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
if: ${{ (github.event_name == 'release' && github.event.release.target_commitish == 'master') || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_image == 'true' && github.event.inputs.use_latest_tag == 'true') }}
run: echo "::set-output name=latest::,${{ steps.setvars.outputs.repo }}:latest"
- name: Build and push
uses: docker/build-push-action@v5
uses: docker/build-push-action@v3
with:
context: .
push: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_image == 'true') }}
@ -126,20 +121,21 @@ jobs:
runs-on: windows-2022
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
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@v3
with:
go-version: '1.22'
go-version: 1.19
cache: true
- 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
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}

View file

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

View file

@ -8,7 +8,7 @@ on:
- master
types: [opened, synchronize]
paths-ignore:
- 'scripts/*.sh'
- 'scripts/**'
- '**/*.md'
workflow_dispatch:
@ -18,61 +18,26 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/setup-go@v3
with:
go-version-file: 'go.mod'
go-version: 1.19
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v4
uses: golangci/golangci-lint-action@v3
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
- uses: actions/checkout@v3
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
@ -88,11 +53,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@ -103,7 +68,7 @@ jobs:
# 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
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@ -117,37 +82,41 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v2
test_cover:
name: Coverage
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
env:
CGO_ENABLED: 0
GOEXPERIMENT: nocoverageredesign
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: 'true'
- name: Sync VM submodule
run: |
git submodule sync
git submodule update --init
- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v3
with:
go-version: '1.22'
go-version: 1.19
cache: true
- name: Update Go modules
run: go mod download -json
- name: Write coverage profile
run: go test -timeout 15m -v ./... -coverprofile=./coverage.txt -covermode=atomic -coverpkg=./pkg...,./cli/...
run: go test -v ./... -coverprofile=./coverage.txt -covermode=atomic -coverpkg=./pkg...,./cli/...
- name: Upload coverage results to Codecov
uses: codecov/codecov-action@v4
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
slug: nspcc-dev/neo-go
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true
tests:
@ -155,36 +124,40 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-22.04, windows-2022, macos-12, macos-14]
go_versions: [ '1.20', '1.21', '1.22' ]
os: [ubuntu-20.04, windows-2022, macos-12]
go_versions: [ '1.17', '1.18', '1.19' ]
exclude:
# Only latest Go version for Windows and MacOS.
- os: windows-2022
go_versions: '1.20'
go_versions: '1.17'
- os: windows-2022
go_versions: '1.21'
go_versions: '1.18'
- os: macos-12
go_versions: '1.20'
go_versions: '1.17'
- os: macos-12
go_versions: '1.21'
- os: macos-14
go_versions: '1.20'
- os: macos-14
go_versions: '1.21'
go_versions: '1.18'
# Exclude latest Go version for Ubuntu as Coverage uses it.
- os: ubuntu-22.04
go_versions: '1.22'
- os: ubuntu-20.04
go_versions: '1.19'
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: 'true'
- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v3
with:
go-version: '${{ matrix.go_versions }}'
cache: true
- 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 -timeout 15m -v -race ./...
run: go test -v -race ./...

4
.gitignore vendored
View file

@ -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/
@ -51,7 +54,6 @@ testdata/
!pkg/services/notary/testdata
!pkg/services/oracle/testdata
!pkg/smartcontract/testdata
!cli/smartcontract/testdata
pkg/vm/testdata/fuzz
!pkg/vm/testdata
!pkg/wallet/testdata

View file

@ -32,32 +32,20 @@ linters:
- revive
# 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"

View file

@ -2,942 +2,6 @@
This document outlines major changes between releases.
## 0.106.1 "Implication" (3 Jun 2024)
An urgent release that fixes mainnet state difference at block 5462944 which halts
blocks processing starting from the height 5468658. This release requires full node
DB resynchronization for mainnet nodes. T5 testnet DB state is not affected by this
bug (at least up to the current 4087361 height). Thus, DB resynchronisation may be
skipped for testnet nodes. No configuration changes implied.
Bugs fixed:
* mainnet state difference at block 5462944 caused by runtime notification
permissions check against the updated contract state instead of executing state
(#3472)
* unused neofs-contract dependency in the node modules (#3458)
## 0.106.0 "Zephyranthes" (21 May 2024)
We're rolling out a large set of updates including all of Neo 3.7.4 protocol changes:
native contracts update functionality and extended native contract APIs united under
the upcoming Cockatrice hardfork. For smart contract developers an ability to
generate smart contract bindings with dynamic hash is supported as well as a number
of useful extensions for `unwrap` package, compatible NNS smart contract RPC binding
and a lot of other handy enhancements. This release also includes a couple of severe
regression bug fixes affecting the node state. As a bonus of fixing a wide range of
failing tests, we've refactored the node services start and shutdown procedures to
make it more stable. This version drops support for Go 1.19 and requires 1.20+ to
build (with Go 1.22 supported).
Notice that this release requires full node resynchronization for both mainnet
and testnet nodes. Please pay a special attention to the Cockatrice hardfork schedule
and check your configurations. It should be configured for 3967000 of T5 testnet and
5450000 of mainnet. To ensure compatibility your node must be configured
appropriately.
New features:
* native contracts update functionality bound to hardforks (#3402, #3444)
* Cockatrice hardfork planned for 5450000 block of mainnet and 3967000 block of T5
testnet (#3402, #3448, #3402, #3446)
* `CommitteeChanged` events are emitted by NeoToken native contract starting from
Cockatrice hardfork (#3351, #3448)
* `getCommitteeAddress` method of NeoToken native contract is available starting
from Cockatrice hardfork (#3362, #3402)
* `keccak256` method of CryptoLib native contract is available starting from
Cockatrice hardfork (#3301, #3402)
* dynamic contract hash support for smart contract bindings (#3405)
* support `StringCompressed` API for `crypto.PublicKey` (#3408)
* support `Copy` API for `transaction.Transaction` and `payload.P2PNotaryRequest`
(#3407)
* extend `verifyWithECDsa` method of native CryptoLib contract with Keccak256 hasher
starting from Cockatrice and add an example of custom Koblitz-based and
Keccak256-based transaction witness verification (#3425)
* autogenerated `nativehashes` package (#3402, #3431)
* introduce `unwrap.Exception` type for better RPC invocation result exceptions
detection (#3438)
Behavior changes:
* Neo Name Service smart contract RPC binding follows the official N3 implementation
(#3291)
* clear LastGasPerVote NeoToken account info on unvoting (#3349)
* return fault exception (if any) in `unwrap` RPC client helpers (#3356)
* move P2PNotary designation role out of P2PSigExtensions (#3452)
Improvements:
* support `smartcontract.Convertible` interface as RPC Actor and Invoker call
parameters (#3297)
* allow to import multisignature account into wallet without WIF and password
specified (#3293)
* upgrade go-ordered-json dependency to avoid possible node state incompatibility
issues caused by smart contract manifest marshalling (#3331, #3333)
* Go 1.22 support, bump minimum required Go version up to Go 1.20 (#3336)
* a number of dependent libraries are updated to the fresh versions (#3338, #3418)
* improve error message of `System.Crypto.CheckMultisig` interop API handler (#3374)
* upgrade dBFT library to v0.2.0 (#3371, #3413)
* increase `ValidUntilBlock` value for transactions generated by CLI commands
(#3376)
* documentation updates (#3375, #3382)
* let default Notary Actor options be customizable (#3394)
* extend `getversion` RPC response with RPC server settings (#3386)
* make errors related to wallet opening more detailed (#3389)
* adjust error messages of `setExecFeeFactor` and `setStoragePrice` methods of
native PolicyContract (#3406)
* make neotest chain constructor options customizable (#3404)
* format improvements for CLI commands description (#3410)
* make state dumps comparator script more verbose (#3411)
* refactor storage `Get` implementation for BoltDB (#3441)
* add `updatecounter` field to the resulting items of `getnativecontracts` RPC API
call (#3439)
Bugs fixed:
* node panic on committee missing from node configuration (#3294)
* autogenerated RPC bindings do not follow Go naming convention (#3299)
* a batch of failing tests is fixed (#3306, #3313, #3321, #3332, #3337, #3335,
#3344, #3340, #3350, #3355, #3364, #3368, #3377, #3393, #3397, #3392, #3398,
#3400)
* outdated minimum required Go version of boilerplate contract generated by
`neo-go contract init` (#3318)
* outdated address used as an example in RPC client documentation (#3327)
* ungraceful node services shutdown procedure (#3307)
* logging data race on node services shutdown (#3307)
* Map parameter support is missing from RPC server handlers (#3329)
* smart contract storage iterator prefix wasn't copied while iterating over values
(#3336)
* RPC error with -511 code (insufficient funds) returned on `sendrawtransaction` RPC
request should cover multiple cases of sender's insufficient funds (#3360, #3361)
* reentrancy possibility for Notary deposit withdrawal (#3357)
* null `findstorage` RPC response in case of missing storage items (#3385)
* uninitialized named return variables in compiler (#3401)
* wrong debug sequence points after `JUMP*` instructions shortening by compiler
(#3412)
* corrupted genesis block record and application log caused by improper Conflicts
attribute processing (#3437)
* false positive DB-based blocked accounts detection in native PolicyContract leaded
to invalid committee computations and reward distribution made by NeoToken (#3443)
## 0.105.1 "Enumeration" (12 Jan 2024)
This is another v3.6.2-compatible release that fixes mainnet state difference at
block 4688591. It is confirmed to have the same state up to 4723K height (which
is current), but to get proper mainnet state you need to resynchronize your node
from the genesis. T5 testnet state is not affected (at least up to the current
3323K height), thus DB resynchronisation may be skipped for testnet nodes.
Improvements:
* better network services logging (#3287, #3290)
Bugs fixed:
* state difference at block 4688591 of N3 mainnet caused by difference at
characters escaping during manifest's `Extra` field JSON serialisation (#3286)
## 0.105.0 "Designation" (29 Dec 2023)
We're rolling out an update for NeoGo nodes that contains a number of user-facing
API improvements for RPC web-socket notification subsystem, CLI utility, wallet
related packages and not only. Try out our new `--await` CLI option for those
commands that relay transactions to the network to automatically wait for the
on-chain transaction execution result. Subscribe for new block headers with
extended RPC notification subsystem interface. Use contract-based accounts
provided by `wallet` package and `neotest` framework to sign transactions. These
and a set of other improvements are available to our users while this release is
staying compatible with 3.6.2 version of C# node.
This version also delivers a bug fix for consensus node panic caused by improper
native NeoToken cache initialisation. Moreover, there's a set of RPC server side
improvements, thus, we recommend to upgrade both consensus and RPC nodes to
provide more stable consensus node functioning and extended user APIs functionality.
No database resynchronisation is needed.
New features:
* block headers RPC web-socket subscription (#3252)
* --await option to synchronize on transaction execution for CLI commands (#3265)
* partial session-based RPC iterator unwrapping (#3274)
* contract-based transaction signers in neotest framework (#3233)
* AMD64 release binaries for macOS (#3251)
* complex contract signature schemes in wallet.Account (#3256)
Behavior changes:
* basic RPC subscription filter validity checks are implemented on both RPC
client and RPC server sides (#3258)
* filter of notary request event RPC subscription is extended with `type` field
(#3236)
* if available, use block headers RPC web-socket subscription for transaction
awaiting via `waiter` package API (#3283)
Improvements:
* add smart contract storage limits to interop utilities (#3232)
* extend ZKP examples documentation with additional links to PoT ceremony
response files (#3234)
* support Go types in VM emitter API (#3237)
* documentation update (#3239, #3242, #3246)
* BoltDB (go.etcd.io/bbolt) dependency upgrade (#3250)
* CLI code refactoring (#2682)
* extend wallet package to work with byte slice based wallets (#3255)
* export RPC client side transaction awaiting functionality via `waiter` package
(#3265, #3283)
Bugs fixed:
* remove stale `updatehistory` section of `getnativecontracts` RPC response (#3240)
* immediately check RPC client initialisation on access to blocks RPC subscription
API (#3257, #3261)
* fix CN panic caused by unexpected call to native NeoToken cache (#3253)
* make "automatically generated" warning of all automatically generated files
follow the standard (#3280)
## 0.104.0 "Globalization" (27 Nov 2023)
We're updating NeoGo to push out a number of useful updates and protocol
extensions as well as make it compatible with 3.6.2 version of C# node. The most
invasive behaviour changes are VM-level protocol constraints imposed on the size
of serialized stackitems and `NativeActivation` node setting removal. This version
also delivers a set of smaller useful changes in the interoperability layer and
native contract functionality including System.Runtime.CurrentSigners interop,
`strLen` StdLib method and PolicyContract-based transaction attributes pricing
as far as a user-facing `canceltx` CLI command and community-requested
`--relative-path` CLI option.
Node operators must resynchronize their nodes to get fully compatible state
(which is confirmed to be compatible with 3.6.2 for current mainnet up to
block 4483627 and T5 testnet up to block 3078762). Please, ensure your node
configuration doesn't contain `NativeActivations` protocol configuration section
as this logic is hidden under Hardforks starting from the current release.
New features:
* System.Runtime.CurrentSigners interop allowing to get signers of the currently
loaded transaction (#3058)
* `strLen` method to native StdLib contract (#3208)
* transaction attributes pricing regulation via native PolicyContract (#3155)
* `canceltx` CLI command as an alternative to unsupported `canceltransaction` RPC
request (#3223, #3214)
Behaviour changes:
* reduce maximum allowed stackitem.Item size (#3185)
* restrict maximum allowed NEF file size and prohibit large contracts deployment (#3186)
* bind `NativeActivations` node setting to the `Hardforks` setting (#3212)
* introduce serialization limit to stackitem.Item and fail large contracts deploy
wrt this setting (#3218)
* add customizable `MaxRequestBodyBytes` and `MaxRequestHeaderBytes` RPC server
configuration setting (#3221)
Improvements:
* provide more detailed error on attempt to read non-existent service wallet
provided via node configuration file (#3210)
* optimize emit of imported code for autogenerated RPC bindings (#3215)
* documentation update (#3203, #3222)
* add `--relative-path` CLI flag allowing to override configuration-specific relative
paths (#3206)
* update code owners (#3216, @fyrchik will live in our hearts forever)
Bugs fixed:
* unify messages of RPC errors according to the RPC errors NEP (#3199)
* limit maximum allowed number ad depth of transaction signers and witnesses per
RPC request that accept transaction (#3207, #3221)
* forbid unknown fields usage in the node configuration file (#3209)
* enable hardfork-dependant code starting exactly from the block height specified
in the node configuration (#3211)
* require Notary native deposit to be valid for at least one subsequent block after
deposit transaction acceptance (#3211)
* deduplicate unnamed event types for autogenerated RPC bindings and make the
binding generation order strictly defined and stable (#3215, #3220)
* do not panic on trying to compile an import cycle (#3215)
* state difference at block 3002333 of T5 testnet caused by difference at characters
escaping during manifest's `Extra` field JSON serialisation (#3225)
* properly start node services that depend on native RoleManagement contract data
with genesis `Roles` protocol configuration setting specified (#3229)
## 0.103.1 "Verification" (09 Nov 2023)
An urgent 3.6.0-compatible version that contains a hotfix for the bug that
prevents node from starting from the existing database every new dBFT epoch.
Also, the release includes a bugfix that fails any non-zero NEO and GAS
roundtrips from accounts with zero balance.
Although the DB format and storage states were not affected by this release,
the node resynchronization is still recommended for those who want to keep
correct application logs. Resynchronization can be skipped if you're ok with
wrong application logs for some transactions (e.g. CN doesn't care, and RPC node
cares a lot).
Bugs fixed:
* properly initialize cache of native NeoToken contract every new dBFT epoch (#3187)
* forbid non-zero NEO and GAS roundtrips from accounts with zero balance (#3191)
## 0.103.0 "Backwardation" (20 Oct 2023)
A minor 3.6.0-compatible version of NeoGo with new salty ZKP-related
functionality, a large batch of deprecated APIs removal and an experimental
genesis block extension. Build Groth-16 circuits, generate proofs and deploy
verification contracts easily with the new end-to-end ZKP example and `zkpbinding`
NeoGo API. Setup node roles via the node configuration starting from the genesis
block without any painful multisignature transaction forming and invoke any
custom script in the genesis-level transaction with the new `Genesis` protocol
configuration extension. And don't forget to move from deprecated RPC client
APIs, as a lot of them have been removed.
New node configuration format is finally adapted and the deprecated configuration
sections are permanently removed according to the schedule. Pay attention to the
`SecondsPerBlock` protocol configuration section that was replaced by
`TimePerBlock`, a set of node and services address- and port- related configuration
section that were replaced by the new `Addresses` format, direct `UnlockWallet`
consensus configuration that was replaced by a separate `Consensus` section and
a set of node-specific protocol configurations that were moved under a separate
`P2P` section.
This release fixes a set of bugs including a vulnerability introduced by changes
in the transaction conflicts storage scheme implemented in #3061 (a part of
0.102.0 release). With the patch presented, it's impossible to uncontrollably
pollute the storage with malicious conflicting records.
This version is fully compatible with C# node 3.6.0. However, it requires
complete resynchronization on upgrade due to the database storage scheme changes.
New features:
* API for Groth-16 verification contracts autogeneration and end-to-end example for
proving and verifying statements on NeoGo (#3043, #3146)
* introduce genesis protocol extensions: default node roles designation and genesis
transactions (#3168)
Behaviour changes:
* a lot of deprecated functionality is dropped: RPC client old APIs, shared
Notifications channel of WebSocket client, SecondsPerBlock protocol
configuration, old way of services and node address and port configuration, old
P2P related application settings configuration, direct UnlockWallet consensus
configuration, direct node-specific protocol configuration (#3150)
* database scheme is changed by new way of storing the transaction conflicting
records (#3138)
Improvements:
* NeoFS SDK dependency upgrade (#3129)
* gnark and gnark-crypto dependencies upgrade (#3145, #3162, #3163)
* introduce timeout restriction for BoltDB opening (#3148)
* bump code coverage uploading action version (#3153)
* Go 1.21 support, bump minimum required Go version up to Go 1.19 (#3157)
* upgrade golang.org/x/net version (#3158)
* add protocol hardforks configuration to the `getversion` RPC response (#3160)
* format autogenerated smart contract bindings with accordance to standard `go fmt` rules (#3164)
* add "DO NOT EDIT" warning to the autogenerated smart contract bindings (#3167)
Bugs fixed:
* avoid race between `getnextblockvalidators` RPC call handler and blockchain's
block handler routine, significantly refactor native validators caching scheme
(#3110)
* do not panic on failed NeoFS oracle requests (#3129)
* enable Notary subsystem in NeoFS mainnet configuration (#3136)
* reorganize storage scheme for transaction conflicting records to avoid possible attack (#3138)
* properly deserialize wildcard trusts field of smart contract manifest (#3140)
* use more strict GAS limit for transaction network fee calculation via RPC call (#3141)
* avoid WebSocket client request blocking (#3142)
* properly emit big uint64 constants during smart contract compilation (#3147)
* avoid race access to the node version by tests (#3149)
* make FindStorage* RPC client APIs to be always compatible with NeoC# RPC server (#3166)
## 0.102.0 "Aberration" (06 Sep 2023)
Long-awaited feature-packed 3.6.0-compatible version of NeoGo with all the
appropriate protocol updates and a set of tasty improvements and bug
fixes. Groth16 ZKP proof checks are there for contract developers as well as
new opcodes. A huge number of improvements went into the RPC server and
client, new RPCs, improved contract binding generator and standardized
extended error codes make developing dApps much easier.
Users of smart contract utilities and RPC-related Prometheus metrics are
advised to take a look at the deprecated APIs removal schedule
(ROADMAP.md). This version was delayed for quite some time (waiting for 3.6),
so the next minor release (0.103.0) will remove a substantial amount of
compatibility code. It's expected to be released in November with 3.6 protocol
compatibility (3.7 cycle is likely to require more time).
This is also the first version that drops support for Go 1.17 and requires
1.18+ to build (with Go 1.20 supported). Using generics in smart contracts is
not supported yet, but some elements of this support can be implemented in
future versions.
Mainnet and testnet node operators, please pay attention to the Basilisk
hardfork schedule and check your configurations. It will happen at block
2680000 for T5 testnet and 4120000 for mainnet. To ensure compatibility your
node must be configured appropriately. This version requires DB
resynchronization, so schedule your updates accordingly.
New features:
* ZKP proof checking support via CryptoLib native contract (operations with
BLS12-381 curve points) (#2940, #3042)
* System.Storage.Find interop now support "Backwards" flag to iterate in the
opposite direction (#2952)
* `util ops` CLI utility that pretty-prints VM script (#2965)
* `notarypool_unsorted_tx` Prometheus metric for notary-enabled nodes (#2696)
* `CloseNotificationChannelIfFull` WSClient option allowing the client to
close notification channel on overflow (#2988)
* autogenerated RPC bindings for contract events and conversion code from
stackitem.Item to structure (#3008, #3035, #3036, #3087)
* event type inference from contract code (#3008)
* dynamic contract hashes support for RPC bindings (#3012)
* "Basilisk" protocol hardfork support (#3056, #3080, #3086, #3104)
* ABORTMSG and ASSERTMSG VM opcodes (#3066)
* standardized RPC error codes (#3063)
* `findstorage` and `findstoragehistoric` RPC support (#3099)
* `getstoragehistoric` RPC support (#3099)
* `getrawnotarypool` and `getrawnotarytransaction` RPC (#3098)
Behaviour changes:
* deprecated `FromAddress` smart contract helper is dropped from the `util`
interop package (#2941)
* a number of deprecated RPC-related Prometheus counters are permanently
removed (#2941)
* NEP-2 account label will be asked on account import via CLI (#2889)
* hashes and states of native contracts are now accessible via native
ContractManagement API (#2991)
* `serv_node_version` gauge Prometheus metric is marked as deprecated and
replaced by `neogo_version` and `server_id` (#3009)
* strict contract script check is back with Basilisk hardfork, it was
previously removed in 0.101.3 for 3.5 protocol compatibility (#3056)
* JSON number deserialization (via StdLib.jsonDeserialize) changes with
Basilisk hardfork allowing for more precision and handling more corner
cases (#3080)
* notification type errors are treated as fatal after Basilisk hardfork,
before it they're just logged (#3086)
* NULL and non-UTF8 items are not allowed for String event types (#3086)
* `Conflicts` and `NotValidBefore` transaction attributes are no longer NeoGo
extensions, they can be used on regular networks (#2962)
Improvements:
* documentation and example improvements (#2945, #2972, #2979, #3020, #3084,
#3099, #3114, #3116, #3119, #3121)
* `getproof` and `verifyproof` RPC API support in RPC client (#2942)
* Go 1.20 support, bump minimum required Go version up to Go 1.18 (#2908)
* faster state reset process (#2819)
* optimized voting data storage scheme for NEO contract (#2892, #2893)
* NeoFS SDK dependency upgrades (#2995, #3032)
* special exported error returned in case of WSClient disconnection (#3000)
* automatic guessing of contract and manifest filenames for `contract
compile` CLI command and `loadnef` VM CLI command (#3013)
* support for pushing stackitem.Convertible objects via VM script emitter (#3016)
* economic adjustment for ranking of transactions with `Conflicts` attribute (#3031)
* `google.golang.org/grpc` dependency upgrade fixing high severity security
vulnerability (#3055, gRPC is only used to communicate with NeoFS nodes in
the oracle service)
* enforce default RPC server values when it's used as an independent package
(not a part of node) (#3107)
* unwrap.Nothing function for RPC clients (#3117)
* address and reverse hash display in opcode dumps (#3115)
Bugs fixed:
* invalid peer port type returned by `getpeers` RPC response (#2914)
* invalid data source for `mempool_unsorted_tx` Prometheus metric (#2969)
* dBFT library upgrade fixing the ability of a single node to speed up the
process of new blocks creation for the whole network (#3018,
nspcc-dev/dbft#75)
* failing CALLT instructions in VM CLI for loaded NEF files (#3020)
* missing signers check for on-chain conflicting transactions (#3061)
* incorrect sequence point boundaries in debug data (#3074)
* compiler panic on encountering generic code (#3041)
* potential pooled fallback notary transaction changes (#3108)
* lost LastGasPerVote value on NEO state deserialization for non-voting
accounts (#3122)
## 0.101.4 "Yarborough" (01 Aug 2023)
Another one 3.5.0-compatible version that is aimed to fix T5 testnet state
difference that has happened at block 2336911 which leads to inability to process
new blocks since 2418703. The issue is fixed by allowing JSON numbers
unmarshalling from scientific notation to Integer stackitem. Maximum parsing
precision for such numbers is currently restricted by 53 bits. This is a
temporary C#-compatible solution that is likely to change in the future versions
when an appropriate C# node bug is fixed (neo-project/neo#2879).
A set of minor bug fixes is included as well to flush some of the long-awaited
changes that were blocked by the 0.102.0 release delay (caused by v3.6.0 C# node
release delay). In particular, invalid headers returned by an RPC server for
error responses, invalid format of incremental dumps created by CLI and deadlock
on unhealthy RPC server shutdown. Long-awaited `--config-file` CLI option to
start the node providing a single configuration file is added.
T5 testnet chain requires a complete resynchronization for this version. Mainnet
chain resynchronization is recommended, but not required.
New features:
* `--config-file` CLI option allowing to start the node with a single configuration file (#3014)
Improvements:
* blockchain Notary and Oracle services documentation improvement (#2972)
* BoltDB (`go.etcd.io/bbolt`) dependency upgrade that fixes a number of Windows-related issues (#3034)
Bugs fixed:
* panic on node start with invalid configuration (#2968)
* deadlock on unhealthy RPC server shutdown (#2966)
* improper WSClient notification channels managing after disconnection (#2980)
* missing Prometheus metric initialisation on node start (#2992)
* invalid initialisation of native contracts cache (#2994)
* incorrect way of incremental DB dumps creation (#3047)
* Notary contract is allowed to be a sender of main Notary request transaction (#3065)
* discrepancy in signer's witness scope parsing on the RPC server side (#3060)
* Invoker calling API isn't allowed to accept nil parameter (#3067)
* contract RPC Client unwrapper helper can't handle missing contract case (#3072)
* JSON numbers can't be unmarshalled to stackitem from scientific notation (#3073)
* invalid content-type header returned by RPC server on error responses (#3075)
## 0.101.3 "Yuckiness" (08 Jul 2023)
Yet another 3.5.0-compatible emergency version that removes scrupulous
instructions check of smart contract's script on contract deployment or update.
Presence of this check leads to the known T5 testnet state incompatibility
(since 1670095) which causes inability to process new blocks (since 2272533).
It should be noted that the corresponding check was accidentally removed from
the reference C# node implementation way back in neo-project/neo#2266 and added
again in neo-project/neo#2849 which is planned to be a part of the upcoming
3.6.0 C# node release. Thus, changes made in the presented 0.101.3 release will
be reverted afterwards and strict contract script check will be present in the
next 3.6.0-compatible version of NeoGo node.
T5 testnet chain requires a complete resynchronization for this version. Mainnet
chain resynchronization is recommended.
Bugs fixed:
* extra strict contract script check on contract deployment or update is
removed (#3052)
## 0.101.2 "Excavation" (29 Jun 2023)
One more (and unexpected one!) 3.5.0-compatible version that fixes a VM bug
leading to mainnet state incompatibility (since 3672783) which in turn leads
to inability to process new blocks (since 3682290).
Mainnet chain needs to be resynchronized for this version.
Improvements:
* documentation updates (#3029, #2990, #2981)
Bugs fixed:
* incorrect handling of empty Any-type parameter for RPC invocations (#2959)
* incorrect state rollbacks in case of exception during cross-contract call
when the call is made from non-TRYing context (#3046)
## 0.101.1 "Shallowness" (17 Mar 2023)
Another 3.5.0-compatible version that delivers important bug fixes and
provides a new API to be used by NeoFS. An upgrade is recommended, the DB
doesn't need to be resynchronized.
New features:
* internal RPC client for deeply integrated applications like NeoFS (#2916)
Improvements:
* documentation updates (#2879, #2880, #2893, #2917, #2920, #2936)
* code style, spelling and updated linter fixes (#2884, #2922, #2933)
* NEP-2 import password can be provided via config file now (#2887)
* custom stack item deserialization limit is available via public APIs now (#2904)
* RPC client endpoint can be retrieved via public API (#2915)
* dependency updates (#2919, #2929)
* WSClient now copies filter parameters to Subscribe* and Receive* methods
improving code safety (#2937)
Bugs fixed:
* name parameter ignored for wallet import command (#2887)
* incorrect RPC binding code generated for Any return type (#2918)
* memory leak on active peer disconnection (#2924)
* consensus process deadlock (#2930)
* dBFT deadlock in "committed at previous view" scenario (#2935)
* panic in RPC waiter code (#2938)
## 0.101.0 "Shortness" (13 Jan 2023)
This release delivers an important fix for block execution application logs
and requires a resynchronization, therefore it's 0.101.0 (even though it's
also 3.5.0-compatible). It fixes some other minor problems as well (the other
most notable change probably is in the compiler), so we recommend upgrading.
Improvements:
* updated golang.org/x/* dependencies (#2854)
* CLI help and required flags handling fixes (#2852)
* transfer data storage optimization (#2865)
* network's magic number is stored (and checked against the config on
startup) in the DB now, reducing potential for node operator errors (#2867)
Bugs fixed:
* in rare cases nodes could request an invalid number of blocks from peers
leading to disconnect (#2846)
* outdated documentation fixes (#2860, #2876)
* application logs for blocks that contained GAS spends for transaction fees
contained (and returned from getapplicationlog RPC) incorrect (off by one)
values for amount in Transfer events; transaction application logs were not
affected by this, but data returned to RPC event subscribers could
potentially be (#2865)
* findstates RPC returned an error instead of an empty data set for valid
contracts that have no data (unlike C# node, #2866)
* miscompiled shadowed range loop variable definition (#2871)
* missing (compared to C# node) explicit (even though null) 'exception' field
in the getapplicationlog RPC output from server (#2872)
## 0.100.1 "Chaptalization" (28 Dec 2022)
This is a tiny update that 99.99% of users can easily skip. The reason for
this release is the need to fix compatibility with the NeoFS mainnet
sidechain and have some stable version to be used there. In any other case it
can be ignored, but if you still decide to upgrade you don't need to
resynchronize.
Behaviour changes:
* Aspidochelone fork is made to include ContractManagement native contract
deploy/update methods call flags change, initially it was an unconditional
part of 0.99.0 NeoGo release (or 3.2.0 C# version), but this behavior is
incompatible with the NeoFS mainnet sidechain; the change to the fork logic
does not affect any other public networks (mainnet/testnet) and any new
networks that have Aspidochelone enabled since block 0 (#2848)
Improvements:
* more robust NEP-11 divisibility check in the RPC server code (#2841)
* microoptimization for some debug log messages (#2842)
* additional fuzz tests for integer serialization and script parsing code (#2851)
## 0.100.0 "Centuplication" (08 Dec 2022)
A 3.5.0-compatible version of NeoGo with all the appropriate protocol updates
and a number of other changes. The most notable ones are configuration
updates. New features and some long-standing inconsistencies required for some
changes and this release brings them with it. Old configurations are still
supported and will work the same way (except for one minor exception in
VerifyBlocks that is only supposed to be used for tests/development), but we'd
like to highlight that all of the old settings will be removed in ~6 months,
so please review these updates and update your configurations. For public
networks the best way to go is to take the new versions from the "config"
directory and then adjust for particular scenario if needed.
This release requires a complete resynchronization due to native contract
changes, so please schedule your updates appropriately.
New features:
* System.Runtime.LoadScript syscall (and appropriate smart contract interops)
allowing to load/run dynamic code (#2719)
* PUSHT/PUSHF VM instructions allowing for simpler/cheaper booleans (#2770)
* ContractManagement native contract was extended with ID->hash mappings and
`getContractById` and `getContractHashes` methods (#2702, #2837)
* LogLevel application configuration option that can be changed on SIGHUP
(#2831)
* additional type data generated by the compiler that then can be used by the
RPC bindings (SDK/contract wrapper) generator, this allows for complex
types (structures/arrays/maps) to be easily represented/handled in
contract-specific RPC code (#2828)
* multiaddress listeners for all services and P2P, this changes the old
Address/Port (and AnnouncedPort for P2P) configuration scheme to more
generic Addresses list, the old configuration is still supported, but is
deprecated and will be removed in future node versions (#2827, #2839)
Behaviour changes:
* Aspidochelone fork block for NeoFS sidechain mainnet configuration is
rescheduled again (#2823, #2830)
* Blockchain's GetHeaderHash() method now accepts uint32 for parameter
(#2814)
* pre-0.97.3 and pre-0.99.0 deprecated compatibility fields and logic were
dropped from the result.Version (`getversion` RPC result) structure (#2786)
* SecondsPerBlock protocol configuration variable was replaced with
TimePerBlock allowing for sub-second precision, SecondsPerBlock is still
supported, but will eventually be removed (#2829)
* AttemptConnPeers, BroadcastFactor, DialTimeout, ExtensiblePoolSize,
MaxPeers, MinPeers, PingInterval, PingTimeout, ProtoTickInterval settings
were moved into the new P2P section, timing parameters now use Duration
type allowing for more human-friendly values in many cases, old parameters
are still supported, but will eventually be removed (#2827)
* consensus (dBFT) is configured as a separate service now that can be
enabled/disabled (and can work in "watch only" mode without a wallet), the
old direct "UnlockWallet" specification in "ApplicationConfiguration" is
still supported, but is deprecated and will be removed in future versions
(#2832)
* GarbageCollectionPeriod, KeepOnlyLatestState, RemoveUntraceableBlocks,
SaveStorageBatch and VerifyBlocks settings were moved from
ProtocolConfiguration to ApplicationConfiguration; old configurations are
still supported, except for VerifyBlocks which is replaced by
SkipBlockVerification with inverted meaning (and hence an inverted default)
for security reasons; this also affects NewBlockchain and neotest APIs
(#2833)
Improvements:
* more user-friendly error and help messages in some cases (#2824, #2834)
* faster node startup time and smaller memory footprint for networks with
lots (1-2M+) of blocks (#2814)
* minor documentation fixes (#2834, #2838)
Bugs fixed:
* transactions with system fee or more than MaxBlockSystemFee are no longer
accepted into the mempool, preventing a form a network-wide DoS (#2826)
* deprecated WSClient subscription methods not working correctly when filters
are being used (#2836)
## 0.99.7 "Hyalinization" (23 Nov 2022)
Bugs, terrestrial arthropods with at least six legs. Sometimes we don't notice
them, sometimes we ignore them, but they do exist and there are times when
they need to be dealt with in one way or another. Most of the bugs fixed in
this release have a little less legs than proper insects, they're far from
being as critical as the ones fixed in the previous release and yet we
consider them annoying enough to warrant this new version of NeoGo to be
published. We don't want to see our users fighting these pesky creatures in
the wild, better update and have a troublefree experience.
If you're not affected by #2815 (requests for verbose transaction data and
application logs failed for some specific mainnet transaction(s)) you may
leave the DB as is with this upgrade, if you want it to be solved then please
resynchronize.
Behaviour changes:
* "loadtx" VM CLI command now uses transaction system fee value as the
default GAS limit for the context, unless --gas is used (previously it
wasn't limited, #2816)
* SendRawTransaction RPC client method will now always return transaction
hash, even if error occured, this also affects Actor APIs and allows to
handle some specific errors (like transaction already present in the
mempool) in more appropriate way (#2817)
* Actor and NotaryActor Wait() wrapper methods now handle "already exists"
and "already on chain" errors as non-errors, waiting (if needed) and
returning proper results (#2817)
Improvements:
* reworked network discoverer/connection manager that has a better picture of
the network (especially if it's a small one), doesn't try to make additional
connections (even when having less than MinPeers of them) when there are no
more real addresses to connect to, establishes stable connections faster in
containerized environments and does more connection attempts when MinPeers
setting is not satisfied, but there are potential candidates (#2811)
* VM invocation stack handling microoptimization (#2812)
* load* VM CLI commands now accept --gas parameter to set the amount of GAS
to be used in this context (#2816)
* better logging during state reset process (#2813)
* subscription-based transaction waiter now handles already accepted
transactions faster (~immediately vs waiting for block timeout previously,
#2817)
* WSClient can return more specific error on websocket handshake failure if
it's provided by server (#2821)
* new MaxWebSocketClients configuration option for the RPC server to control
the number of allowed websocket clients, missing or 0 value make the server
use the default of 64 which is compatible with the previous behavior (you
can use old configurations if you don't care about this option, #2821)
Bugs fixed:
* occasional "loadgo" VM CLI test failures because of timeout (#2810)
* websocket waiter test instability (#2809)
* websocket event unsubscriptions could lead to node deadlock in rare
circumstances (#2809)
* exception handler could use improper evaluation stack in some cases (#2812)
* pointer stack items were not handled correctly in "protected" serialization
mode leading to inability to deserialize these items back (#2816)
* state reset functionality not working for big chains like mainnet (#2813)
## 0.99.6 "Funambulation" (16 Nov 2022)
An emergency release fixing mainnet incompatibility at block 2504320 leading
to inability to process block 2504813. A couple of other less severe bugs got
fixed as well. Unfortunately, this release requires complete resynchronization
for mainnet, other networks can use old DBs.
Improvements:
* only transactions requested by the consensus process are forwarded to it
now slightly improving performance in some networked scenarios (#2788)
* a complete set of simple array unwrappers in the unwrap library (#2792)
* RPC binding generator can handle simple arrays now with configuration file
provided (#2792)
* more user-friendly error messages in some cases (#2797, #2802, #2804)
Bugs fixed:
* DB was not properly closed after state reset (#2791)
* VM stack handling problem leading to lost items after return from call to
another contract (#2800)
* "istack" command panic in the VM CLI (#2800)
* failure to decode incorrect contract from ContractManagement state leading
to failure to start the node, it'll be ignored now similar to how C# node
treats this (#2802)
* subscription to execution events failed if no "state" filter was used along
with "container" (#2804)
* Actor using WSClient could deadlock in the transaction waiter during
unsubscription (#2804)
## 0.99.5 "Underestimation" (11 Nov 2022)
It wasn't long since the previous release until a juicy set of features and
optimisations came on board. So we've decided to change our release plan and
supply one more 3.4.0 compatible version. It comes up with a massive suite
of network P2P and broadcast improvements. We've carefully investigated message
sending timeouts on benchmarks, reworked the broadcast logic and significantly
reduced delays in message sending and the amount of useless traffic between our
nodes. For smart-contract developers and RPC client users several new features
were added. These are automatic RPC bindings generator, poll-based and
websocket-based transactions awaiting and an extended websocket client
subscriptions interface. For better debugging experience we've improved state
related functionality of VM CLI and added an ability to reset chain state to a
particular height.
An important dBFT initialization bug that caused timestamp-related errors on
block addition was fixed. Also, those who use NeoGo node with
EnableCORSWorkaround setting may want to update the node to be able to handle
web-socket requests with Origin set in the header. The users of websocket RPC
client subscription mechanism are welcomed to move from deprecated subscriptions
API to the extended one. Finally, we are kindly asking our users to have a look
at our roadmap file where deprecated code removal schedule is attached.
This version is fully compatible with C# node 3.4.0 and does not require
resynchronization on upgrade. This time for sure, the next release is planned
to be compatible with 3.5.0.
New features:
* logarithmic gossip fan-out, new `BroadcastFactor` application setting and
adaptive peer requests are added to optimise broadcasting process (#608,
#2678, #2743)
* Prometheus histograms with handling time are added for P2P commands (#2750)
and RPC calls (#2784)
* transaction awaiting is added to RPC clients (#2740, #2749)
* RPC bindings generator (#2705) is added for both safe (#2766) and
state-changing (#2769, #2778) methods with initial iterator support (#2783)
* the ability to reset chain state to a particular height is added (#2576,
#2773)
Behaviour changes:
* Aspidochelone fork block for NeoFS sidechain mainnet configuration is
rescheduled (#2754, #2776)
* RPC server will now handle any Origin in websocket connection when
EnableCORSWorkaround setting is on (#2772, #2724)
* WS RPC client subscriptions API is extended to accept custom subscriber
channels, old subscriptions API is marked as deprecated (#2756, #2764)
Improvements:
* network code refactoring (#2742)
* network packets sending is now limited in time (#2738)
* broadcast messages will be sent concurrently to each peer (#2741)
* ping messages are broadcast using the common broadcast logic (#2742)
* transactions requested by dBFT will be accepted on demand only (#2746, #2748,
nspcc-dev/dbft#63)
* VM CLI's state-based functionality improvements (extended signers and
arguments parsing, enabled logging) and additional commands (`loadtx`,
`loaddeployed`, `jump`) (#2729, #2606, #2740)
* incoming P2P transactions are now processed concurrently and buffer for
inventory hashes is reusable (#2755)
* `GetData` and `GetBlocksByIndex` replies are now batched on a TCP-level
(#2757)
* optimisation of incoming P2P transactions and blocks filtering are introduced
(#2758, #2759)
* batch size of broadcast transactions hashes is adjusted (#2760)
* peer reconnections logic is improved, `GetAddr` messages are sent more
often (#2761, #2745)
* documentation for wallet configuration file is clarified (#2763)
* Koblitz curve implementation of btcd/btcec was replaced with decred/secp256k1
due to GitHub security warning (#2781)
* `NOT` opcode for Bool emission is now used to reduce resulting script
cost (#2762)
Bugs fixed:
* failing tests cleanup on Windows (#2736)
* race in CLI test (#2765)
* invalid conversion of PublicKey smart contract parameter to emitable (#2739)
* initialize dBFT's context with previous block's timestamp on view 0 (#2753,
#2752, nspcc-dev/dbft#64)
## 0.99.4 "Transliteration" (07 Oct 2022)
A small update mostly interesting for people building/testing smart contracts
with NeoGo. It contains long-awaited VM CLI update that allows to use
blockchain state and complete set of interops, additional helper functions for
smart contracts and notification checking code (for upcoming 3.5.0 protocol
changes). Node operators using EnableCORSWorkaround (that is still not
recommended, but available) also may want to update to be able to handle
pre-flight CORS requests.
This version is compatible with C# node 3.4.0 and does not require
resynchronization on upgrade. The next release is planned to be compatible
with 3.5.0.
New features:
* address conversion helpers for smart contracts (#2698)
* interop helper to call specific version of a contract (#2695)
* notifications are now checked for manifest compliance, a warning is logged
in case on detected inconsistencies (#2710)
* VM CLI can now use blockchain state DB (including historic states) to run
code with a complete set of interops and contracts available; additional
commands were added to it to inspect storage and generated events (#2723)
Behavior changes:
* type assertion with two return values in Go contracts can't be compiled now
(it never worked properly, #2718)
* RPC server will now handle pre-flight CORS requests (via OPTIONS methods)
when EnableCORSWorkaround setting is on (#2724)
* all transaction-generating CLI commands were unified to accept
gas/sysgas/out/force parameters, ask for confirmation and be able to save
transaction into a file; this affects wallet claim and candidate-related
commands mostly (#2731)
Improvements:
* smartcontract.Builder API was extended with Len method (#2691)
* NNS example contract adjustments for better DNS compatibility (#2679)
* documentation updates (#2708, #2722, #2726, #2728)
* compiler now emits code to explicitly cast runtime.Notify() parameters to
appropriate types (#2720)
* CLI tests repackaging (#2725)
* NeoFS sidechain configurations for mainnet and testnet (#2730)
Bugs fixed:
* panic on node shutdown (#2689)
* panic on inlined append() compilation (#2716)
## 0.99.3 "Simplification" (09 Sep 2022)
Version 0.99.3 brings with it most of the planned refactoring changes to the
RPC client. dApp backend code can be greatly simplified in many cases by
using new APIs (old ones are still available for transition period of about
one-two releases more). We also have some updates for CLI and compiler and a
number of bug fixes.
This version is compatible with C# node 3.4.0 and does not require
resynchronization on upgrade. The next release is planned to be compatible
with 3.5.0 (if it's to be released around currently planned dates).
New features:
* native contract RPC wrappers (#2643, #2646, #2650, #2666, #2665)
* NEP-11 RPC wrappers (#2651)
* invoker interface extension with session-based iterators support (#2650)
* notary Actor greatly simplifying creation of notary-assisted transactions
via RPC client (#2665)
* historic smart contract calls can now be made via CLI (#2683)
Behavior changes:
* calculatenetworkfee RPC can handle paid attributes (NeoGo extensions) and
invalid contract signatures now, it won't return an error for them (#2658)
* graceful node shutdown on SIGTERM (#2660)
* wallet balance commands now require at least 0.99.1 NeoGo version (or
compatible C# node) used by the RPC server (#2667)
Improvements:
* build system corrections (#2641, #2684)
* additional types in `unwrap` (#2650, #2651)
* session iterator consistency check in `unwrap` (#2650)
* multitransfers in NEP-17 RPC wrapper (#2653)
* extended transaction validity time in CLI wallet commands (#2653)
* reference counter optimization in VM (#2659)
* RPC Actor API can have default (used for all transactions) attributes and
callbacks now (#2665)
* neptoken RPC package can now provide wallet.Token data (#2667)
* NEP-11 balance commands can now work without direct token hash
specification or previous token import (#2667)
* support for offline signing in CLI (#2668)
* compiler can optimize out unused global variables now (#2624)
* private keys are now cleaned up from memory when they're no longer needed,
additional Account APIs added to reduce direct interactions with private
keys (#2672)
* updated linter settings, some code cleanup (#2674)
* more documentation and examples for new RPC APIs (#2681)
* refactored state-changing methods of NEP-11/NEP-17 RPC wrappers into a
separate structure to simplify reusing them in higher-level code (#2681)
* simplified rpcclient historic API (#2686)
Bugs fixed:
* compiler panic on empty package list (#2645)
* compiler not allowed to use unnamed parameters in exported methods (#2648)
* compiler allowed to export multireturn functions (#2648)
* compiler panic on nil method receiver in the compiled code (#2649)
* compiler panic on variable initialization from multireturn call (#2644)
* potential lockups or panics on node shutdown (#2652)
* contract manifest not checked for correctness in bindings generation CLI
command (#2656)
* SignTx wallet Account API could lead to inconsistent result in some cases
(#2665)
* wallet Account API allowed to sign with locked accounts (#2665)
* potential panic in keys.WIFDecode on some inputs (#2672)
## 0.99.2 "Recalibration" (12 Aug 2022)
This is a 3.4.0-compatible (tested for mainnet and testnet) update to NeoGo
@ -2609,7 +1673,7 @@ Behavior changes:
* contracts no longer have single entry point, rather they export a set of
methods with specific offsets. Go smart contract compiler has been changed
accordingly to add all exported (as in Go) methods to the manifest
(but with the first letter being lowercased to match NEP-5 expectations,
(but with the first letter being lowercased to match NEP-5 expections,
#1228). Please also refer to examples changes to better see how it affects
contracts, manifests and configuration files (#1296)
* native contracts are now called via Neo.Native.Call syscall (#1191)

View file

@ -1,6 +1,6 @@
# Builder image
# Keep go version in sync with Build GA job.
FROM golang:1.22-alpine as builder
FROM golang:1.18-alpine as builder
# Display go version for information purposes.
RUN go version

View file

@ -1,6 +1,75 @@
# Builder image
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);
# Keep go version in sync with Build GA job.
FROM golang:1.22.0-windowsservercore-ltsc2022 as builder
ENV GOLANG_VERSION=1.19
RUN $url = 'https://go.dev/dl/go1.19.windows-amd64.zip'; \
Write-Host ('Downloading {0} ...' -f $url); \
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; \
Invoke-WebRequest -Uri $url -OutFile 'go.zip'; \
\
$sha256 = 'bcaaf966f91980d35ae93c37a8fe890e4ddfca19448c0d9f66c027d287e2823a'; \
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

View file

@ -1,6 +1,7 @@
MIT License
Copyright (c) 2018-2023 NeoSPCC (@nspcc-dev), Anthony De Meulemeester (@anthdm), City of Zion community (@CityOfZion)
Copyright (c) 2018-2022 Anthony De Meulemeester (@anthdm), City of Zion
community (@CityOfZion), NeoSPCC (@nspcc-dev)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -3,7 +3,7 @@ REPONAME = "neo-go"
NETMODE ?= "privnet"
BINARY=neo-go
BINARY_PATH=./bin/$(BINARY)$(shell go env GOEXE)
GO_VERSION ?= 1.20
GO_VERSION ?= 1.18
DESTDIR = ""
SYSCONFIGDIR = "/etc"
BINDIR = "/usr/bin"
@ -16,7 +16,7 @@ DC_FILE ?= ".docker/docker-compose.yml" # Single docker-compose for Ubuntu/WSC,
ENV_IMAGE_TAG="env_neo_go_image"
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 --match "v*" 2>/dev/null | sed 's/^v//')"
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)'"
@ -96,10 +96,10 @@ version:
@echo $(VERSION)
gh-docker-vars:
@echo "file=$(D_FILE)"
@echo "version=$(VERSION)"
@echo "repo=$(IMAGE_REPO)"
@echo "suffix=$(IMAGE_SUFFIX)"
@echo "::set-output name=file::$(D_FILE)"
@echo "::set-output name=version::$(VERSION)"
@echo "::set-output name=repo::$(IMAGE_REPO)"
@echo "::set-output name=suffix::$(IMAGE_SUFFIX)"
test:
@go test ./... -cover

View file

@ -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,8 @@
<hr />
[![codecov](https://codecov.io/gh/nspcc-dev/neo-go/branch/master/graph/badge.svg)](https://codecov.io/gh/nspcc-dev/neo-go)
[![GithubWorkflows Tests](https://github.com/nspcc-dev/neo-go/actions/workflows/tests.yml/badge.svg)](https://github.com/nspcc-dev/neo-go/actions/workflows/tests.yml)
[![CircleCI](https://circleci.com/gh/nspcc-dev/neo-go/tree/master.svg?style=shield)](https://circleci.com/gh/nspcc-dev/neo-go/tree/master)
[![GithubWorkflows Tests](https://github.com/nspcc-dev/neo-go/actions/workflows/run_tests.yml/badge.svg)](https://github.com/nspcc-dev/neo-go/actions/workflows/run_tests.yml)
[![Report](https://goreportcard.com/badge/github.com/nspcc-dev/neo-go)](https://goreportcard.com/report/github.com/nspcc-dev/neo-go)
[![GoDoc](https://godoc.org/github.com/nspcc-dev/neo-go?status.svg)](https://godoc.org/github.com/nspcc-dev/neo-go)
![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/nspcc-dev/neo-go?sort=semver)
@ -20,21 +17,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,16 +47,13 @@ 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.16+ 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
@ -68,11 +61,13 @@ To build NeoGo on Windows platform we recommend you to install `make` from [MinG
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
@ -101,9 +96,6 @@ Available network flags:
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:
@ -134,39 +126,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
simple contracts that can be compiled/deployed/run using neo-go compiler, SDK
and a private network. For details on how Go code is translated to Neo VM
bytecode and what you can and can not do in a 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 a wallet on an RPC
node to perform various actions (unless your node provides some service
for the network like consensus or oracle nodes do).
## 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.
@ -180,9 +170,10 @@ 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

View file

@ -7,65 +7,10 @@ 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 0.99.3 (September 2022)
* completely reworked RPC packages with all of the old functionality (but
keeping old APIs for this release as well)
* VM CLI improvements
## 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)
* stable version with full Neo N3 support and useful extensions

View file

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

View file

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

155
cli/candidate_test.go Normal file
View file

@ -0,0 +1,155 @@
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)
// additional parameter
e.RunWithError(t, "neo-go", "wallet", "candidate", "register",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet,
"--address", validatorPriv.Address(),
"error")
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) {
// positional instead of a flag.
e.RunWithError(t, "neo-go", "wallet", "candidate", "vote",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet,
"--address", validatorPriv.Address(),
validatorHex) // not "--candidate hex", but "hex".
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)
// additional argument
e.RunWithError(t, "neo-go", "wallet", "candidate", "unregister",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet,
"--address", validatorPriv.Address(),
"argument")
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")
// Excessive parameters.
e.RunWithError(t, "neo-go", "query", "voter", "--rpc-endpoint", "http://"+e.RPC.Addr, validatorPriv.Address(), validatorPriv.Address())
e.RunWithError(t, "neo-go", "query", "committee", "--rpc-endpoint", "http://"+e.RPC.Addr, "something")
e.RunWithError(t, "neo-go", "query", "candidates", "--rpc-endpoint", "http://"+e.RPC.Addr, "something")
}

View file

@ -24,144 +24,19 @@ 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 {
cosigner, err := parseCosigner(c)
if err != nil {
return nil, fmt.Errorf("failed to parse signer #%d: %w", i, err)
if args.Present() && len(args) > offset {
for i, c := range args[offset:] {
cosigner, err := parseCosigner(c)
if err != nil {
return nil, cli.NewExitError(fmt.Errorf("failed to parse signer #%d: %w", i, err), 1)
}
signers = append(signers, cosigner)
}
signers = append(signers, cosigner)
}
return signers, nil
}
@ -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
@ -324,7 +199,10 @@ func ParseParams(args []string, calledFromMain bool) (int, []smartcontract.Param
// 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()
sender, err := address.StringToUint160(senderAcc.Address)
if err != nil {
return nil, err
}
signersAccounts = append(signersAccounts, actor.SignerAccount{
Signer: transaction.Signer{
Account: sender,

View file

@ -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()},

View file

@ -1,10 +1,9 @@
package smartcontract_test
package main
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strconv"
@ -13,8 +12,6 @@ import (
"github.com/nspcc-dev/neo-go/cli/smartcontract"
"github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/nspcc-dev/neo-go/internal/versionutil"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/core/state"
@ -25,7 +22,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
@ -34,12 +30,9 @@ import (
"gopkg.in/yaml.v3"
)
// Keep contract NEFs consistent between runs.
const _ = versionutil.TestVersion
func TestCalcHash(t *testing.T) {
tmpDir := t.TempDir()
e := testcli.NewExecutor(t, false)
e := newExecutor(t, false)
nefPath := "./testdata/verify.nef"
src, err := os.ReadFile(nefPath)
@ -90,24 +83,27 @@ func TestCalcHash(t *testing.T) {
})
t.Run("valid, uint160", func(t *testing.T) {
e.Run(t, append(cmd, "--sender", sender.StringLE())...)
e.CheckNextLine(t, expected.StringLE())
e.checkNextLine(t, expected.StringLE())
})
t.Run("valid, uint160 with 0x", func(t *testing.T) {
e.Run(t, append(cmd, "--sender", "0x"+sender.StringLE())...)
e.CheckNextLine(t, expected.StringLE())
e.checkNextLine(t, expected.StringLE())
})
t.Run("valid, address", func(t *testing.T) {
e.Run(t, append(cmd, "--sender", address.Uint160ToString(sender))...)
e.CheckNextLine(t, expected.StringLE())
e.checkNextLine(t, expected.StringLE())
})
}
func TestContractBindings(t *testing.T) {
// For proper nef generation.
config.Version = "v0.98.1-test"
// For proper contract init. The actual version as it will be replaced.
smartcontract.ModVersion = "v0.0.0"
tmpDir := t.TempDir()
e := testcli.NewExecutor(t, false)
e := newExecutor(t, false)
ctrPath := filepath.Join(tmpDir, "testcontract")
e.Run(t, "neo-go", "contract", "init", "--name", ctrPath)
@ -146,7 +142,18 @@ func Blocks() []*alias.Block {
cmd = append(cmd, "--in", ctrPath, "--bindings", bindingsPath)
// Replace `pkg/interop` in go.mod to avoid getting an actual module version.
require.NoError(t, updateGoMod(ctrPath, "myimport.com/testcontract", "../../pkg/interop"))
goMod := filepath.Join(ctrPath, "go.mod")
data, err := os.ReadFile(goMod)
require.NoError(t, err)
i := bytes.IndexByte(data, '\n')
data = append([]byte("module myimport.com/testcontract"), data[i:]...)
wd, err := os.Getwd()
require.NoError(t, err)
data = append(data, "\nreplace github.com/nspcc-dev/neo-go/pkg/interop => "...)
data = append(data, filepath.Join(wd, "../pkg/interop")...)
require.NoError(t, os.WriteFile(goMod, data, os.ModePerm))
cmd = append(cmd, "--config", cfgPath,
"--out", filepath.Join(tmpDir, "out.nef"),
@ -156,7 +163,7 @@ func Blocks() []*alias.Block {
e.RunWithError(t, append(cmd, "something")...)
})
e.Run(t, cmd...)
e.CheckEOF(t)
e.checkEOF(t)
require.FileExists(t, bindingsPath)
outPath := filepath.Join(t.TempDir(), "binding.go")
@ -166,9 +173,7 @@ func Blocks() []*alias.Block {
bs, err := os.ReadFile(outPath)
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 testcontract contains wrappers for testcontract contract.
require.Equal(t, `// Package testcontract contains wrappers for testcontract contract.
package testcontract
import (
@ -203,90 +208,15 @@ func ToMap(a []testcontract.MyPair) map[int]string {
`, string(bs))
}
// updateGoMod updates the go.mod file located in the specified directory.
// It sets the module name and replaces the neo-go interop package path with
// the provided one to avoid getting an actual module version.
func updateGoMod(dir, moduleName, neoGoPath string) error {
goModPath := filepath.Join(dir, "go.mod")
data, err := os.ReadFile(goModPath)
if err != nil {
return fmt.Errorf("failed to read go.mod: %w", err)
}
i := bytes.IndexByte(data, '\n')
if i == -1 {
return fmt.Errorf("unexpected go.mod format")
}
updatedData := append([]byte("module "+moduleName), data[i:]...)
wd, err := os.Getwd()
if err != nil {
return fmt.Errorf("failed to get working directory: %w", err)
}
replacementPath := filepath.Join(wd, neoGoPath)
updatedData = append(updatedData, "\nreplace github.com/nspcc-dev/neo-go/pkg/interop => "+replacementPath+" \n"...)
if err := os.WriteFile(goModPath, updatedData, os.ModePerm); err != nil {
return fmt.Errorf("failed to write updated go.mod: %w", err)
}
return nil
}
func TestDynamicWrapper(t *testing.T) {
// For proper contract init. The actual version as it will be replaced.
smartcontract.ModVersion = "v0.0.0"
tmpDir := t.TempDir()
e := testcli.NewExecutor(t, true)
ctrPath := "../smartcontract/testdata"
verifyHash := testcli.DeployContract(t, e, filepath.Join(ctrPath, "verify.go"), filepath.Join(ctrPath, "verify.yml"), testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass)
helperContract := `package testcontract
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
verify "myimport.com/testcontract/bindings"
)
func CallVerifyContract(h interop.Hash160) bool{
contractInstance := verify.NewContract(h)
return contractInstance.Verify()
}`
helperDir := filepath.Join(tmpDir, "helper")
e.Run(t, "neo-go", "contract", "init", "--name", helperDir)
require.NoError(t, updateGoMod(helperDir, "myimport.com/testcontract", "../../pkg/interop"))
require.NoError(t, os.WriteFile(filepath.Join(helperDir, "main.go"), []byte(helperContract), os.ModePerm))
require.NoError(t, os.Mkdir(filepath.Join(helperDir, "bindings"), os.ModePerm))
e.Run(t, "neo-go", "contract", "generate-wrapper",
"--config", filepath.Join(ctrPath, "verify.bindings.yml"), "--manifest", filepath.Join(ctrPath, "verify.manifest.json"),
"--out", filepath.Join(helperDir, "bindings", "testdata.go"))
e.Run(t, "neo-go", "contract", "compile", "--in", filepath.Join(helperDir, "main.go"), "--config", filepath.Join(helperDir, "neo-go.yml"))
helperHash := testcli.DeployContract(t, e, filepath.Join(helperDir, "main.go"), filepath.Join(helperDir, "neo-go.yml"), testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass)
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", "--await", helperHash.StringLE(), "callVerifyContract", verifyHash.StringLE())
tx, _ := e.CheckTxPersisted(t, "Sent invocation transaction ")
aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
require.NoError(t, err)
require.Equal(t, aer[0].Stack[0].Value().(bool), true)
}
func TestContractInitAndCompile(t *testing.T) {
// For proper nef generation.
config.Version = "v0.98.1-test"
// For proper contract init. The actual version as it will be replaced.
smartcontract.ModVersion = "v0.0.0"
tmpDir := t.TempDir()
e := testcli.NewExecutor(t, false)
e := newExecutor(t, false)
t.Run("no path is provided", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "init")
@ -306,8 +236,7 @@ func TestContractInitAndCompile(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "init", "--name", ctrPath)
})
ctrRootPath := filepath.Join(ctrPath, "main")
srcPath := ctrRootPath + ".go"
srcPath := filepath.Join(ctrPath, "main.go")
cfgPath := filepath.Join(ctrPath, "neo-go.yml")
nefPath := filepath.Join(tmpDir, "testcontract.nef")
manifestPath := filepath.Join(tmpDir, "testcontract.manifest.json")
@ -333,7 +262,15 @@ func TestContractInitAndCompile(t *testing.T) {
})
// Replace `pkg/interop` in go.mod to avoid getting an actual module version.
require.NoError(t, updateGoMod(ctrPath, "myimport.com/testcontract", "../../pkg/interop"))
goMod := filepath.Join(ctrPath, "go.mod")
data, err := os.ReadFile(goMod)
require.NoError(t, err)
wd, err := os.Getwd()
require.NoError(t, err)
data = append(data, "\nreplace github.com/nspcc-dev/neo-go/pkg/interop => "...)
data = append(data, filepath.Join(wd, "../pkg/interop")...)
require.NoError(t, os.WriteFile(goMod, data, os.ModePerm))
cmd = append(cmd, "--config", cfgPath)
@ -342,35 +279,25 @@ func TestContractInitAndCompile(t *testing.T) {
})
e.Run(t, cmd...)
e.CheckEOF(t)
e.checkEOF(t)
require.FileExists(t, nefPath)
require.FileExists(t, manifestPath)
t.Run("output hex script with --verbose", func(t *testing.T) {
e.Run(t, append(cmd, "--verbose")...)
e.CheckNextLine(t, "^[0-9a-hA-H]+$")
})
t.Run("autocomplete outputs", func(t *testing.T) {
cfg, err := os.ReadFile(cfgPath)
require.NoError(t, err)
require.NoError(t, os.WriteFile(filepath.Join(ctrPath, "main.yml"), cfg, os.ModePerm))
e.Run(t, "neo-go", "contract", "compile", "--in", srcPath)
defaultNefPath := ctrRootPath + ".nef"
defaultManifestPath := ctrRootPath + ".manifest.json"
defaultBindingsPath := ctrRootPath + ".bindings.yml"
require.FileExists(t, defaultNefPath)
require.FileExists(t, defaultManifestPath)
require.FileExists(t, defaultBindingsPath)
e.checkNextLine(t, "^[0-9a-hA-H]+$")
})
}
// Checks that error is returned if GAS available for test-invoke exceeds
// GAS needed to be consumed.
func TestDeployBigContract(t *testing.T) {
e := testcli.NewExecutorWithConfig(t, true, true, func(c *config.Config) {
e := newExecutorWithConfig(t, true, true, func(c *config.Config) {
c.ApplicationConfiguration.RPC.MaxGasInvoke = fixedn.Fixed8(1)
})
// For proper nef generation.
config.Version = "0.90.0-test"
tmpDir := t.TempDir()
nefName := filepath.Join(tmpDir, "deploy.nef")
@ -380,15 +307,18 @@ func TestDeployBigContract(t *testing.T) {
"--config", "testdata/deploy/neo-go.yml",
"--out", nefName, "--manifest", manifestName)
e.In.WriteString(testcli.ValidatorPass + "\r")
e.In.WriteString("one\r")
e.RunWithError(t, "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--address", validatorAddr,
"--in", nefName, "--manifest", manifestName)
}
func TestContractDeployWithData(t *testing.T) {
eCompile := testcli.NewExecutor(t, false)
eCompile := newExecutor(t, false)
// For proper nef generation.
config.Version = "0.90.0-test"
tmpDir := t.TempDir()
nefName := filepath.Join(tmpDir, "deploy.nef")
@ -398,36 +328,28 @@ func TestContractDeployWithData(t *testing.T) {
"--config", "testdata/deploy/neo-go.yml",
"--out", nefName, "--manifest", manifestName)
deployContract := func(t *testing.T, haveData bool, scope string, await bool) {
e := testcli.NewExecutor(t, true)
deployContract := func(t *testing.T, haveData bool, scope string) {
e := newExecutor(t, true)
cmd := []string{
"neo-go", "contract", "deploy",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
"--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", validatorWallet, "--address", validatorAddr,
"--in", nefName, "--manifest", manifestName,
"--force",
}
if await {
cmd = append(cmd, "--await")
}
if haveData {
cmd = append(cmd, "[", "key1", "12", "key2", "take_me_to_church", "]")
}
if scope != "" {
cmd = append(cmd, "--", testcli.ValidatorAddr+":"+scope)
cmd = append(cmd, "--", validatorAddr+":"+scope)
} else {
scope = "CalledByEntry"
}
e.In.WriteString(testcli.ValidatorPass + "\r")
e.In.WriteString("one\r")
e.Run(t, cmd...)
var tx *transaction.Transaction
if await {
tx, _ = e.CheckAwaitableTxPersisted(t)
} else {
tx, _ = e.CheckTxPersisted(t)
}
tx, _ := e.checkTxPersisted(t, "Sent invocation transaction ")
require.Equal(t, scope, tx.Signers[0].Scopes.String())
if !haveData {
return
@ -440,7 +362,7 @@ func TestContractDeployWithData(t *testing.T) {
require.NoError(t, err)
e.Run(t, "neo-go", "contract", "testinvokefunction",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--rpc-endpoint", "http://"+e.RPC.Addr,
h.StringLE(),
"getValueWithKey", "key1",
)
@ -452,7 +374,7 @@ func TestContractDeployWithData(t *testing.T) {
require.Equal(t, []byte{12}, res.Stack[0].Value())
e.Run(t, "neo-go", "contract", "testinvokefunction",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--rpc-endpoint", "http://"+e.RPC.Addr,
h.StringLE(),
"getValueWithKey", "key2",
)
@ -464,16 +386,16 @@ func TestContractDeployWithData(t *testing.T) {
require.Equal(t, []byte("take_me_to_church"), res.Stack[0].Value())
}
deployContract(t, true, "", false)
deployContract(t, false, "Global", false)
deployContract(t, true, "Global", false)
deployContract(t, false, "", true)
deployContract(t, true, "Global", true)
deployContract(t, true, "", true)
deployContract(t, true, "")
deployContract(t, false, "Global")
deployContract(t, true, "Global")
}
func TestDeployWithSigners(t *testing.T) {
e := testcli.NewExecutor(t, true)
e := newExecutor(t, true)
// For proper nef generation.
config.Version = "0.90.0-test"
tmpDir := t.TempDir()
nefName := filepath.Join(tmpDir, "deploy.nef")
@ -485,59 +407,62 @@ func TestDeployWithSigners(t *testing.T) {
t.Run("missing nef", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--address", validatorAddr,
"--in", "", "--manifest", manifestName)
})
t.Run("missing manifest", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--address", validatorAddr,
"--in", nefName, "--manifest", "")
})
t.Run("corrupted data", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--address", validatorAddr,
"--in", nefName, "--manifest", manifestName,
"[", "str1")
})
t.Run("invalid data", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--address", validatorAddr,
"--in", nefName, "--manifest", manifestName,
"str1", "str2")
})
t.Run("missing wallet", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--address", testcli.ValidatorAddr,
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--address", validatorAddr,
"--in", nefName, "--manifest", manifestName,
"[", "str1", "str2", "]")
})
t.Run("missing RPC", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "deploy",
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
"--wallet", validatorWallet, "--address", validatorAddr,
"--in", nefName, "--manifest", manifestName,
"[", "str1", "str2", "]")
})
e.In.WriteString(testcli.ValidatorPass + "\r")
e.In.WriteString("one\r")
e.Run(t, "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--address", validatorAddr,
"--in", nefName, "--manifest", manifestName,
"--force",
"--", testcli.ValidatorAddr+":Global")
tx, _ := e.CheckTxPersisted(t, "Sent invocation transaction ")
"--", validatorAddr+":Global")
tx, _ := e.checkTxPersisted(t, "Sent invocation transaction ")
require.Equal(t, transaction.Global, tx.Signers[0].Scopes)
}
func TestContractManifestGroups(t *testing.T) {
e := testcli.NewExecutor(t, true)
e := newExecutor(t, true)
// For proper nef generation.
config.Version = "0.90.0-test"
tmpDir := t.TempDir()
_, err := wallet.NewWalletFromFile(testcli.TestWalletPath)
_, err := wallet.NewWalletFromFile(testWalletPath)
require.NoError(t, err)
nefName := filepath.Join(tmpDir, "deploy.nef")
@ -556,70 +481,93 @@ func TestContractManifestGroups(t *testing.T) {
})
t.Run("invalid sender", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
"--wallet", testWalletPath, "--address", testWalletAccount,
"--sender", "not-a-sender")
})
t.Run("invalid NEF file", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
"--sender", testcli.TestWalletAccount, "--nef", tmpDir)
"--wallet", testWalletPath, "--address", testWalletAccount,
"--sender", testWalletAccount, "--nef", tmpDir)
})
t.Run("corrupted NEF file", func(t *testing.T) {
f := filepath.Join(tmpDir, "invalid.nef")
require.NoError(t, os.WriteFile(f, []byte{1, 2, 3}, os.ModePerm))
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
"--sender", testcli.TestWalletAccount, "--nef", f)
"--wallet", testWalletPath, "--address", testWalletAccount,
"--sender", testWalletAccount, "--nef", f)
})
t.Run("invalid manifest file", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
"--sender", testcli.TestWalletAccount, "--nef", nefName,
"--wallet", testWalletPath, "--address", testWalletAccount,
"--sender", testWalletAccount, "--nef", nefName,
"--manifest", tmpDir)
})
t.Run("corrupted manifest file", func(t *testing.T) {
f := filepath.Join(tmpDir, "invalid.manifest.json")
require.NoError(t, os.WriteFile(f, []byte{1, 2, 3}, os.ModePerm))
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
"--sender", testcli.TestWalletAccount, "--nef", nefName,
"--wallet", testWalletPath, "--address", testWalletAccount,
"--sender", testWalletAccount, "--nef", nefName,
"--manifest", f)
})
t.Run("unknown account", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
"--wallet", testcli.TestWalletPath, "--address", util.Uint160{}.StringLE(),
"--sender", testcli.TestWalletAccount, "--nef", nefName,
"--wallet", testWalletPath, "--address", util.Uint160{}.StringLE(),
"--sender", testWalletAccount, "--nef", nefName,
"--manifest", manifestName)
})
cmd := []string{"neo-go", "contract", "manifest", "add-group",
"--nef", nefName, "--manifest", manifestName}
t.Run("excessive parameters", func(t *testing.T) {
e.RunWithError(t, append(cmd, "--wallet", testcli.TestWalletPath,
"--sender", testcli.TestWalletAccount, "--address", testcli.TestWalletAccount, "something")...)
e.RunWithError(t, append(cmd, "--wallet", testWalletPath,
"--sender", testWalletAccount, "--address", testWalletAccount, "something")...)
})
e.In.WriteString("testpass\r")
e.Run(t, append(cmd, "--wallet", testcli.TestWalletPath,
"--sender", testcli.TestWalletAccount, "--address", testcli.TestWalletAccount)...)
e.Run(t, append(cmd, "--wallet", testWalletPath,
"--sender", testWalletAccount, "--address", testWalletAccount)...)
e.In.WriteString("testpass\r") // should override signature with the previous sender
e.Run(t, append(cmd, "--wallet", testcli.TestWalletPath,
"--sender", testcli.ValidatorAddr, "--address", testcli.TestWalletAccount)...)
e.Run(t, append(cmd, "--wallet", testWalletPath,
"--sender", validatorAddr, "--address", testWalletAccount)...)
e.In.WriteString(testcli.ValidatorPass + "\r")
e.In.WriteString("one\r")
e.Run(t, "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--in", nefName, "--manifest", manifestName,
"--force",
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr)
"--wallet", validatorWallet, "--address", validatorAddr)
}
func deployVerifyContract(t *testing.T, e *testcli.Executor) util.Uint160 {
return testcli.DeployContract(t, e, "testdata/verify.go", "testdata/verify.yml", testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass)
func deployVerifyContract(t *testing.T, e *executor) util.Uint160 {
return deployContract(t, e, "testdata/verify.go", "testdata/verify.yml", validatorWallet, validatorAddr, "one")
}
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.Addr,
"--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
}
func TestContract_TestInvokeScript(t *testing.T) {
e := testcli.NewExecutor(t, true)
e := newExecutor(t, true)
tmpDir := t.TempDir()
badNef := filepath.Join(tmpDir, "invalid.nef")
goodNef := filepath.Join(tmpDir, "deploy.nef")
@ -631,22 +579,22 @@ func TestContract_TestInvokeScript(t *testing.T) {
t.Run("missing in", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0])
"--rpc-endpoint", "http://"+e.RPC.Addr)
})
t.Run("unexisting in", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--in", badNef)
})
t.Run("invalid nef", func(t *testing.T) {
require.NoError(t, os.WriteFile(badNef, []byte("qwer"), os.ModePerm))
e.RunWithError(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--in", badNef)
})
t.Run("invalid signers", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--in", goodNef, "--", "not-a-valid-signer")
})
t.Run("no RPC endpoint", func(t *testing.T) {
@ -654,43 +602,13 @@ func TestContract_TestInvokeScript(t *testing.T) {
"--rpc-endpoint", "http://123456789",
"--in", goodNef)
})
t.Run("good", func(t *testing.T) {
e.Run(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--in", goodNef)
})
t.Run("good with hashed signer", func(t *testing.T) {
e.Run(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--in", goodNef, "--", util.Uint160{1, 2, 3}.StringLE())
})
t.Run("good with addressed signer", func(t *testing.T) {
e.Run(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--in", goodNef, "--", address.Uint160ToString(util.Uint160{1, 2, 3}))
})
t.Run("historic, invalid", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--historic", "bad",
"--in", goodNef)
})
t.Run("historic, index", func(t *testing.T) {
e.Run(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--historic", "0",
"--in", goodNef)
})
t.Run("historic, hash", func(t *testing.T) {
e.Run(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--historic", e.Chain.GetHeaderHash(0).StringLE(),
"--in", goodNef)
})
}
func TestComlileAndInvokeFunction(t *testing.T) {
e := testcli.NewExecutor(t, true)
e := newExecutor(t, true)
// For proper nef generation.
config.Version = "0.90.0-test"
tmpDir := t.TempDir()
nefName := filepath.Join(tmpDir, "deploy.nef")
@ -700,21 +618,31 @@ func TestComlileAndInvokeFunction(t *testing.T) {
"--config", "testdata/deploy/neo-go.yml",
"--out", nefName, "--manifest", manifestName)
// Check that it is possible to invoke before deploy.
// This doesn't make much sense, because every method has an offset
// which is contained in the manifest. This should be either removed or refactored.
e.Run(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--in", nefName, "--", util.Uint160{1, 2, 3}.StringLE())
e.Run(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--in", nefName, "--", address.Uint160ToString(util.Uint160{1, 2, 3}))
tmp := t.TempDir()
configPath := filepath.Join(tmp, "config.yaml")
cfg := config.Wallet{
Path: testcli.ValidatorWallet,
Password: testcli.ValidatorPass,
Path: validatorWallet,
Password: "one",
}
yml, err := yaml.Marshal(cfg)
require.NoError(t, err)
require.NoError(t, os.WriteFile(configPath, yml, 0666))
e.Run(t, "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--force",
"--wallet-config", configPath, "--address", testcli.ValidatorAddr,
"--rpc-endpoint", "http://"+e.RPC.Addr, "--force",
"--wallet-config", configPath, "--address", validatorAddr,
"--in", nefName, "--manifest", manifestName)
e.CheckTxPersisted(t, "Sent invocation transaction ")
e.checkTxPersisted(t, "Sent invocation transaction ")
line, err := e.Out.ReadString('\n')
require.NoError(t, err)
line = strings.TrimSpace(strings.TrimPrefix(line, "Contract: "))
@ -728,13 +656,13 @@ func TestComlileAndInvokeFunction(t *testing.T) {
"--manifest", manifestName)
e.Run(t, "neo-go", "contract", "calc-hash",
"--sender", testcli.ValidatorAddr, "--in", nefName,
"--sender", validatorAddr, "--in", nefName,
"--manifest", manifestName)
e.CheckNextLine(t, h.StringLE())
e.checkNextLine(t, h.StringLE())
})
cmd := []string{"neo-go", "contract", "testinvokefunction",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0]}
"--rpc-endpoint", "http://" + e.RPC.Addr}
t.Run("missing hash", func(t *testing.T) {
e.RunWithError(t, cmd...)
})
@ -761,21 +689,18 @@ func TestComlileAndInvokeFunction(t *testing.T) {
e.Run(t, cmd...)
checkGetValueOut := func(str string) {
res := new(result.Invoke)
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException)
require.Len(t, res.Stack, 1)
require.Equal(t, []byte(str), res.Stack[0].Value())
}
checkGetValueOut("on create|sub create")
res := new(result.Invoke)
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException)
require.Len(t, res.Stack, 1)
require.Equal(t, []byte("on create|sub create"), res.Stack[0].Value())
// deploy verification contract
hVerify := deployVerifyContract(t, e)
t.Run("real invoke", func(t *testing.T) {
cmd := []string{"neo-go", "contract", "invokefunction",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0]}
"--rpc-endpoint", "http://" + e.RPC.Addr}
t.Run("missing wallet", func(t *testing.T) {
cmd := append(cmd, h.StringLE(), "getValue")
e.RunWithError(t, cmd...)
@ -795,28 +720,28 @@ func TestComlileAndInvokeFunction(t *testing.T) {
e.RunWithError(t, cmd...)
})
t.Run("non-existent address", func(t *testing.T) {
cmd := append(cmd, "--wallet", testcli.ValidatorWallet,
cmd := append(cmd, "--wallet", validatorWallet,
"--address", random.Uint160().StringLE(),
h.StringLE(), "getValue")
e.RunWithError(t, cmd...)
})
t.Run("invalid password", func(t *testing.T) {
e.In.WriteString("invalid_password\r")
cmd := append(cmd, "--wallet", testcli.ValidatorWallet,
cmd := append(cmd, "--wallet", validatorWallet,
h.StringLE(), "getValue")
e.RunWithError(t, cmd...)
})
t.Run("good: default address", func(t *testing.T) {
e.In.WriteString("one\r")
e.In.WriteString("y\r")
e.Run(t, append(cmd, "--wallet", testcli.ValidatorWallet, h.StringLE(), "getValue")...)
e.Run(t, append(cmd, "--wallet", validatorWallet, h.StringLE(), "getValue")...)
})
t.Run("good: from wallet config", func(t *testing.T) {
e.In.WriteString("y\r")
e.Run(t, append(cmd, "--wallet-config", configPath, h.StringLE(), "getValue")...)
})
cmd = append(cmd, "--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr)
cmd = append(cmd, "--wallet", validatorWallet, "--address", validatorAddr)
t.Run("cancelled", func(t *testing.T) {
e.In.WriteString("one\r")
e.In.WriteString("n\r")
@ -841,13 +766,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
e.In.WriteString("one\r")
e.In.WriteString("y\r")
e.Run(t, append(cmd, h.StringLE(), "getValue",
"--", testcli.ValidatorAddr, hVerify.StringLE())...)
})
t.Run("with await", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, append(cmd, "--force", "--await", h.StringLE(), "getValue")...)
e.CheckAwaitableTxPersisted(t)
"--", validatorAddr, hVerify.StringLE())...)
})
})
@ -855,9 +774,9 @@ func TestComlileAndInvokeFunction(t *testing.T) {
txout := filepath.Join(tmpDir, "test_contract_tx.json")
cmd = []string{"neo-go", "contract", "invokefunction",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
"--rpc-endpoint", "http://" + e.RPC.Addr,
"--out", txout,
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
"--wallet", validatorWallet, "--address", validatorAddr,
}
t.Run("without cosigner", func(t *testing.T) {
@ -868,22 +787,22 @@ func TestComlileAndInvokeFunction(t *testing.T) {
t.Run("with cosigner", func(t *testing.T) {
t.Run("cosigner is sender (none)", func(t *testing.T) {
e.In.WriteString("one\r")
e.RunWithError(t, append(cmd, h.StringLE(), "checkSenderWitness", "--", testcli.ValidatorAddr+":None")...)
e.RunWithError(t, append(cmd, h.StringLE(), "checkSenderWitness", "--", validatorAddr+":None")...)
})
t.Run("cosigner is sender (customcontract)", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, append(cmd, h.StringLE(), "checkSenderWitness", "--", testcli.ValidatorAddr+":CustomContracts:"+h.StringLE())...)
e.Run(t, append(cmd, h.StringLE(), "checkSenderWitness", "--", validatorAddr+":CustomContracts:"+h.StringLE())...)
})
t.Run("cosigner is sender (global)", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, append(cmd, h.StringLE(), "checkSenderWitness", "--", testcli.ValidatorAddr+":Global")...)
e.Run(t, append(cmd, h.StringLE(), "checkSenderWitness", "--", validatorAddr+":Global")...)
})
acc, err := wallet.NewAccount()
require.NoError(t, err)
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
err = acc.ConvertMultisig(2, keys.PublicKeys{acc.PublicKey(), pk.PublicKey()})
err = acc.ConvertMultisig(2, keys.PublicKeys{acc.PrivateKey().PublicKey(), pk.PublicKey()})
require.NoError(t, err)
t.Run("cosigner is multisig account", func(t *testing.T) {
@ -894,7 +813,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
t.Run("good", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, append(cmd, hVerify.StringLE(), "verify", "--", testcli.MultisigAddr)...)
e.Run(t, append(cmd, hVerify.StringLE(), "verify", "--", multisigAddr)...)
})
})
@ -914,7 +833,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
t.Run("test Storage.Find", func(t *testing.T) {
cmd := []string{"neo-go", "contract", "testinvokefunction",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
"--rpc-endpoint", "http://" + e.RPC.Addr,
h.StringLE(), "testFind"}
t.Run("keys only", func(t *testing.T) {
@ -947,12 +866,6 @@ func TestComlileAndInvokeFunction(t *testing.T) {
})
})
var (
hashBeforeUpdate util.Uint256
indexBeforeUpdate uint32
indexAfterUpdate uint32
stateBeforeUpdate util.Uint256
)
t.Run("Update", func(t *testing.T) {
nefName := filepath.Join(tmpDir, "updated.nef")
manifestName := filepath.Join(tmpDir, "updated.manifest.json")
@ -971,61 +884,35 @@ func TestComlileAndInvokeFunction(t *testing.T) {
rawManifest, err := os.ReadFile(manifestName)
require.NoError(t, err)
indexBeforeUpdate = e.Chain.BlockHeight()
hashBeforeUpdate = e.Chain.CurrentHeaderHash()
mptBeforeUpdate, err := e.Chain.GetStateRoot(indexBeforeUpdate)
require.NoError(t, err)
stateBeforeUpdate = mptBeforeUpdate.Root
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,
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--address", validatorAddr,
"--force",
h.StringLE(), "update",
"bytes:"+hex.EncodeToString(rawNef),
"bytes:"+hex.EncodeToString(rawManifest),
)
e.CheckTxPersisted(t, "Sent invocation transaction ")
e.checkTxPersisted(t, "Sent invocation transaction ")
indexAfterUpdate = e.Chain.BlockHeight()
e.In.WriteString("one\r")
e.Run(t, "neo-go", "contract", "testinvokefunction",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--rpc-endpoint", "http://"+e.RPC.Addr,
h.StringLE(), "getValue")
checkGetValueOut("on update|sub update")
})
t.Run("historic", func(t *testing.T) {
t.Run("bad ref", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "testinvokefunction",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--historic", "bad",
h.StringLE(), "getValue")
})
for name, ref := range map[string]string{
"by index": strconv.FormatUint(uint64(indexBeforeUpdate), 10),
"by block hash": hashBeforeUpdate.StringLE(),
"by state hash": stateBeforeUpdate.StringLE(),
} {
t.Run(name, func(t *testing.T) {
e.Run(t, "neo-go", "contract", "testinvokefunction",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--historic", ref,
h.StringLE(), "getValue")
})
checkGetValueOut("on create|sub create")
}
t.Run("updated historic", func(t *testing.T) {
e.Run(t, "neo-go", "contract", "testinvokefunction",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--historic", strconv.FormatUint(uint64(indexAfterUpdate), 10),
h.StringLE(), "getValue")
checkGetValueOut("on update|sub update")
})
res := new(result.Invoke)
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
require.Equal(t, vmstate.Halt.String(), res.State)
require.Len(t, res.Stack, 1)
require.Equal(t, []byte("on update|sub update"), res.Stack[0].Value())
})
}
func TestContractInspect(t *testing.T) {
e := testcli.NewExecutor(t, false)
e := newExecutor(t, false)
// For proper nef generation.
config.Version = "0.90.0-test"
const srcPath = "testdata/deploy/main.go"
tmpDir := t.TempDir()
@ -1056,11 +943,14 @@ func TestContractInspect(t *testing.T) {
func TestCompileExamples(t *testing.T) {
tmpDir := t.TempDir()
const examplePath = "../../examples"
const examplePath = "../examples"
infos, err := os.ReadDir(examplePath)
require.NoError(t, err)
e := testcli.NewExecutor(t, false)
// For proper nef generation.
config.Version = "0.90.0-test"
e := newExecutor(t, false)
for _, info := range infos {
if !info.IsDir() {
@ -1068,10 +958,6 @@ func TestCompileExamples(t *testing.T) {
// there are also a couple of files inside the `/examples` which doesn't need to be compiled
continue
}
if info.Name() == "zkp" {
// A set of special ZKP-related examples, they have their own tests.
continue
}
t.Run(info.Name(), func(t *testing.T) {
infos, err := os.ReadDir(filepath.Join(examplePath, info.Name()))
require.NoError(t, err)
@ -1079,9 +965,6 @@ func TestCompileExamples(t *testing.T) {
outF := filepath.Join(tmpDir, info.Name()+".nef")
manifestF := filepath.Join(tmpDir, info.Name()+".manifest.json")
bindingF := filepath.Join(tmpDir, info.Name()+".binding.yml")
wrapperF := filepath.Join(tmpDir, info.Name()+".go")
rpcWrapperF := filepath.Join(tmpDir, info.Name()+".rpc.go")
cfgName := filterFilename(infos, ".yml")
opts := []string{
@ -1090,7 +973,6 @@ func TestCompileExamples(t *testing.T) {
"--out", outF,
"--manifest", manifestF,
"--config", filepath.Join(examplePath, info.Name(), cfgName),
"--bindings", bindingF,
}
e.Run(t, opts...)
@ -1109,16 +991,6 @@ func TestCompileExamples(t *testing.T) {
require.NotNil(t, m.ABI.GetMethod("put", 1))
require.NotNil(t, m.ABI.GetMethod("put", 2))
}
e.Run(t, "neo-go", "contract", "generate-wrapper",
"--manifest", manifestF,
"--config", bindingF,
"--out", wrapperF,
"--hash", "0x00112233445566778899aabbccddeeff00112233")
e.Run(t, "neo-go", "contract", "generate-rpcwrapper",
"--manifest", manifestF,
"--config", bindingF,
"--out", rpcWrapperF,
"--hash", "0x00112233445566778899aabbccddeeff00112233")
})
}
@ -1148,27 +1020,3 @@ func filterFilename(infos []os.DirEntry, ext string) string {
}
return ""
}
func TestContractCompile_NEFSizeCheck(t *testing.T) {
tmpDir := t.TempDir()
e := testcli.NewExecutor(t, false)
src := `package nefconstraints
var data = "%s"
func Main() string {
return data
}`
data := make([]byte, stackitem.MaxSize-10)
for i := range data {
data[i] = byte('a')
}
in := filepath.Join(tmpDir, "main.go")
cfg := filepath.Join(tmpDir, "main.yml")
require.NoError(t, os.WriteFile(cfg, []byte("name: main"), os.ModePerm))
require.NoError(t, os.WriteFile(in, []byte(fmt.Sprintf(src, data)), os.ModePerm))
e.RunWithError(t, "neo-go", "contract", "compile", "--in", in)
require.NoFileExists(t, filepath.Join(tmpDir, "main.nef"))
}

View file

@ -1,4 +1,4 @@
package server_test
package main
import (
"os"
@ -6,24 +6,19 @@ import (
"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"))
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.Type = "leveldb"
cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath = chainPath
return cfg
}
@ -35,7 +30,9 @@ func TestDBRestoreDump(t *testing.T) {
cfgPath := filepath.Join(tmpDir, "protocol.unit_testnet.yml")
require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm))
e := testcli.NewExecutor(t, false)
// 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",
@ -112,47 +109,3 @@ func TestDBRestoreDump(t *testing.T) {
require.NoError(t, err)
require.Equal(t, d1, d2, "dumps differ")
}
func TestDBDumpRestoreIncremental(t *testing.T) {
tmpDir := t.TempDir()
chainPath := filepath.Join(tmpDir, "neogotestchain")
nonincDump := filepath.Join(tmpDir, "nonincDump.acc")
incDump := filepath.Join(tmpDir, "incDump.acc")
cfg, err := config.LoadFile(filepath.Join("..", "..", "config", "protocol.unit_testnet.yml"))
require.NoError(t, err, "could not load config")
cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB
cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath = chainPath
out, err := yaml.Marshal(cfg)
require.NoError(t, err)
cfgPath := filepath.Join(tmpDir, "protocol.unit_testnet.yml")
require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm))
e := testcli.NewExecutor(t, false)
// Create DB from dump.
e.Run(t, "neo-go", "db", "restore", "--unittest", "--config-path", tmpDir, "--in", inDump)
// Create two dumps: non-incremental and incremental.
dumpBaseArgs := []string{"neo-go", "db", "dump", "--unittest",
"--config-path", tmpDir}
// Dump first 15 blocks to a non-incremental dump.
e.Run(t, append(dumpBaseArgs, "--out", nonincDump, "--count", "15")...)
// Dump second 15 blocks to an incremental dump.
e.Run(t, append(dumpBaseArgs, "--out", incDump, "--start", "15", "--count", "15")...)
// Clean the DB.
require.NoError(t, os.RemoveAll(chainPath))
// Restore chain from two dumps.
restoreBaseArgs := []string{"neo-go", "db", "restore", "--unittest", "--config-path", tmpDir}
// Restore first 15 blocks from non-incremental dump.
e.Run(t, append(restoreBaseArgs, "--in", nonincDump)...)
// Restore second 15 blocks from incremental dump.
e.Run(t, append(restoreBaseArgs, "--in", incDump, "-n", "--count", "15")...)
}

View file

@ -1,11 +1,4 @@
/*
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"
@ -13,13 +6,11 @@ import (
"fmt"
"io"
"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"
@ -41,34 +32,25 @@ 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).
@ -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, *rpcsrv.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,15 +130,14 @@ 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 := network.NewServerConfig(cfg)
serverConfig.UserAgent = fmt.Sprintf(config.UserAgentFormat, "0.98.3-test")
netSrv, err := network.NewServer(serverConfig, chain, chain.GetStateSyncModule(), zap.NewNop())
require.NoError(t, err)
@ -164,16 +145,14 @@ func NewTestChain(t *testing.T, f func(*config.Config), run bool) (*core.Blockch
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: &cfg.ApplicationConfiguration.UnlockWallet,
TimePerBlock: serverConfig.TimePerBlock,
})
require.NoError(t, err)
netSrv.AddConsensusService(cons, cons.OnPayload, cons.OnTransaction)
netSrv.Start()
go netSrv.Start(make(chan error, 1))
errCh := make(chan error, 2)
rpcServer := rpcsrv.New(chain, cfg.ApplicationConfiguration.RPC, netSrv, nil, logger, errCh)
rpcServer.Start()
@ -181,17 +160,17 @@ func NewTestChain(t *testing.T, f func(*config.Config), run bool) (*core.Blockch
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 +178,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,7 +186,7 @@ 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()
@ -223,7 +202,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 +213,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,33 +253,19 @@ 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{
@ -313,7 +278,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)
@ -332,14 +297,7 @@ func (e *Executor) CheckTxPersisted(t *testing.T, prefix ...string) (*transactio
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 +308,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
}

View file

@ -1,15 +1,48 @@
package main
import (
"fmt"
"os"
"runtime"
"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 versionPrinter(c *cli.Context) {
_, _ = fmt.Fprintf(c.App.Writer, "NeoGo\nVersion: %s\nGoVersion: %s\n",
config.Version,
runtime.Version(),
)
}
func newApp() *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
}

14
cli/main_test.go Normal file
View file

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

View file

@ -1,6 +1,7 @@
package wallet_test
package main
import (
"encoding/hex"
"encoding/json"
"fmt"
"math/big"
@ -8,7 +9,6 @@ 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"
@ -24,9 +24,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 +44,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 +54,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 +73,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 +98,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 +117,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,19 +145,19 @@ 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],
"--rpc-endpoint", "http://"+e.RPC.Addr,
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)
@ -193,15 +193,15 @@ func TestSignMultisigTx(t *testing.T) {
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 +216,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 +248,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 +261,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+`)
}
}

View file

@ -1,4 +1,4 @@
package nep_test
package main
import (
"bytes"
@ -13,8 +13,6 @@ import (
"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,7 +46,7 @@ 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
@ -73,12 +68,12 @@ 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) {
@ -94,7 +89,7 @@ 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)
})
})
@ -114,7 +109,7 @@ 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
@ -127,14 +122,14 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// 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 +138,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,16 +161,15 @@ 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()+"\\)")
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 = "("
@ -188,11 +182,11 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
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.checkNextLine(t, "^\\s*Token: "+tokstring+"\\s*$")
e.checkNextLine(t, "^\\s*Amount: 1\\s*$")
e.checkNextLine(t, "^\\s*Updated: [0-9]+\\s*$")
}
e.CheckEOF(t)
e.checkEOF(t)
}
// balance check: by symbol, token is not imported
e.Run(t, append(cmdCheckBalance, "--token", "HASHY")...)
@ -205,7 +199,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// 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())
@ -215,7 +209,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// 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)
@ -227,7 +221,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 +232,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 +247,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 +263,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,12 +273,12 @@ 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())
@ -293,8 +287,8 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
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())...)
@ -302,9 +296,9 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
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 +316,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)
// 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 +337,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)
@ -372,32 +358,14 @@ 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))
}
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 := os.ReadFile(validatorWallet)
require.NoError(t, err)
wall := filepath.Join(tmpDir, "my_wallet.json")
err = os.WriteFile(wall, bytesRead, 0755)
@ -408,18 +376,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,15 +412,15 @@ 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)
e.checkEOF(t)
type idAmount struct {
id string
@ -461,19 +429,19 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.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}
"--address", 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()+"\\)")
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*$")
e.checkNextLine(t, "^\\s*Token: "+o.id+"\\s*$")
e.checkNextLine(t, "^\\s*Amount: "+o.amount+"\\s*$")
e.checkNextLine(t, "^\\s*Updated: [0-9]+\\s*$")
}
e.CheckEOF(t)
e.checkEOF(t)
}
tokz := []idAmount{
{hex.EncodeToString(token1ID), "1"},
@ -481,29 +449,29 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
}
// balance check: by symbol, token is not imported
e.Run(t, append(cmdCheckBalance, "--token", "NFSO")...)
checkBalanceResult(t, testcli.ValidatorAddr, tokz...)
checkBalanceResult(t, validatorAddr, tokz...)
// overall NFSO balance check: by hash, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, testcli.ValidatorAddr, tokz...)
checkBalanceResult(t, validatorAddr, tokz...)
// 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, tokz[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())
// overall balance check: by symbol, ok
e.Run(t, append(cmdCheckBalance, "--token", "NFSO")...)
checkBalanceResult(t, testcli.ValidatorAddr, tokz...)
checkBalanceResult(t, validatorAddr, tokz...)
// 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, tokz[0])
// remove token from wallet
e.In.WriteString("y\r")
@ -512,7 +480,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 +491,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())
@ -559,12 +527,12 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// 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 +541,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, tokz...)
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, tokz[1]) // 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,
@ -657,21 +625,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, tokz[1])
}
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)
}

View file

@ -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,24 @@ 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)
cmd := append(cmdbase, "--address", validatorAddr)
t.Run("excessive parameters", func(t *testing.T) {
e.RunWithError(t, append(cmd, "--token", "NEO", "gas")...)
})
t.Run("NEO", func(t *testing.T) {
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 +47,67 @@ 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.checkNextLine(t, `^\s*Can't find data for "kek" token\s*`)
e.checkEOF(t)
})
t.Run("Bad wallet", func(t *testing.T) {
e.RunWithError(t, append(cmdbalance, "--wallet", "/dev/null")...)
@ -119,18 +115,18 @@ 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)
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 +157,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 +184,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 +212,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 +279,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 +292,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,37 +322,37 @@ 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],
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--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",
@ -375,12 +369,12 @@ 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",

View file

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

View file

@ -6,61 +6,26 @@ package options
import (
"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/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
)
// DefaultTimeout is the default timeout used for RPC requests.
const DefaultTimeout = 10 * time.Second
// RPCEndpointFlag is a long flag name for an 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", Usage: "use private network configuration"},
cli.BoolFlag{Name: "mainnet, m", Usage: "use mainnet network configuration"},
cli.BoolFlag{Name: "testnet, t", Usage: "use testnet network configuration"},
cli.BoolFlag{Name: "unittest", Hidden: true},
}
@ -77,42 +42,7 @@ var RPC = []cli.Flag{
},
}
// 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.
@ -136,9 +66,6 @@ func GetTimeoutContext(ctx *cli.Context) (context.Context, func()) {
if dur == 0 {
dur = DefaultTimeout
}
if !ctx.IsSet("timeout") && ctx.Bool("await") {
dur = DefaultAwaitableTimeout
}
return context.WithTimeout(context.Background(), dur)
}
@ -158,253 +85,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
}

View file

@ -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)

View file

@ -7,6 +7,7 @@ import (
"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"
)
@ -15,10 +16,16 @@ import (
// as an input to `multisig sign`. If a wallet.Account is given and can sign,
// it's signed as well using it.
func InitAndSave(net netmode.Magic, tx *transaction.Transaction, acc *wallet.Account, filename string) error {
scCtx := context.NewParameterContext(context.TransactionType, net, tx)
scCtx := context.NewParameterContext("Neo.Network.P2P.Payloads.Transaction", 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 {
priv := acc.PrivateKey()
pub := priv.PublicKey()
sign := priv.SignHashable(uint32(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)
}
}

View file

@ -3,6 +3,7 @@ package query
import (
"bytes"
"encoding/base64"
"encoding/hex"
"fmt"
"sort"
"strconv"
@ -110,10 +111,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,49 +120,47 @@ 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 == vmstate.Halt)))
}
}
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)
_, _ = 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 {
@ -200,17 +196,15 @@ 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 {
@ -234,7 +228,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
}
@ -305,7 +299,7 @@ func queryVoter(ctx *cli.Context) error {
}
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)))

View file

@ -1,4 +1,4 @@
package query_test
package main
import (
"encoding/base64"
@ -10,7 +10,6 @@ import (
"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"
@ -23,76 +22,78 @@ import (
)
func TestQueryTx(t *testing.T) {
e := testcli.NewExecutorSuspended(t)
e := newExecutorSuspended(t)
w, err := wallet.NewWalletFromFile("../testdata/testwallet.json")
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,
"--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", validatorWallet,
"--to", w.Accounts[0].Address,
"--token", "NEO",
"--from", testcli.ValidatorAddr,
"--from", validatorAddr,
"--force",
}
e.In.WriteString("one\r")
e.Run(t, append(transferArgs, "--amount", "1")...)
line := e.GetNextLine(t)
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]}
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)
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 { _, aerErr := e.Chain.GetAppExecResults(txHash, trigger.Application); return aerErr == nil }, time.Second*2, time.Millisecond*50)
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`)
e.checkNextLine(t, `Hash:\s+`+txHash.StringLE())
e.checkNextLine(t, `OnChain:\s+true`)
_, height, err := e.Chain.GetTransaction(txHash)
_, 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)
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())...)
compareQueryTxVerbose(t, e, tx)
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.Addresses()[0],
"--wallet", testcli.ValidatorWallet,
"--address", testcli.ValidatorAddr,
"--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 ")
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)
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())...)
compareQueryTxVerbose(t, e, tx)
e.compareQueryTxVerbose(t, tx)
})
})
@ -112,43 +113,43 @@ func TestQueryTx(t *testing.T) {
})
}
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`)
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(height).StringLE())
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 == vmstate.Halt))
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, 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)))
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)
e.checkScriptDump(t, n)
if res[0].Execution.VMState != vmstate.Halt {
e.CheckNextLine(t, `Exception:\s+`+regexp.QuoteMeta(res[0].Execution.FaultException))
e.checkNextLine(t, `Exception:\s+`+regexp.QuoteMeta(res[0].Execution.FaultException))
}
e.CheckEOF(t)
e.checkEOF(t)
}
func TestQueryHeight(t *testing.T) {
e := testcli.NewExecutor(t, true)
e := newExecutor(t, true)
args := []string{"neo-go", "query", "height", "--rpc-endpoint", "http://" + e.RPC.Addresses()[0]}
args := []string{"neo-go", "query", "height", "--rpc-endpoint", "http://" + e.RPC.Addr}
e.Run(t, args...)
e.CheckNextLine(t, `^Latest block: [0-9]+$`)
e.CheckNextLine(t, `^Validated state: [0-9]+$`)
e.CheckEOF(t)
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")...)
})

View file

@ -4,8 +4,10 @@ import (
"context"
"errors"
"fmt"
"net/url"
"os"
"os/signal"
"runtime"
"syscall"
"time"
@ -32,13 +34,22 @@ import (
"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", Usage: "path to directory with configuration files"},
}
cfgFlags = append(cfgFlags, options.Network...)
var cfgWithCountFlags = make([]cli.Flag, len(cfgFlags))
copy(cfgWithCountFlags, cfgFlags)
cfgFlags = append(cfgFlags, options.Debug)
cfgFlags = append(cfgFlags, cli.BoolFlag{Name: "debug, d", Usage: "enable debug logging (LOTS of output)"})
cfgWithCountFlags = append(cfgWithCountFlags,
cli.UintFlag{
@ -74,18 +85,11 @@ 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",
UsageText: "neo-go node [--config-path path] [-d] [-p/-m/-t]",
Action: startServer,
Flags: cfgFlags,
},
@ -96,24 +100,17 @@ 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]",
UsageText: "neo-go db dump -o file [-s start] [-c count] [--config-path path] [-p/-m/-t]",
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]",
UsageText: "neo-go db restore -i file [--dump] [-n] [-c count] [--config-path path] [-p/-m/-t]",
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,
},
},
},
}
@ -131,23 +128,103 @@ func newGraceContext() context.Context {
return ctx
}
// 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) {
configPath := "./config"
if argCp := ctx.String("config-path"); argCp != "" {
configPath = argCp
}
return config.Load(configPath, options.GetNetwork(ctx))
}
// 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.
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
}
@ -156,11 +233,11 @@ 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 +274,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 {
@ -212,11 +286,11 @@ 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 +311,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,47 +399,7 @@ 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) {
func mkOracle(config config.OracleConfiguration, magic netmode.Magic, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (*oracle.Oracle, error) {
if !config.Enabled {
return nil, nil
}
@ -385,19 +419,17 @@ 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 config.Wallet, tpb time.Duration, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (consensus.Service, error) {
if len(config.Path) == 0 {
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,
Wallet: &config,
TimePerBlock: tpb,
})
if err != nil {
@ -422,7 +454,7 @@ func mkP2PNotary(config config.P2PNotary, chain *core.Blockchain, serv *network.
}
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 != nil && !errors.Is(err, core.ErrAlreadyExists) {
return fmt.Errorf("can't relay completed notary transaction: hash %s, error: %w", tx.Hash().StringLE(), err)
}
return nil
@ -440,12 +472,11 @@ func startServer(ctx *cli.Context) error {
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 +487,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 {
@ -486,7 +514,7 @@ func startServer(ctx *cli.Context) error {
if err != nil {
return cli.NewExitError(err, 1)
}
dbftSrv, err := mkConsensus(cfg.ApplicationConfiguration.Consensus, serverConfig.TimePerBlock, chain, serv, log)
dbftSrv, err := mkConsensus(cfg.ApplicationConfiguration.UnlockWallet, serverConfig.TimePerBlock, chain, serv, log)
if err != nil {
return cli.NewExitError(err, 1)
}
@ -498,12 +526,9 @@ func startServer(ctx *cli.Context) error {
rpcServer := rpcsrv.New(chain, cfg.ApplicationConfiguration.RPC, serv, oracleSrv, log, errChan)
serv.AddService(&rpcServer)
serv.Start()
go serv.Start(errChan)
if !cfg.ApplicationConfiguration.RPC.StartWhenSynchronized {
// 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()
rpcServer.Start()
}
sigCh := make(chan os.Signal, 1)
@ -523,10 +548,8 @@ Main:
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)
cfgnew, err := getConfigFromContext(ctx)
if err != nil {
log.Warn("can't reread the config file, signal ignored", zap.Error(err))
break // Continue working.
@ -539,41 +562,22 @@ Main:
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.
}
}
configureAddresses(&cfgnew.ApplicationConfiguration)
switch sig {
case sighup:
if newLogLevel != zapcore.InvalidLevel {
logLevel.SetLevel(newLogLevel)
log.Warn("using new logging level", zap.Stringer("level", newLogLevel))
}
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.Start()
}
pprof.ShutDown()
pprof = metrics.NewPprofService(cfgnew.ApplicationConfiguration.Pprof, log)
err = pprof.Start()
if err != nil {
shutdownErr = fmt.Errorf("failed to start Pprof service: %w", err)
cancel() // Fatal error, like for RPC server.
}
go pprof.Start()
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.
}
go prometheus.Start()
case sigusr1:
if oracleSrv != nil {
serv.DelService(oracleSrv)
@ -622,7 +626,7 @@ Main:
serv.DelConsensusService(dbftSrv)
dbftSrv.Shutdown()
}
dbftSrv, err = mkConsensus(cfgnew.ApplicationConfiguration.Consensus, serverConfig.TimePerBlock, chain, serv, log)
dbftSrv, err = mkConsensus(cfgnew.ApplicationConfiguration.UnlockWallet, 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.
@ -646,29 +650,39 @@ Main:
return nil
}
// initBlockChain initializes BlockChain with preselected DB.
func initBlockChain(cfg config.Config, log *zap.Logger) (*core.Blockchain, storage.Store, 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)
}
chain, err := core.NewBlockchain(store, cfg.Blockchain(), 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)
// 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
}
return nil, nil, cli.NewExitError(fmt.Errorf(errText, errArgs...), 1)
}
return chain, store, nil
}
// Logo returns NeoGo logo.
// initBlockChain initializes BlockChain with preselected DB.
func initBlockChain(cfg config.Config, log *zap.Logger) (*core.Blockchain, error) {
store, err := storage.NewStore(cfg.ApplicationConfiguration.DBConfiguration)
if err != nil {
return nil, cli.NewExitError(fmt.Errorf("could not initialize storage: %w", err), 1)
}
chain, err := core.NewBlockchain(store, cfg.ProtocolConfiguration, log)
if err != nil {
return nil, cli.NewExitError(fmt.Errorf("could not initialize blockchain: %w", err), 1)
}
return chain, nil
}
// Logo returns Neo-Go logo.
func Logo() string {
return `
_ ____________ __________

View file

@ -5,17 +5,14 @@ import (
"flag"
"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/stretchr/testify/require"
"github.com/urfave/cli"
"go.uber.org/zap"
"gopkg.in/yaml.v3"
)
@ -31,58 +28,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)
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)
})
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("config-path", "../../config", "")
set.Bool("testnet", true, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
cfg, err := getConfigFromContext(ctx)
require.NoError(t, err)
require.Equal(t, netmode.TestNet, cfg.ProtocolConfiguration.Magic)
}
func TestHandleLoggingParams(t *testing.T) {
@ -92,74 +44,49 @@ 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))
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 +101,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 {
@ -358,35 +285,74 @@ func TestRestoreDB(t *testing.T) {
require.NoError(t, restoreDB(ctx))
}
func TestConfigureAddresses(t *testing.T) {
defaultAddress := "http://localhost:10333"
customAddress := "http://localhost: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: config.RPC{
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: config.BasicService{
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: config.BasicService{
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,
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)
}

View file

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

View file

@ -1,7 +1,6 @@
package server_test
package main
import (
"errors"
"io"
"os"
"path/filepath"
@ -11,22 +10,20 @@ 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"
)
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)
@ -41,7 +38,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...)
@ -77,8 +74,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...)
})
@ -114,7 +110,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 +119,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)
})
}
}

View file

@ -7,77 +7,45 @@ import (
"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"
)
var generatorFlags = []cli.Flag{
cli.StringFlag{
Name: "config, c",
Usage: "Configuration file to use",
},
cli.StringFlag{
Name: "manifest, m",
Required: true,
Usage: "Read contract manifest (*.manifest.json) file",
},
cli.StringFlag{
Name: "out, o",
Required: true,
Usage: "Output of the compiled wrapper",
},
cli.StringFlag{
Name: "hash",
Usage: "Smart-contract hash. If not passed, the wrapper will be designed for dynamic hash usage",
},
}
var generateWrapperCmd = cli.Command{
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,
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>",
Description: ``,
Action: contractGenerateWrapper,
Flags: []cli.Flag{
cli.StringFlag{
Name: "config, c",
Usage: "Configuration file to use",
},
cli.StringFlag{
Name: "manifest, m",
Usage: "Read contract manifest (*.manifest.json) file",
},
cli.StringFlag{
Name: "out, o",
Usage: "Output of the compiled contract",
},
cli.StringFlag{
Name: "hash",
Usage: "Smart-contract hash",
},
},
}
// 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)
}
h, err := util.Uint160DecodeStringLE(strings.TrimPrefix(ctx.String("hash"), "0x"))
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid contract hash: %w", err), 1)
}
m, _, err := readManifest(ctx.String("manifest"), h)
if err != nil {
@ -106,7 +74,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)
}

View file

@ -1,9 +1,7 @@
package smartcontract
import (
"bytes"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
@ -46,15 +44,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{
@ -151,9 +140,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 +161,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 +171,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,7 +182,7 @@ 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)
}
@ -215,8 +197,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.
@ -233,99 +215,6 @@ func MyFunc(in map[int]mycontract.Input) []mycontract.Output {
data, err := os.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 +224,6 @@ func TestGenerateValidPackageName(t *testing.T) {
Name: "get",
Parameters: []manifest.Parameter{},
ReturnType: smartcontract.IntegerType,
Safe: true,
},
)
@ -351,7 +239,7 @@ func TestGenerateValidPackageName(t *testing.T) {
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,
@ -360,9 +248,7 @@ func TestGenerateValidPackageName(t *testing.T) {
data, err := os.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 +261,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) {
@ -558,18 +277,18 @@ func TestGenerate_Errors(t *testing.T) {
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")
checkError(t, "invalid contract hash", "--hash", "xxx")
})
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(), "--hash", util.Uint160{}.StringLE())
})
t.Run("missing manifest file", func(t *testing.T) {
checkError(t, "can't read contract manifest", "--manifest", "notexists", "--hash", util.Uint160{}.StringLE(), "--out", "zzz")
checkError(t, "can't read contract manifest", "--manifest", "notexists", "--hash", util.Uint160{}.StringLE())
})
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, "json: cannot unmarshal array into Go value of type manifest.Manifest", "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE())
})
t.Run("invalid manifest", func(t *testing.T) {
manifestFile := filepath.Join(t.TempDir(), "invalid.json")
@ -577,7 +296,7 @@ func TestGenerate_Errors(t *testing.T) {
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")
checkError(t, "ABI: no methods", "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE())
})
manifestFile := filepath.Join(t.TempDir(), "manifest.json")
@ -595,7 +314,7 @@ func TestGenerate_Errors(t *testing.T) {
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
@ -607,106 +326,6 @@ callflags:
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)
})
}

View file

@ -8,7 +8,6 @@ import (
"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"
@ -38,16 +37,15 @@ func manifestAddGroup(ctx *cli.Context) error {
h := state.CreateContractHash(sender, nf.Checksum, m.Name)
gAcc, w, err := options.GetAccFromContext(ctx)
gAcc, _, err := getAccFromContext(ctx)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't get account to sign group with: %w", err), 1)
}
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
@ -110,7 +108,7 @@ 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 {
if err := m.IsValid(hash); err != nil {
return nil, nil, fmt.Errorf("manifest is invalid: %w", err)
}
return m, manifestBytes, nil

View file

@ -1,6 +1,7 @@
package smartcontract
import (
"encoding/hex"
"errors"
"fmt"
@ -18,7 +19,7 @@ const (
permMethodKey = "methods"
)
func (p permission) MarshalYAML() (any, error) {
func (p permission) MarshalYAML() (interface{}, error) {
m := yaml.Node{Kind: yaml.MappingNode}
switch p.Contract.Type {
case manifest.PermissionWildcard:
@ -27,14 +28,15 @@ func (p permission) MarshalYAML() (any, error) {
&yaml.Node{Kind: yaml.ScalarNode, Value: permHashKey},
&yaml.Node{Kind: yaml.ScalarNode, Value: p.Contract.Value.(util.Uint160).StringLE()})
case manifest.PermissionGroup:
bs := p.Contract.Value.(*keys.PublicKey).Bytes()
m.Content = append(m.Content,
&yaml.Node{Kind: yaml.ScalarNode, Value: permGroupKey},
&yaml.Node{Kind: yaml.ScalarNode, Value: p.Contract.Value.(*keys.PublicKey).StringCompressed()})
&yaml.Node{Kind: yaml.ScalarNode, 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
}
@ -51,8 +53,8 @@ func (p permission) MarshalYAML() (any, error) {
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 +66,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 +104,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 +116,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)

View file

@ -8,20 +8,23 @@ import (
"os"
"path/filepath"
"strings"
"time"
"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"
cliwallet "github.com/nspcc-dev/neo-go/cli/wallet"
"github.com/nspcc-dev/neo-go/pkg/compiler"
"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/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/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"
@ -36,17 +39,44 @@ import (
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")
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")
addressFlag = flags.AddressFlag{
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 or specify wallet config file with the '--wallet-config' flag")
errConflictingWalletFlags = errors.New("--wallet flag conflicts with --wallet-config flag, please, provide one of them to specify wallet location")
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; conflicts with --wallet-config flag",
}
walletConfigFlag = cli.StringFlag{
Name: "wallet-config",
Usage: "path to wallet config to use to get the key for transaction signing; conflicts with --wallet flag",
}
addressFlag = flags.AddressFlag{
Name: addressFlagName,
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 +98,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 +110,17 @@ 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,
walletConfigFlag,
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 +132,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 +139,12 @@ 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,
UsageText: "neo-go contract compile -i path [-o nef] [-v] [-d] [-m manifest] [-c yaml] [--bindings file] [--no-standards] [--no-events] [--no-permissions]",
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,10 +178,6 @@ 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",
@ -190,29 +187,25 @@ func NewCommands() []cli.Command {
{
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 +213,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 +223,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.
@ -309,7 +402,26 @@ func NewCommands() []cli.Command {
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,
walletConfigFlag,
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",
},
},
},
},
},
@ -346,15 +458,13 @@ 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,
},
Name: "args",
Type: smartcontract.ArrayType,
},
},
},
@ -375,9 +485,6 @@ func initSmartContract(ctx *cli.Context) error {
}
gm := []byte("module " + contractName + `
go 1.20
require (
github.com/nspcc-dev/neo-go/pkg/interop ` + ver + `
)`)
@ -406,48 +513,20 @@ 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,
BindingsFile: ctx.String("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 +537,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 {
@ -530,9 +608,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 +629,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,54 +646,60 @@ 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
gas, sysgas fixedn.Fixed8
signersAccounts []actor.SignerAccount
resp *result.Invoke
sender util.Uint160
signAndPush = acc != nil
inv *invoker.Invoker
act *actor.Actor
)
if signAndPush {
gas = flags.Fixed8FromContext(ctx, "gas")
sysgas = flags.Fixed8FromContext(ctx, "sysgas")
signersAccounts, err = cmdargs.GetSignersAccounts(acc, wall, cosigners, transaction.None)
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1)
return sender, cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1)
}
sender = signersAccounts[0].Signer.Account
}
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return sender, err
}
if signAndPush {
_, act, err = options.GetRPCWithActor(gctx, ctx, signersAccounts)
act, err = actor.New(c, signersAccounts)
if err != nil {
return err
}
inv = &act.Invoker
} else {
_, inv, err = options.GetRPCWithInvoker(gctx, ctx, cosigners)
if err != nil {
return err
return sender, cli.NewExitError(fmt.Errorf("failed to create RPC actor: %w", err), 1)
}
}
out := ctx.String("out")
resp, err = inv.Call(script, operation, params...)
// It's a bit easier to keep this as is (not using invoker.Invoker)
// during transition period. Mostly because of the need to convert params
// to []interface{}.
resp, err = c.InvokeFunction(script, operation, params, cosigners)
if err != nil {
return cli.NewExitError(err, 1)
return sender, 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, cli.NewExitError(errText, 1)
}
action := "send"
@ -630,27 +709,55 @@ func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet,
process = "Saving"
}
if !ctx.Bool("force") {
return cli.NewExitError(errText+".\nUse --force flag to "+action+" the transaction anyway.", 1)
return sender, cli.NewExitError(errText+".\nUse --force flag to "+action+" the transaction anyway.", 1)
}
fmt.Fprintln(ctx.App.Writer, errText+".\n"+process+" transaction...")
}
if !signAndPush {
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
} else {
if len(resp.Script) == 0 {
return sender, cli.NewExitError(errors.New("no script returned from the RPC node"), 1)
}
ver := act.GetVersion()
tx, err := act.MakeUnsignedUncheckedRun(resp.Script, resp.GasConsumed+int64(sysgas), nil)
if err != nil {
return sender, cli.NewExitError(fmt.Errorf("failed to create tx: %w", err), 1)
}
tx.NetworkFee += int64(gas)
if out != "" {
// Make a long-lived transaction, it's to be signed manually.
tx.ValidUntilBlock += (ver.Protocol.MaxValidUntilBlockIncrement - uint32(ver.Protocol.ValidatorsCount)) - 2
m := act.GetNetwork()
if err := paramcontext.InitAndSave(m, tx, acc, out); err != nil {
return sender, cli.NewExitError(err, 1)
}
fmt.Fprintln(ctx.App.Writer, tx.Hash().StringLE())
} else {
if !ctx.Bool("force") {
promptTime := time.Now()
err := input.ConfirmTx(ctx.App.Writer, tx)
if err != nil {
return sender, cli.NewExitError(err, 1)
}
waitTime := time.Since(promptTime)
// Compensate for confirmation waiting.
tx.ValidUntilBlock += uint32((waitTime.Milliseconds() / int64(ver.Protocol.MillisecondsPerBlock))) + 1
}
txHash, _, err := act.SignAndSend(tx)
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())
}
}
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 {
@ -676,12 +783,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,10 +809,9 @@ 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"`
Overloads map[string]string `yaml:"overloads,omitempty"`
}
func inspect(ctx *cli.Context) error {
@ -744,6 +850,68 @@ 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")
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 := cliwallet.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()
}
acc, err := getUnlockedAccount(wall, addr, pass)
return acc, wall, err
}
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.PrivateKey() != nil {
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
}
// contractDeploy deploys contract.
func contractDeploy(ctx *cli.Context) error {
nefFile, f, err := readNEFFile(ctx.String("in"))
@ -756,8 +924,16 @@ func contractDeploy(ctx *cli.Context) error {
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 +945,10 @@ func contractDeploy(ctx *cli.Context) error {
appCallParams = append(appCallParams, data[0])
}
acc, w, err := options.GetAccFromContext(ctx)
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 +960,7 @@ func contractDeploy(ctx *cli.Context) error {
}}
}
extErr := invokeWithArgs(ctx, acc, w, management.Hash, "deploy", appCallParams, cosigners)
sender, extErr := invokeWithArgs(ctx, acc, w, management.Hash, "deploy", appCallParams, cosigners)
if extErr != nil {
return extErr
}

View file

@ -1,6 +1,7 @@
package smartcontract
import (
"encoding/hex"
"flag"
"os"
"testing"
@ -50,7 +51,7 @@ func init() {
}
// RuntimeNotify sends runtime notification with "Hello world!" name
func RuntimeNotify(args []any) {
func RuntimeNotify(args []interface{}) {
runtime.Notify(notificationName, args)
}`, string(main))
@ -108,7 +109,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 +118,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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,40 +0,0 @@
package structs
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
)
type Internal struct {
Bool bool
Int int
Bytes []byte
String string
H160 interop.Hash160
H256 interop.Hash256
PK interop.PublicKey
PubKey interop.PublicKey
Sign interop.Signature
ArrOfBytes [][]byte
ArrOfH160 []interop.Hash160
Map map[int][]interop.PublicKey
Struct *Internal
unexportedField int // this one should be exported in the resulting RPC binding.
}
func Contract(mc management.Contract) management.Contract {
return mc
}
func Block(b *ledger.Block) *ledger.Block {
return b
}
func Transaction(t *ledger.Transaction) *ledger.Transaction {
return t
}
func Struct(s *Internal) *Internal {
return s
}

View file

@ -1,3 +0,0 @@
name: "Types"
sourceurl: https://github.com/nspcc-dev/neo-go/
safemethods: ["bool", "int", "bytes", "string", "any", "hash160", "hash256", "publicKey", "signature", "bools", "ints", "bytess", "strings", "hash160s", "hash256s", "publicKeys", "signatures", "aAAStrings", "maps", "crazyMaps", "anyMaps", "unnamedStructs", "unnamedStructsX"]

View file

@ -1,444 +0,0 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package types contains RPC wrappers for Types contract.
package types
import (
"errors"
"fmt"
"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/unwrap"
"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
}
// UnnamedX is a contract-specific unnamedX type used by its methods.
type UnnamedX struct {
I *big.Int
B bool
}
// 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}
}
// AAAStrings invokes `aAAStrings` method of contract.
func (c *ContractReader) AAAStrings(s [][][]string) ([][][]string, error) {
return func(item stackitem.Item, err error) ([][][]string, error) {
if err != nil {
return nil, err
}
return func(item stackitem.Item) ([][][]string, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([][][]string, len(arr))
for i := range res {
res[i], err = func(item stackitem.Item) ([][]string, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([][]string, len(arr))
for i := range res {
res[i], err = func(item stackitem.Item) ([]string, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([]string, len(arr))
for i := range res {
res[i], 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[i])
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[i])
if err != nil {
return nil, fmt.Errorf("item %d: %w", i, err)
}
}
return res, nil
}(item)
}(unwrap.Item(c.invoker.Call(c.hash, "aAAStrings", s)))
}
// Any invokes `any` method of contract.
func (c *ContractReader) Any(a any) (any, error) {
return func(item stackitem.Item, err error) (any, error) {
if err != nil {
return nil, err
}
return item.Value(), error(nil)
}(unwrap.Item(c.invoker.Call(c.hash, "any", a)))
}
// AnyMaps invokes `anyMaps` method of contract.
func (c *ContractReader) AnyMaps(m map[*big.Int]any) (map[*big.Int]any, error) {
return func(item stackitem.Item, err error) (map[*big.Int]any, error) {
if err != nil {
return nil, err
}
return func(item stackitem.Item) (map[*big.Int]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[*big.Int]any)
for i := range m {
k, err := m[i].Key.TryInteger()
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
}(item)
}(unwrap.Item(c.invoker.Call(c.hash, "anyMaps", m)))
}
// Bool invokes `bool` method of contract.
func (c *ContractReader) Bool(b bool) (bool, error) {
return unwrap.Bool(c.invoker.Call(c.hash, "bool", b))
}
// Bools invokes `bools` method of contract.
func (c *ContractReader) Bools(b []bool) ([]bool, error) {
return unwrap.ArrayOfBools(c.invoker.Call(c.hash, "bools", b))
}
// Bytes invokes `bytes` method of contract.
func (c *ContractReader) Bytes(b []byte) ([]byte, error) {
return unwrap.Bytes(c.invoker.Call(c.hash, "bytes", b))
}
// Bytess invokes `bytess` method of contract.
func (c *ContractReader) Bytess(b [][]byte) ([][]byte, error) {
return unwrap.ArrayOfBytes(c.invoker.Call(c.hash, "bytess", b))
}
// CrazyMaps invokes `crazyMaps` method of contract.
func (c *ContractReader) CrazyMaps(m map[*big.Int][]map[string][]util.Uint160) (map[*big.Int][]map[string][]util.Uint160, error) {
return func(item stackitem.Item, err error) (map[*big.Int][]map[string][]util.Uint160, error) {
if err != nil {
return nil, err
}
return 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
}(item)
}(unwrap.Item(c.invoker.Call(c.hash, "crazyMaps", m)))
}
// Hash160 invokes `hash160` method of contract.
func (c *ContractReader) Hash160(h util.Uint160) (util.Uint160, error) {
return unwrap.Uint160(c.invoker.Call(c.hash, "hash160", h))
}
// Hash160s invokes `hash160s` method of contract.
func (c *ContractReader) Hash160s(h []util.Uint160) ([]util.Uint160, error) {
return unwrap.ArrayOfUint160(c.invoker.Call(c.hash, "hash160s", h))
}
// Hash256 invokes `hash256` method of contract.
func (c *ContractReader) Hash256(h util.Uint256) (util.Uint256, error) {
return unwrap.Uint256(c.invoker.Call(c.hash, "hash256", h))
}
// Hash256s invokes `hash256s` method of contract.
func (c *ContractReader) Hash256s(h []util.Uint256) ([]util.Uint256, error) {
return unwrap.ArrayOfUint256(c.invoker.Call(c.hash, "hash256s", h))
}
// Int invokes `int` method of contract.
func (c *ContractReader) Int(i *big.Int) (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "int", i))
}
// Ints invokes `ints` method of contract.
func (c *ContractReader) Ints(i []*big.Int) ([]*big.Int, error) {
return unwrap.ArrayOfBigInts(c.invoker.Call(c.hash, "ints", i))
}
// Maps invokes `maps` method of contract.
func (c *ContractReader) Maps(m map[string]string) (map[string]string, error) {
return func(item stackitem.Item, err error) (map[string]string, error) {
if err != nil {
return nil, err
}
return func(item stackitem.Item) (map[string]string, 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]string)
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) (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].Value)
if err != nil {
return nil, fmt.Errorf("value %d: %w", i, err)
}
res[k] = v
}
return res, nil
}(item)
}(unwrap.Item(c.invoker.Call(c.hash, "maps", m)))
}
// PublicKey invokes `publicKey` method of contract.
func (c *ContractReader) PublicKey(k *keys.PublicKey) (*keys.PublicKey, error) {
return unwrap.PublicKey(c.invoker.Call(c.hash, "publicKey", k))
}
// PublicKeys invokes `publicKeys` method of contract.
func (c *ContractReader) PublicKeys(k keys.PublicKeys) (keys.PublicKeys, error) {
return unwrap.ArrayOfPublicKeys(c.invoker.Call(c.hash, "publicKeys", k))
}
// Signature invokes `signature` method of contract.
func (c *ContractReader) Signature(s []byte) ([]byte, error) {
return unwrap.Bytes(c.invoker.Call(c.hash, "signature", s))
}
// Signatures invokes `signatures` method of contract.
func (c *ContractReader) Signatures(s [][]byte) ([][]byte, error) {
return unwrap.ArrayOfBytes(c.invoker.Call(c.hash, "signatures", s))
}
// String invokes `string` method of contract.
func (c *ContractReader) String(s string) (string, error) {
return unwrap.UTF8String(c.invoker.Call(c.hash, "string", s))
}
// Strings invokes `strings` method of contract.
func (c *ContractReader) Strings(s []string) ([]string, error) {
return unwrap.ArrayOfUTF8Strings(c.invoker.Call(c.hash, "strings", s))
}
// UnnamedStructs invokes `unnamedStructs` method of contract.
func (c *ContractReader) UnnamedStructs() (*Unnamed, error) {
return itemToUnnamed(unwrap.Item(c.invoker.Call(c.hash, "unnamedStructs")))
}
// UnnamedStructsX invokes `unnamedStructsX` method of contract.
func (c *ContractReader) UnnamedStructsX() (*UnnamedX, error) {
return itemToUnnamedX(unwrap.Item(c.invoker.Call(c.hash, "unnamedStructsX")))
}
// 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) != 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
}
// 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) != 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
}

View file

@ -1,440 +0,0 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package types contains RPC wrappers for Types contract.
package types
import (
"errors"
"fmt"
"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/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
"unicode/utf8"
)
// Unnamed is a contract-specific unnamed type used by its methods.
type Unnamed struct {
I *big.Int
}
// UnnamedX is a contract-specific unnamedX type used by its methods.
type UnnamedX struct {
I *big.Int
B bool
}
// 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 provided contract hash and the given Invoker.
func NewReader(invoker Invoker, hash util.Uint160) *ContractReader {
return &ContractReader{invoker, hash}
}
// AAAStrings invokes `aAAStrings` method of contract.
func (c *ContractReader) AAAStrings(s [][][]string) ([][][]string, error) {
return func(item stackitem.Item, err error) ([][][]string, error) {
if err != nil {
return nil, err
}
return func(item stackitem.Item) ([][][]string, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([][][]string, len(arr))
for i := range res {
res[i], err = func(item stackitem.Item) ([][]string, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([][]string, len(arr))
for i := range res {
res[i], err = func(item stackitem.Item) ([]string, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([]string, len(arr))
for i := range res {
res[i], 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[i])
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[i])
if err != nil {
return nil, fmt.Errorf("item %d: %w", i, err)
}
}
return res, nil
}(item)
}(unwrap.Item(c.invoker.Call(c.hash, "aAAStrings", s)))
}
// Any invokes `any` method of contract.
func (c *ContractReader) Any(a any) (any, error) {
return func(item stackitem.Item, err error) (any, error) {
if err != nil {
return nil, err
}
return item.Value(), error(nil)
}(unwrap.Item(c.invoker.Call(c.hash, "any", a)))
}
// AnyMaps invokes `anyMaps` method of contract.
func (c *ContractReader) AnyMaps(m map[*big.Int]any) (map[*big.Int]any, error) {
return func(item stackitem.Item, err error) (map[*big.Int]any, error) {
if err != nil {
return nil, err
}
return func(item stackitem.Item) (map[*big.Int]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[*big.Int]any)
for i := range m {
k, err := m[i].Key.TryInteger()
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
}(item)
}(unwrap.Item(c.invoker.Call(c.hash, "anyMaps", m)))
}
// Bool invokes `bool` method of contract.
func (c *ContractReader) Bool(b bool) (bool, error) {
return unwrap.Bool(c.invoker.Call(c.hash, "bool", b))
}
// Bools invokes `bools` method of contract.
func (c *ContractReader) Bools(b []bool) ([]bool, error) {
return unwrap.ArrayOfBools(c.invoker.Call(c.hash, "bools", b))
}
// Bytes invokes `bytes` method of contract.
func (c *ContractReader) Bytes(b []byte) ([]byte, error) {
return unwrap.Bytes(c.invoker.Call(c.hash, "bytes", b))
}
// Bytess invokes `bytess` method of contract.
func (c *ContractReader) Bytess(b [][]byte) ([][]byte, error) {
return unwrap.ArrayOfBytes(c.invoker.Call(c.hash, "bytess", b))
}
// CrazyMaps invokes `crazyMaps` method of contract.
func (c *ContractReader) CrazyMaps(m map[*big.Int][]map[string][]util.Uint160) (map[*big.Int][]map[string][]util.Uint160, error) {
return func(item stackitem.Item, err error) (map[*big.Int][]map[string][]util.Uint160, error) {
if err != nil {
return nil, err
}
return 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
}(item)
}(unwrap.Item(c.invoker.Call(c.hash, "crazyMaps", m)))
}
// Hash160 invokes `hash160` method of contract.
func (c *ContractReader) Hash160(h util.Uint160) (util.Uint160, error) {
return unwrap.Uint160(c.invoker.Call(c.hash, "hash160", h))
}
// Hash160s invokes `hash160s` method of contract.
func (c *ContractReader) Hash160s(h []util.Uint160) ([]util.Uint160, error) {
return unwrap.ArrayOfUint160(c.invoker.Call(c.hash, "hash160s", h))
}
// Hash256 invokes `hash256` method of contract.
func (c *ContractReader) Hash256(h util.Uint256) (util.Uint256, error) {
return unwrap.Uint256(c.invoker.Call(c.hash, "hash256", h))
}
// Hash256s invokes `hash256s` method of contract.
func (c *ContractReader) Hash256s(h []util.Uint256) ([]util.Uint256, error) {
return unwrap.ArrayOfUint256(c.invoker.Call(c.hash, "hash256s", h))
}
// Int invokes `int` method of contract.
func (c *ContractReader) Int(i *big.Int) (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "int", i))
}
// Ints invokes `ints` method of contract.
func (c *ContractReader) Ints(i []*big.Int) ([]*big.Int, error) {
return unwrap.ArrayOfBigInts(c.invoker.Call(c.hash, "ints", i))
}
// Maps invokes `maps` method of contract.
func (c *ContractReader) Maps(m map[string]string) (map[string]string, error) {
return func(item stackitem.Item, err error) (map[string]string, error) {
if err != nil {
return nil, err
}
return func(item stackitem.Item) (map[string]string, 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]string)
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) (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].Value)
if err != nil {
return nil, fmt.Errorf("value %d: %w", i, err)
}
res[k] = v
}
return res, nil
}(item)
}(unwrap.Item(c.invoker.Call(c.hash, "maps", m)))
}
// PublicKey invokes `publicKey` method of contract.
func (c *ContractReader) PublicKey(k *keys.PublicKey) (*keys.PublicKey, error) {
return unwrap.PublicKey(c.invoker.Call(c.hash, "publicKey", k))
}
// PublicKeys invokes `publicKeys` method of contract.
func (c *ContractReader) PublicKeys(k keys.PublicKeys) (keys.PublicKeys, error) {
return unwrap.ArrayOfPublicKeys(c.invoker.Call(c.hash, "publicKeys", k))
}
// Signature invokes `signature` method of contract.
func (c *ContractReader) Signature(s []byte) ([]byte, error) {
return unwrap.Bytes(c.invoker.Call(c.hash, "signature", s))
}
// Signatures invokes `signatures` method of contract.
func (c *ContractReader) Signatures(s [][]byte) ([][]byte, error) {
return unwrap.ArrayOfBytes(c.invoker.Call(c.hash, "signatures", s))
}
// String invokes `string` method of contract.
func (c *ContractReader) String(s string) (string, error) {
return unwrap.UTF8String(c.invoker.Call(c.hash, "string", s))
}
// Strings invokes `strings` method of contract.
func (c *ContractReader) Strings(s []string) ([]string, error) {
return unwrap.ArrayOfUTF8Strings(c.invoker.Call(c.hash, "strings", s))
}
// UnnamedStructs invokes `unnamedStructs` method of contract.
func (c *ContractReader) UnnamedStructs() (*Unnamed, error) {
return itemToUnnamed(unwrap.Item(c.invoker.Call(c.hash, "unnamedStructs")))
}
// UnnamedStructsX invokes `unnamedStructsX` method of contract.
func (c *ContractReader) UnnamedStructsX() (*UnnamedX, error) {
return itemToUnnamedX(unwrap.Item(c.invoker.Call(c.hash, "unnamedStructsX")))
}
// 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) != 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
}
// 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) != 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
}

View file

@ -1,103 +0,0 @@
package types
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
)
func Bool(b bool) bool {
return false
}
func Int(i int) int {
return 0
}
func Bytes(b []byte) []byte {
return nil
}
func String(s string) string {
return ""
}
func Any(a any) any {
return nil
}
func Hash160(h interop.Hash160) interop.Hash160 {
return nil
}
func Hash256(h interop.Hash256) interop.Hash256 {
return nil
}
func PublicKey(k interop.PublicKey) interop.PublicKey {
return nil
}
func Signature(s interop.Signature) interop.Signature {
return nil
}
func Bools(b []bool) []bool {
return nil
}
func Ints(i []int) []int {
return nil
}
func Bytess(b [][]byte) [][]byte {
return nil
}
func Strings(s []string) []string {
return nil
}
func Hash160s(h []interop.Hash160) []interop.Hash160 {
return nil
}
func Hash256s(h []interop.Hash256) []interop.Hash256 {
return nil
}
func PublicKeys(k []interop.PublicKey) []interop.PublicKey {
return nil
}
func Signatures(s []interop.Signature) []interop.Signature {
return nil
}
func AAAStrings(s [][][]string) [][][]string {
return s
}
func Maps(m map[string]string) map[string]string {
return m
}
func CrazyMaps(m map[int][]map[string][]interop.Hash160) map[int][]map[string][]interop.Hash160 {
return m
}
func AnyMaps(m map[int]any) map[int]any {
return m
}
func UnnamedStructs() struct{ I int } {
return struct{ I int }{I: 123}
}
func UnnamedStructsX() struct {
I int
B bool
} {
return struct {
I int
B bool
}{I: 123, B: true}
}

View file

@ -1,297 +0,0 @@
package: testdata
hash: "0x0000000000000000000000000000000000000000"
overrides:
burnGas.gas: int
call: any
call.args: '[]any'
call.f: any
call.method: string
call.scriptHash: github.com/nspcc-dev/neo-go/pkg/interop.Hash160
callWithToken: any
callWithToken.args: '[]any'
callWithToken.flags: int
callWithToken.method: string
callWithToken.scriptHash: string
callWithTokenNoRet.args: '[]any'
callWithTokenNoRet.flags: int
callWithTokenNoRet.method: string
callWithTokenNoRet.scriptHash: string
checkWitness: bool
checkWitness.hashOrKey: '[]byte'
createMultisigAccount: '[]byte'
createMultisigAccount.m: int
createMultisigAccount.pubs: '[]github.com/nspcc-dev/neo-go/pkg/interop.PublicKey'
createStandardAccount: '[]byte'
createStandardAccount.pub: github.com/nspcc-dev/neo-go/pkg/interop.PublicKey
currentHash: github.com/nspcc-dev/neo-go/pkg/interop.Hash256
currentIndex: int
currentSigners: '[]github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.TransactionSigner'
equals: bool
equals.b: any
gasLeft: int
getAddressVersion: int
getBlock: '*github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.Block'
getBlock.indexOrHash: any
getCallFlags: any
getCallingScriptHash: github.com/nspcc-dev/neo-go/pkg/interop.Hash160
getEntryScriptHash: github.com/nspcc-dev/neo-go/pkg/interop.Hash160
getExecutingScriptHash: github.com/nspcc-dev/neo-go/pkg/interop.Hash160
getInvocationCounter: int
getNetwork: int
getNotifications: '[][]any'
getNotifications.h: github.com/nspcc-dev/neo-go/pkg/interop.Hash160
getRandom: int
getScriptContainer: '*github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.Transaction'
getTime: int
getTransaction: '*github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.Transaction'
getTransaction.hash: github.com/nspcc-dev/neo-go/pkg/interop.Hash256
getTransactionFromBlock: '*github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.Transaction'
getTransactionFromBlock.indexOrHash: any
getTransactionFromBlock.txIndex: int
getTransactionHeight: int
getTransactionHeight.hash: github.com/nspcc-dev/neo-go/pkg/interop.Hash256
getTransactionSigners: '[]github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.TransactionSigner'
getTransactionSigners.hash: github.com/nspcc-dev/neo-go/pkg/interop.Hash256
getTransactionVMState: int
getTransactionVMState.hash: github.com/nspcc-dev/neo-go/pkg/interop.Hash256
getTrigger: int
loadScript: any
loadScript.args: '[]any'
loadScript.f: any
loadScript.script: '[]byte'
log.message: string
notify.args: '[]any'
notify.name: string
onNEP11Payment.amount: int
onNEP11Payment.data: any
onNEP11Payment.from: github.com/nspcc-dev/neo-go/pkg/interop.Hash160
onNEP11Payment.token: '[]byte'
onNEP17Payment.amount: int
onNEP17Payment.data: any
onNEP17Payment.from: github.com/nspcc-dev/neo-go/pkg/interop.Hash160
opcode0NoReturn.op: string
opcode1: any
opcode1NoReturn.arg: any
opcode1NoReturn.op: string
opcode1.arg: any
opcode1.op: string
opcode2: any
opcode2NoReturn.arg1: any
opcode2NoReturn.arg2: any
opcode2NoReturn.op: string
opcode2.arg1: any
opcode2.arg2: any
opcode2.op: string
opcode3: any
opcode3.arg1: any
opcode3.arg2: any
opcode3.arg3: any
opcode3.op: string
platform: '[]byte'
syscall0: any
syscall0NoReturn.name: string
syscall0.name: string
syscall1: any
syscall1NoReturn.arg: any
syscall1NoReturn.name: string
syscall1.arg: any
syscall1.name: string
syscall2: any
syscall2NoReturn.arg1: any
syscall2NoReturn.arg2: any
syscall2NoReturn.name: string
syscall2.arg1: any
syscall2.arg2: any
syscall2.name: string
syscall3: any
syscall3NoReturn.arg1: any
syscall3NoReturn.arg2: any
syscall3NoReturn.arg3: any
syscall3NoReturn.name: string
syscall3.arg1: any
syscall3.arg2: any
syscall3.arg3: any
syscall3.name: string
syscall4: any
syscall4NoReturn.arg1: any
syscall4NoReturn.arg2: any
syscall4NoReturn.arg3: any
syscall4NoReturn.arg4: any
syscall4NoReturn.name: string
syscall4.arg1: any
syscall4.arg2: any
syscall4.arg3: any
syscall4.arg4: any
syscall4.name: string
toBlockSR: '*github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.BlockSR'
verify: bool
namedtypes:
ledger.Block:
base: Array
name: ledger.Block
fields:
- field: Hash
base: Hash256
- field: Version
base: Integer
- field: PrevHash
base: Hash256
- field: MerkleRoot
base: Hash256
- field: Timestamp
base: Integer
- field: Nonce
base: Integer
- field: Index
base: Integer
- field: NextConsensus
base: Hash160
- field: TransactionsLength
base: Integer
ledger.BlockSR:
base: Array
name: ledger.BlockSR
fields:
- field: Hash
base: Hash256
- field: Version
base: Integer
- field: PrevHash
base: Hash256
- field: MerkleRoot
base: Hash256
- field: Timestamp
base: Integer
- field: Nonce
base: Integer
- field: Index
base: Integer
- field: NextConsensus
base: Hash160
- field: TransactionsLength
base: Integer
- field: PrevStateRoot
base: Hash256
ledger.Transaction:
base: Array
name: ledger.Transaction
fields:
- field: Hash
base: Hash256
- field: Version
base: Integer
- field: Nonce
base: Integer
- field: Sender
base: Hash160
- field: SysFee
base: Integer
- field: NetFee
base: Integer
- field: ValidUntilBlock
base: Integer
- field: Script
base: ByteArray
ledger.TransactionSigner:
base: Array
name: ledger.TransactionSigner
fields:
- field: Account
base: Hash160
- field: Scopes
base: Integer
- field: AllowedContracts
base: Array
value:
base: Hash160
- field: AllowedGroups
base: Array
value:
base: PublicKey
- field: Rules
base: Array
value:
base: Array
name: ledger.WitnessRule
ledger.WitnessCondition:
base: Array
name: ledger.WitnessCondition
fields:
- field: Type
base: Integer
- field: Value
base: Any
ledger.WitnessRule:
base: Array
name: ledger.WitnessRule
fields:
- field: Action
base: Integer
- field: Condition
base: Array
name: ledger.WitnessCondition
types:
call.args:
base: Array
value:
base: Any
call.f:
base: InteropInterface
interface: iterator
callWithToken.args:
base: Array
value:
base: Any
callWithTokenNoRet.args:
base: Array
value:
base: Any
createMultisigAccount.pubs:
base: Array
value:
base: PublicKey
currentSigners:
base: Array
value:
base: Array
name: ledger.TransactionSigner
getBlock:
base: Array
name: ledger.Block
getCallFlags:
base: InteropInterface
interface: iterator
getNotifications:
base: Array
value:
base: Array
value:
base: Any
getScriptContainer:
base: Array
name: ledger.Transaction
getTransaction:
base: Array
name: ledger.Transaction
getTransactionFromBlock:
base: Array
name: ledger.Transaction
getTransactionSigners:
base: Array
value:
base: Array
name: ledger.TransactionSigner
loadScript.args:
base: Array
value:
base: Any
loadScript.f:
base: InteropInterface
interface: iterator
notify.args:
base: Array
value:
base: Any
toBlockSR:
base: Array
name: ledger.BlockSR

View file

@ -1,147 +0,0 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package verify contains RPC wrappers for verify contract.
package verify
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/smartcontract"
"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}
// HelloWorldEvent represents "Hello world!" event emitted by the contract.
type HelloWorldEvent struct {
Args []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}
}
func (c *Contract) scriptForVerify() ([]byte, error) {
return smartcontract.CreateCallWithAssertScript(c.hash, "verify")
}
// Verify creates a transaction invoking `verify` 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) Verify() (util.Uint256, uint32, error) {
script, err := c.scriptForVerify()
if err != nil {
return util.Uint256{}, 0, err
}
return c.actor.SendRun(script)
}
// VerifyTransaction creates a transaction invoking `verify` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) VerifyTransaction() (*transaction.Transaction, error) {
script, err := c.scriptForVerify()
if err != nil {
return nil, err
}
return c.actor.MakeRun(script)
}
// VerifyUnsigned creates a transaction invoking `verify` 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) VerifyUnsigned() (*transaction.Transaction, error) {
script, err := c.scriptForVerify()
if err != nil {
return nil, err
}
return c.actor.MakeUnsignedRun(script, nil)
}
// HelloWorldEventsFromApplicationLog retrieves a set of all emitted events
// with "Hello world!" name from the provided [result.ApplicationLog].
func HelloWorldEventsFromApplicationLog(log *result.ApplicationLog) ([]*HelloWorldEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*HelloWorldEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "Hello world!" {
continue
}
event := new(HelloWorldEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize HelloWorldEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to HelloWorldEvent or
// returns an error if it's not possible to do to so.
func (e *HelloWorldEvent) 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.Args, 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 Args: %w", err)
}
return nil
}

View file

@ -1,79 +0,0 @@
{
"groups" : [],
"extra" : null,
"supportedstandards" : [],
"name" : "verify",
"trusts" : [],
"permissions" : [
{
"methods" : "*",
"contract" : "*"
}
],
"abi" : {
"methods" : [
{
"safe" : false,
"offset" : 0,
"parameters" : [],
"name" : "verify",
"returntype" : "Boolean"
},
{
"returntype" : "Void",
"safe" : false,
"offset" : 5,
"parameters" : [
{
"type" : "Hash160",
"name" : "from"
},
{
"type" : "Integer",
"name" : "amount"
},
{
"type" : "Any",
"name" : "data"
}
],
"name" : "onNEP17Payment"
},
{
"returntype" : "Void",
"safe" : false,
"offset" : 5,
"parameters" : [
{
"type" : "Hash160",
"name" : "from"
},
{
"type" : "Integer",
"name" : "amount"
},
{
"type" : "ByteArray",
"name" : "tokenid"
},
{
"type" : "Any",
"name" : "data"
}
],
"name" : "onNEP11Payment"
}
],
"events" : [
{
"parameters" : [
{
"type" : "Array",
"name" : "args"
}
],
"name" : "Hello world!"
}
]
}
}

View file

@ -1,7 +1,7 @@
package deploy
import (
"github.com/nspcc-dev/neo-go/cli/smartcontract/testdata/deploy/sub"
"github.com/nspcc-dev/neo-go/cli/testdata/deploy/sub"
"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"
@ -13,7 +13,7 @@ var key = "key"
const mgmtKey = "mgmt"
func _deploy(data any, isUpdate bool) {
func _deploy(data interface{}, isUpdate bool) {
var value string
ctx := storage.GetContext()
@ -25,7 +25,7 @@ func _deploy(data any, isUpdate bool) {
storage.Put(ctx, mgmtKey, sh)
if data != nil {
arr := data.([]any)
arr := data.([]interface{})
for i := 0; i < len(arr)-1; i += 2 {
storage.Put(ctx, arr[i], arr[i+1])
}
@ -70,12 +70,12 @@ func GetValueWithKey(key string) string {
}
// TestFind finds items with the specified prefix.
func TestFind(f storage.FindFlags) []any {
func TestFind(f storage.FindFlags) []interface{} {
ctx := storage.GetContext()
storage.Put(ctx, "findkey1", "value1")
storage.Put(ctx, "findkey2", "value2")
var result []any
var result []interface{}
iter := storage.Find(ctx, "findkey", f)
for iterator.Next(iter) {
result = append(result, iterator.Value(iter))

View file

@ -4,7 +4,7 @@ import "github.com/nspcc-dev/neo-go/pkg/interop/storage"
var Key = "sub"
func _deploy(data any, isUpdate bool) {
func _deploy(data interface{}, isUpdate bool) {
ctx := storage.GetContext()
value := "sub create"
if isUpdate {

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