diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 90e46f211..e453dd35a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -53,9 +53,9 @@ jobs: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: - go-version: 1.19 + go-version: '1.20' cache: true - name: Update Go modules @@ -76,6 +76,12 @@ jobs: 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 @@ -133,9 +139,9 @@ jobs: # For proper `deps` make target execution. - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: - go-version: 1.19 + go-version: '1.20' cache: true - name: Login to DockerHub diff --git a/.github/workflows/dco.yml b/.github/workflows/dco.yml new file mode 100644 index 000000000..40ed8fcb4 --- /dev/null +++ b/.github/workflows/dco.yml @@ -0,0 +1,21 @@ +name: DCO check + +on: + pull_request: + branches: + - master + +jobs: + commits_check_job: + runs-on: ubuntu-latest + name: Commits Check + steps: + - name: Get PR Commits + id: 'get-pr-commits' + uses: tim-actions/get-pr-commits@master + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: DCO Check + uses: tim-actions/dco@master + with: + commits: ${{ steps.get-pr-commits.outputs.commits }} diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 1eb45a742..0613c7e09 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -18,9 +18,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v4 with: - go-version: 1.19 + go-version: '1.18' - uses: actions/checkout@v3 - name: golangci-lint uses: golangci/golangci-lint-action@v3 @@ -101,9 +101,9 @@ jobs: git submodule update --init - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: - go-version: 1.19 + go-version: '1.20' cache: true - name: Update Go modules @@ -125,20 +125,20 @@ jobs: strategy: matrix: os: [ubuntu-20.04, windows-2022, macos-12] - go_versions: [ '1.17', '1.18', '1.19' ] + go_versions: [ '1.18', '1.19', '1.20' ] exclude: # Only latest Go version for Windows and MacOS. - os: windows-2022 - go_versions: '1.17' + go_versions: '1.18' - os: windows-2022 - go_versions: '1.18' - - os: macos-12 - go_versions: '1.17' + go_versions: '1.19' - os: macos-12 go_versions: '1.18' + - os: macos-12 + go_versions: '1.19' # Exclude latest Go version for Ubuntu as Coverage uses it. - os: ubuntu-20.04 - go_versions: '1.19' + go_versions: '1.20' fail-fast: false steps: - uses: actions/checkout@v3 @@ -146,7 +146,7 @@ jobs: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '${{ matrix.go_versions }}' cache: true diff --git a/.gitignore b/.gitignore index fbc04a091..3262aa4e4 100644 --- a/.gitignore +++ b/.gitignore @@ -7,9 +7,6 @@ # 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/ @@ -54,6 +51,7 @@ 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 diff --git a/.golangci.yml b/.golangci.yml index b79417f01..8c7a48623 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -69,3 +69,7 @@ issues: - EXC0003 # test/Test ... consider calling this - EXC0004 # govet - EXC0005 # C-style breaks + exclude-rules: + - linters: + - revive + text: "unused-parameter" diff --git a/Dockerfile b/Dockerfile index 814944743..debcff904 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # Builder image # Keep go version in sync with Build GA job. -FROM golang:1.18-alpine as builder +FROM golang:1.20-alpine as builder # Display go version for information purposes. RUN go version diff --git a/Dockerfile.wsc b/Dockerfile.wsc index 7eef193c3..25b35ff62 100644 --- a/Dockerfile.wsc +++ b/Dockerfile.wsc @@ -43,14 +43,14 @@ RUN $newPath = ('{0}\bin;C:\Program Files\Go\bin;{1}' -f $env:GOPATH, $env:PATH) [Environment]::SetEnvironmentVariable('PATH', $newPath, [EnvironmentVariableTarget]::Machine); # Keep go version in sync with Build GA job. -ENV GOLANG_VERSION=1.19 +ENV GOLANG_VERSION=1.20 -RUN $url = 'https://go.dev/dl/go1.19.windows-amd64.zip'; \ +RUN $url = 'https://go.dev/dl/go1.20.windows-amd64.zip'; \ Write-Host ('Downloading {0} ...' -f $url); \ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; \ Invoke-WebRequest -Uri $url -OutFile 'go.zip'; \ \ - $sha256 = 'bcaaf966f91980d35ae93c37a8fe890e4ddfca19448c0d9f66c027d287e2823a'; \ + $sha256 = 'e8f6d8bbcf3df58d2ba29818e13b04c2e42ba2e4d90d580720b21c34d10bbf68'; \ Write-Host ('Verifying sha256 ({0}) ...' -f $sha256); \ if ((Get-FileHash go.zip -Algorithm sha256).Hash -ne $sha256) { \ Write-Host 'FAILED!'; \ diff --git a/README.md b/README.md index bef2d78a2..0fbd656ad 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ NeoGo, `:latest` points to the latest release) or build yourself. ### Building -Building NeoGo requires Go 1.17+ and `make`: +Building NeoGo requires Go 1.18+ and `make`: ``` make diff --git a/ROADMAP.md b/ROADMAP.md index ae96679a7..bfe2aa845 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -40,17 +40,6 @@ While a lot of the code is already converted to new APIs, old ones still can be used in some code not known to us. Therefore we will remove old APIs not earlier than May 2023, with 0.103.0 release. -## util.FromAddress smart contract helper - -`util` smart contract library has a FromAddress function that is one of the -oldest lines in the entire NeoGo code base, dating back to 2018. Version -0.99.4 of NeoGo (October 2022) has introduced a new `address` package with -`ToHash160` function, it covers a bit more use cases but can be used as a -direct replacement of the old function, so please update your code. - -util.FromAddress is expected to be removed around March 2023 (~0.102.0 -release). - ## WSClient Notifications channel and SubscribeFor* APIs Version 0.99.5 of NeoGo introduces a new set of subscription APIs that gives @@ -61,15 +50,6 @@ Receive* APIs. Removal of these APIs is scheduled for May 2023 (~0.103.0 release). -## Prometheus RPC counters - -A number of neogo_${method}_called Prometheus counters are marked as -deprecated since version 0.99.5, neogo_rpc_${method}_time histograms can be -used instead (that also have a counter). - -It's not a frequently used thing and it's easy to replace it, so removal of -old counters is scheduled for January-February 2023 (~0.100.X release). - ## SecondsPerBlock protocol configuration With 0.100.0 version SecondsPerBlock protocol configuration setting was @@ -134,3 +114,28 @@ for security reasons. Removal of these options from ProtocolConfiguration is scheduled for May-June 2023 (~0.103.0 release). + +## 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 ~September 2023 +(~0.105.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 Sep-Oct 2023. + +## `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 Sep-Oct 2023 (~0.105.0 release). diff --git a/cli/cmdargs/parser.go b/cli/cmdargs/parser.go index 315577de5..cb46971dd 100644 --- a/cli/cmdargs/parser.go +++ b/cli/cmdargs/parser.go @@ -230,9 +230,9 @@ func parseCosigner(c string) (transaction.Signer, error) { } // GetDataFromContext returns data parameter from context args. -func GetDataFromContext(ctx *cli.Context) (int, interface{}, *cli.ExitError) { +func GetDataFromContext(ctx *cli.Context) (int, any, *cli.ExitError) { var ( - data interface{} + data any offset int params []smartcontract.Parameter err error diff --git a/cli/server/server.go b/cli/server/server.go index 39e67739c..b55982a4e 100644 --- a/cli/server/server.go +++ b/cli/server/server.go @@ -669,7 +669,7 @@ func initBlockChain(cfg config.Config, log *zap.Logger) (*core.Blockchain, stora chain, err := core.NewBlockchain(store, cfg.Blockchain(), log) if err != nil { errText := "could not initialize blockchain: %w" - errArgs := []interface{}{err} + errArgs := []any{err} closeErr := store.Close() if closeErr != nil { errText += "; failed to close the DB: %w" diff --git a/cli/smartcontract/contract_test.go b/cli/smartcontract/contract_test.go index 917f07221..bb41a4a30 100644 --- a/cli/smartcontract/contract_test.go +++ b/cli/smartcontract/contract_test.go @@ -237,7 +237,8 @@ func TestContractInitAndCompile(t *testing.T) { e.RunWithError(t, "neo-go", "contract", "init", "--name", ctrPath) }) - srcPath := filepath.Join(ctrPath, "main.go") + ctrRootPath := filepath.Join(ctrPath, "main") + srcPath := ctrRootPath + ".go" cfgPath := filepath.Join(ctrPath, "neo-go.yml") nefPath := filepath.Join(tmpDir, "testcontract.nef") manifestPath := filepath.Join(tmpDir, "testcontract.manifest.json") @@ -288,6 +289,19 @@ func TestContractInitAndCompile(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) + }) } // Checks that error is returned if GAS available for test-invoke exceeds diff --git a/cli/smartcontract/generate.go b/cli/smartcontract/generate.go index 47c662865..0831fd494 100644 --- a/cli/smartcontract/generate.go +++ b/cli/smartcontract/generate.go @@ -29,9 +29,8 @@ var generatorFlags = []cli.Flag{ Usage: "Output of the compiled wrapper", }, cli.StringFlag{ - Name: "hash", - Required: true, - Usage: "Smart-contract hash", + Name: "hash", + Usage: "Smart-contract hash", }, } @@ -53,21 +52,29 @@ var generateRPCWrapperCmd = cli.Command{ } func contractGenerateWrapper(ctx *cli.Context) error { - return contractGenerateSomething(ctx, binding.Generate) + return contractGenerateSomething(ctx, binding.Generate, false) } func contractGenerateRPCWrapper(ctx *cli.Context) error { - return contractGenerateSomething(ctx, rpcbinding.Generate) + return contractGenerateSomething(ctx, rpcbinding.Generate, true) } // contractGenerateSomething reads generator parameters and calls the given callback. -func contractGenerateSomething(ctx *cli.Context, cb func(binding.Config) error) error { +func contractGenerateSomething(ctx *cli.Context, cb func(binding.Config) error, allowEmptyHash bool) error { if err := cmdargs.EnsureNone(ctx); err != nil { return err } - h, err := util.Uint160DecodeStringLE(strings.TrimPrefix(ctx.String("hash"), "0x")) - if err != nil { - return cli.NewExitError(fmt.Errorf("invalid contract hash: %w", err), 1) + 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) + } + } else if !allowEmptyHash { + return cli.NewExitError("contract hash must be provided via --hash flag", 1) } m, _, err := readManifest(ctx.String("manifest"), h) if err != nil { diff --git a/cli/smartcontract/generate_test.go b/cli/smartcontract/generate_test.go index 1b60f830e..be3cd313e 100644 --- a/cli/smartcontract/generate_test.go +++ b/cli/smartcontract/generate_test.go @@ -3,6 +3,7 @@ package smartcontract import ( "bytes" "encoding/json" + "fmt" "os" "path/filepath" "strings" @@ -187,7 +188,7 @@ func Zum(typev int, typev_ int, funcv int) int { } // JustExecute invokes ` + "`justExecute`" + ` method of contract. -func JustExecute(arr []interface{}) { +func JustExecute(arr []any) { neogointernal.CallWithTokenNoRet(Hash, "justExecute", int(contract.All), arr) } @@ -197,7 +198,7 @@ func GetPublicKey() interop.PublicKey { } // OtherTypes invokes ` + "`otherTypes`" + ` method of contract. -func OtherTypes(ctr interop.Hash160, tx interop.Hash256, sig interop.Signature, data interface{}) bool { +func OtherTypes(ctr interop.Hash160, tx interop.Hash256, sig interop.Signature, data any) bool { return neogointernal.CallWithToken(Hash, "otherTypes", int(contract.All), ctr, tx, sig, data).(bool) } @@ -212,8 +213,8 @@ func GetFromMap(intMap map[string]int, indices []string) []int { } // DoSomething invokes ` + "`doSomething`" + ` method of contract. -func DoSomething(bytes []byte, str string) interface{} { - return neogointernal.CallWithToken(Hash, "doSomething", int(contract.ReadStates), bytes, str).(interface{}) +func DoSomething(bytes []byte, str string) any { + return neogointernal.CallWithToken(Hash, "doSomething", int(contract.ReadStates), bytes, str).(any) } // GetBlockWrapper invokes ` + "`getBlockWrapper`" + ` method of contract. @@ -303,27 +304,32 @@ var Hash = util.Uint160{0x4, 0x8, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x0, 0x1, // Invoker is used by ContractReader to call various safe methods. type Invoker interface { - Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) + 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 { - return &ContractReader{invoker} + 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(Hash, "get")) + 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() @@ -341,10 +347,14 @@ func TestGenerateRPCBindings(t *testing.T) { data, err := os.ReadFile(outFile) require.NoError(t, err) data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows. - expected, err := os.ReadFile(good) - require.NoError(t, err) - expected = bytes.ReplaceAll(expected, []byte("\r"), []byte{}) // Windows. - require.Equal(t, string(expected), string(data)) + 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)) + } }) } @@ -363,6 +373,8 @@ func TestGenerateRPCBindings(t *testing.T) { checkBinding(filepath.Join("testdata", "nonepiter", "iter.manifest.json"), "0x00112233445566778899aabbccddeeff00112233", filepath.Join("testdata", "nonepiter", "iter.go")) + + require.False(t, rewriteExpectedOutputs) } func TestAssistedRPCBindings(t *testing.T) { @@ -370,38 +382,69 @@ func TestAssistedRPCBindings(t *testing.T) { app := cli.NewApp() app.Commands = NewCommands() - var checkBinding = func(source string) { - t.Run(source, func(t *testing.T) { + 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) { + 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") - require.NoError(t, app.Run([]string{"", "contract", "compile", + cmd := []string{"", "contract", "compile", "--in", source, - "--config", filepath.Join(source, "config.yml"), + "--config", configFile, "--manifest", manifestF, "--bindings", bindingF, "--out", nefF, - })) - outFile := filepath.Join(tmpDir, "out.go") - require.NoError(t, app.Run([]string{"", "contract", "generate-rpcwrapper", + } + if guessEventTypes { + cmd = append(cmd, "--guess-eventtypes") + } + require.NoError(t, app.Run(cmd)) + + cmds := []string{"", "contract", "generate-rpcwrapper", "--config", bindingF, "--manifest", manifestF, - "--out", outFile, - "--hash", "0x00112233445566778899aabbccddeeff00112233", - })) + "--out", expectedFile, + } + if hasDefinedHash { + cmds = append(cmds, "--hash", "0x00112233445566778899aabbccddeeff00112233") + } + require.NoError(t, app.Run(cmds)) - data, err := os.ReadFile(outFile) + data, err := os.ReadFile(expectedFile) require.NoError(t, err) data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows. - expected, err := os.ReadFile(filepath.Join(source, "rpcbindings.out")) - require.NoError(t, err) - expected = bytes.ReplaceAll(expected, []byte("\r"), []byte{}) // Windows. - require.Equal(t, string(expected), string(data)) + 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)) + } }) } - checkBinding(filepath.Join("testdata", "types")) - checkBinding(filepath.Join("testdata", "structs")) + for _, hasDefinedHash := range []bool{true, false} { + checkBinding(filepath.Join("testdata", "types"), hasDefinedHash, false) + checkBinding(filepath.Join("testdata", "structs"), hasDefinedHash, false) + } + checkBinding(filepath.Join("testdata", "notifications"), true, false) + checkBinding(filepath.Join("testdata", "notifications"), true, false, "_extended") + checkBinding(filepath.Join("testdata", "notifications"), true, true, "_guessed") + + require.False(t, rewriteExpectedOutputs) } func TestGenerate_Errors(t *testing.T) { @@ -467,3 +510,55 @@ callflags: "--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", "invalid5"), "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", "invalid6"), "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", "invalid7"), "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", "invalid8"), "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", "invalid9"), "configured declared named type intersects with the contract's one: `invalid9.NamedStruct`") + }) +} diff --git a/cli/smartcontract/permission.go b/cli/smartcontract/permission.go index 0c4608b84..cfb28ad0b 100644 --- a/cli/smartcontract/permission.go +++ b/cli/smartcontract/permission.go @@ -19,7 +19,7 @@ const ( permMethodKey = "methods" ) -func (p permission) MarshalYAML() (interface{}, error) { +func (p permission) MarshalYAML() (any, error) { m := yaml.Node{Kind: yaml.MappingNode} switch p.Contract.Type { case manifest.PermissionWildcard: @@ -36,7 +36,7 @@ func (p permission) MarshalYAML() (interface{}, error) { return nil, fmt.Errorf("invalid permission type: %d", p.Contract.Type) } - var val interface{} = "*" + var val any = "*" if !p.Methods.IsWildcard() { val = p.Methods.Value } @@ -53,8 +53,8 @@ func (p permission) MarshalYAML() (interface{}, error) { return m, nil } -func (p *permission) UnmarshalYAML(unmarshal func(interface{}) error) error { - var m map[string]interface{} +func (p *permission) UnmarshalYAML(unmarshal func(any) error) error { + var m map[string]any if err := unmarshal(&m); err != nil { return err } @@ -66,7 +66,7 @@ func (p *permission) UnmarshalYAML(unmarshal func(interface{}) error) error { return p.fillMethods(m) } -func (p *permission) fillType(m map[string]interface{}) error { +func (p *permission) fillType(m map[string]any) error { vh, ok1 := m[permHashKey] vg, ok2 := m[permGroupKey] switch { @@ -104,7 +104,7 @@ func (p *permission) fillType(m map[string]interface{}) error { return nil } -func (p *permission) fillMethods(m map[string]interface{}) error { +func (p *permission) fillMethods(m map[string]any) error { methods, ok := m[permMethodKey] if !ok { return errors.New("'methods' field is missing from permission") @@ -116,7 +116,7 @@ func (p *permission) fillMethods(m map[string]interface{}) error { p.Methods.Value = nil return nil } - case []interface{}: + case []any: ms := make([]string, len(mt)) for i := range mt { ms[i], ok = mt[i].(string) diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index 61d026bc9..38dea5f13 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -24,6 +24,7 @@ import ( "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" @@ -81,7 +82,7 @@ func init() { } // RuntimeNotify sends runtime notification with "Hello world!" name -func RuntimeNotify(args []interface{}) { +func RuntimeNotify(args []any) { runtime.Notify(notificationName, args) }` ) @@ -125,12 +126,19 @@ 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]", - 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] [--guess-eventtypes]", + Description: `Compiles given smart contract to a .nef file and emits other associated + information (manifest, bindings configuration, debug information files) if + asked to. If none of --out, --manifest, --config, --bindings flags are specified, + then the output filenames for these flags will be guessed using the contract + name or path provided via --in option by trimming/adding corresponding suffixes + to the common part of the path. In the latter case the configuration filepath + will be guessed from the --in option using the same rule."`, + Action: contractCompile, Flags: []cli.Flag{ cli.StringFlag{ Name: "in, i", - Usage: "Input file for the smart contract to be compiled", + Usage: "Input file for the smart contract to be compiled (*.go file or directory)", }, cli.StringFlag{ Name: "out, o", @@ -164,6 +172,10 @@ 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", @@ -345,13 +357,15 @@ func initSmartContract(ctx *cli.Context) error { SourceURL: "http://example.com/", SupportedStandards: []string{}, SafeMethods: []string{}, - Events: []manifest.Event{ + Events: []compiler.HybridEvent{ { Name: "Hello world!", - Parameters: []manifest.Parameter{ + Parameters: []compiler.HybridParameter{ { - Name: "args", - Type: smartcontract.ArrayType, + Parameter: manifest.Parameter{ + Name: "args", + Type: smartcontract.ArrayType, + }, }, }, }, @@ -400,20 +414,48 @@ func contractCompile(ctx *cli.Context) error { manifestFile := ctx.String("manifest") confFile := ctx.String("config") debugFile := ctx.String("debug") - if len(confFile) == 0 && (len(manifestFile) != 0 || len(debugFile) != 0) { + out := ctx.String("out") + bindings := ctx.String("bindings") + if len(confFile) == 0 && (len(manifestFile) != 0 || len(debugFile) != 0 || len(bindings) != 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: ctx.String("out"), + Outfile: out, DebugInfo: debugFile, ManifestFile: manifestFile, - BindingsFile: ctx.String("bindings"), + BindingsFile: bindings, NoStandardCheck: ctx.Bool("no-standards"), NoEventsCheck: ctx.Bool("no-events"), NoPermissionsCheck: ctx.Bool("no-permissions"), + + GuessEventTypes: ctx.Bool("guess-eventtypes"), } if len(confFile) != 0 { @@ -424,6 +466,7 @@ 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 { @@ -495,7 +538,7 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error { err error exitErr *cli.ExitError operation string - params []interface{} + params []any paramsStart = 1 scParams []smartcontract.Parameter cosigners []transaction.Signer @@ -521,7 +564,7 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error { if err != nil { return cli.NewExitError(err, 1) } - params = make([]interface{}, len(scParams)) + params = make([]any, len(scParams)) for i := range scParams { params[i] = scParams[i] } @@ -548,7 +591,7 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error { return invokeWithArgs(ctx, acc, w, script, operation, params, cosigners) } -func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet, script util.Uint160, operation string, params []interface{}, cosigners []transaction.Signer) error { +func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet, script util.Uint160, operation string, params []any, cosigners []transaction.Signer) error { var ( err error signersAccounts []actor.SignerAccount @@ -672,9 +715,10 @@ type ProjectConfig struct { SourceURL string SafeMethods []string SupportedStandards []string - Events []manifest.Event + Events []compiler.HybridEvent Permissions []permission - Overloads map[string]string `yaml:"overloads,omitempty"` + Overloads map[string]string `yaml:"overloads,omitempty"` + NamedTypes map[string]binding.ExtendedType `yaml:"namedtypes,omitempty"` } func inspect(ctx *cli.Context) error { @@ -787,7 +831,7 @@ func contractDeploy(ctx *cli.Context) error { return cli.NewExitError(fmt.Errorf("failed to read manifest file: %w", err), 1) } - var appCallParams = []interface{}{f, manifestBytes} + var appCallParams = []any{f, manifestBytes} signOffset, data, err := cmdargs.ParseParams(ctx.Args(), true) if err != nil { diff --git a/cli/smartcontract/smart_contract_test.go b/cli/smartcontract/smart_contract_test.go index a2b119dfe..cd89b16f6 100644 --- a/cli/smartcontract/smart_contract_test.go +++ b/cli/smartcontract/smart_contract_test.go @@ -51,7 +51,7 @@ func init() { } // RuntimeNotify sends runtime notification with "Hello world!" name -func RuntimeNotify(args []interface{}) { +func RuntimeNotify(args []any) { runtime.Notify(notificationName, args) }`, string(main)) diff --git a/cli/smartcontract/testdata/deploy/main.go b/cli/smartcontract/testdata/deploy/main.go index 42bf4c06f..c45b459e0 100644 --- a/cli/smartcontract/testdata/deploy/main.go +++ b/cli/smartcontract/testdata/deploy/main.go @@ -13,7 +13,7 @@ var key = "key" const mgmtKey = "mgmt" -func _deploy(data interface{}, isUpdate bool) { +func _deploy(data any, isUpdate bool) { var value string ctx := storage.GetContext() @@ -25,7 +25,7 @@ func _deploy(data interface{}, isUpdate bool) { storage.Put(ctx, mgmtKey, sh) if data != nil { - arr := data.([]interface{}) + arr := data.([]any) 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) []interface{} { +func TestFind(f storage.FindFlags) []any { ctx := storage.GetContext() storage.Put(ctx, "findkey1", "value1") storage.Put(ctx, "findkey2", "value2") - var result []interface{} + var result []any iter := storage.Find(ctx, "findkey", f) for iterator.Next(iter) { result = append(result, iterator.Value(iter)) diff --git a/cli/smartcontract/testdata/deploy/sub/put.go b/cli/smartcontract/testdata/deploy/sub/put.go index e21b3cbcf..08f634741 100644 --- a/cli/smartcontract/testdata/deploy/sub/put.go +++ b/cli/smartcontract/testdata/deploy/sub/put.go @@ -4,7 +4,7 @@ import "github.com/nspcc-dev/neo-go/pkg/interop/storage" var Key = "sub" -func _deploy(data interface{}, isUpdate bool) { +func _deploy(data any, isUpdate bool) { ctx := storage.GetContext() value := "sub create" if isUpdate { diff --git a/cli/smartcontract/testdata/gas/gas.go b/cli/smartcontract/testdata/gas/gas.go index d3c938c29..e83072fbb 100644 --- a/cli/smartcontract/testdata/gas/gas.go +++ b/cli/smartcontract/testdata/gas/gas.go @@ -25,6 +25,7 @@ type Actor interface { type ContractReader struct { nep17.TokenReader invoker Invoker + hash util.Uint160 } // Contract implements all contract methods. @@ -32,16 +33,18 @@ 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 { - return &ContractReader{*nep17.NewReader(invoker, Hash), invoker} + 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 nep17t = nep17.New(actor, Hash) - return &Contract{ContractReader{nep17t.TokenReader, actor}, nep17t.TokenWriter, actor} + var hash = Hash + var nep17t = nep17.New(actor, hash) + return &Contract{ContractReader{nep17t.TokenReader, actor, hash}, nep17t.TokenWriter, actor, hash} } - diff --git a/cli/smartcontract/testdata/invalid5/invalid.go b/cli/smartcontract/testdata/invalid5/invalid.go new file mode 100644 index 000000000..0cae2ed71 --- /dev/null +++ b/cli/smartcontract/testdata/invalid5/invalid.go @@ -0,0 +1,7 @@ +package invalid5 + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +func Main() { + runtime.Notify("Non declared event") +} diff --git a/cli/smartcontract/testdata/invalid5/invalid.yml b/cli/smartcontract/testdata/invalid5/invalid.yml new file mode 100644 index 000000000..eda948267 --- /dev/null +++ b/cli/smartcontract/testdata/invalid5/invalid.yml @@ -0,0 +1 @@ +name: Test undeclared event \ No newline at end of file diff --git a/cli/smartcontract/testdata/invalid6/invalid.go b/cli/smartcontract/testdata/invalid6/invalid.go new file mode 100644 index 000000000..dd3a3ecdd --- /dev/null +++ b/cli/smartcontract/testdata/invalid6/invalid.go @@ -0,0 +1,7 @@ +package invalid6 + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +func Main() { + runtime.Notify("SomeEvent", "p1", "p2") +} diff --git a/cli/smartcontract/testdata/invalid6/invalid.yml b/cli/smartcontract/testdata/invalid6/invalid.yml new file mode 100644 index 000000000..13933803e --- /dev/null +++ b/cli/smartcontract/testdata/invalid6/invalid.yml @@ -0,0 +1,6 @@ +name: Test undeclared event +events: + - name: SomeEvent + parameters: + - name: p1 + type: String \ No newline at end of file diff --git a/cli/smartcontract/testdata/invalid7/invalid.go b/cli/smartcontract/testdata/invalid7/invalid.go new file mode 100644 index 000000000..41bc20c47 --- /dev/null +++ b/cli/smartcontract/testdata/invalid7/invalid.go @@ -0,0 +1,7 @@ +package invalid7 + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +func Main() { + runtime.Notify("SomeEvent", "p1", 5) +} diff --git a/cli/smartcontract/testdata/invalid7/invalid.yml b/cli/smartcontract/testdata/invalid7/invalid.yml new file mode 100644 index 000000000..a217deefd --- /dev/null +++ b/cli/smartcontract/testdata/invalid7/invalid.yml @@ -0,0 +1,8 @@ +name: Test undeclared event +events: + - name: SomeEvent + parameters: + - name: p1 + type: String + - name: p2 + type: String \ No newline at end of file diff --git a/cli/smartcontract/testdata/invalid8/invalid.go b/cli/smartcontract/testdata/invalid8/invalid.go new file mode 100644 index 000000000..dba9173ca --- /dev/null +++ b/cli/smartcontract/testdata/invalid8/invalid.go @@ -0,0 +1,17 @@ +package invalid8 + +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"}) +} diff --git a/cli/smartcontract/testdata/invalid8/invalid.yml b/cli/smartcontract/testdata/invalid8/invalid.yml new file mode 100644 index 000000000..6c0c34748 --- /dev/null +++ b/cli/smartcontract/testdata/invalid8/invalid.yml @@ -0,0 +1,6 @@ +name: Test undeclared event +events: + - name: SomeEvent + parameters: + - name: p1 + type: Array \ No newline at end of file diff --git a/cli/smartcontract/testdata/invalid9/invalid.go b/cli/smartcontract/testdata/invalid9/invalid.go new file mode 100644 index 000000000..5036bca9f --- /dev/null +++ b/cli/smartcontract/testdata/invalid9/invalid.go @@ -0,0 +1,12 @@ +package invalid9 + +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} +} diff --git a/cli/smartcontract/testdata/invalid9/invalid.yml b/cli/smartcontract/testdata/invalid9/invalid.yml new file mode 100644 index 000000000..40bbf66db --- /dev/null +++ b/cli/smartcontract/testdata/invalid9/invalid.yml @@ -0,0 +1,16 @@ +name: Test undeclared event +events: + - name: SomeEvent + parameters: + - name: p1 + type: Array + extendedtype: + base: Array + name: invalid9.NamedStruct +namedtypes: + invalid9.NamedStruct: + base: Array + name: invalid9.NamedStruct + fields: + - field: SomeInt + base: Integer diff --git a/cli/smartcontract/testdata/nameservice/nns.go b/cli/smartcontract/testdata/nameservice/nns.go index d156d985c..97bd878a3 100644 --- a/cli/smartcontract/testdata/nameservice/nns.go +++ b/cli/smartcontract/testdata/nameservice/nns.go @@ -2,6 +2,8 @@ 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" @@ -11,11 +13,26 @@ import ( "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 @@ -27,11 +44,11 @@ type Actor interface { nep11.Actor - MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) + 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 ...interface{}) (*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 ...interface{}) (util.Uint256, uint32, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) SendRun(script []byte) (util.Uint256, uint32, error) } @@ -39,6 +56,7 @@ type Actor interface { type ContractReader struct { nep11.NonDivisibleReader invoker Invoker + hash util.Uint160 } // Contract implements all contract methods. @@ -46,23 +64,25 @@ 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 { - return &ContractReader{*nep11.NewNonDivisibleReader(invoker, Hash), invoker} + 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 nep11ndt = nep11.NewNonDivisible(actor, Hash) - return &Contract{ContractReader{nep11ndt.NonDivisibleReader, actor}, nep11ndt.BaseWriter, actor} + 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(Hash, "roots")) + return unwrap.SessionIterator(c.invoker.Call(c.hash, "roots")) } // RootsExpanded is similar to Roots (uses the same contract @@ -71,27 +91,27 @@ func (c *ContractReader) Roots() (uuid.UUID, result.Iterator, error) { // 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(Hash, "roots", _numOfIteratorItems)) + 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(Hash, "getPrice", length)) + 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(Hash, "isAvailable", name)) + 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(Hash, "getRecord", name, typev)) + 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(Hash, "getAllRecords", name)) + return unwrap.SessionIterator(c.invoker.Call(c.hash, "getAllRecords", name)) } // GetAllRecordsExpanded is similar to GetAllRecords (uses the same contract @@ -100,26 +120,26 @@ func (c *ContractReader) GetAllRecords(name string) (uuid.UUID, result.Iterator, // 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(Hash, "getAllRecords", _numOfIteratorItems, name)) + 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(Hash, "resolve", name, typev)) + 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(Hash, "update", nef, manifest) + 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(Hash, "update", nef, manifest) + return c.actor.MakeCall(c.hash, "update", nef, manifest) } // UpdateUnsigned creates a transaction invoking `update` method of the contract. @@ -127,21 +147,21 @@ func (c *Contract) UpdateTransaction(nef []byte, manifest string) (*transaction. // 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(Hash, "update", nil, nef, manifest) + 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(Hash, "addRoot", root) + 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(Hash, "addRoot", root) + return c.actor.MakeCall(c.hash, "addRoot", root) } // AddRootUnsigned creates a transaction invoking `addRoot` method of the contract. @@ -149,40 +169,40 @@ func (c *Contract) AddRootTransaction(root string) (*transaction.Transaction, er // 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(Hash, "addRoot", nil, root) + 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 []interface{}) (util.Uint256, uint32, error) { - return c.actor.SendCall(Hash, "setPrice", priceList) +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 []interface{}) (*transaction.Transaction, error) { - return c.actor.MakeCall(Hash, "setPrice", priceList) +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 []interface{}) (*transaction.Transaction, error) { - return c.actor.MakeUnsignedCall(Hash, "setPrice", nil, priceList) +func (c *Contract) SetPriceUnsigned(priceList []any) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "setPrice", nil, priceList) } -func scriptForRegister(name string, owner util.Uint160) ([]byte, error) { - return smartcontract.CreateCallWithAssertScript(Hash, "register", name, owner) +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 := scriptForRegister(name, owner) + script, err := c.scriptForRegister(name, owner) if err != nil { return util.Uint256{}, 0, err } @@ -193,7 +213,7 @@ func (c *Contract) Register(name string, owner util.Uint160) (util.Uint256, uint // 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 := scriptForRegister(name, owner) + script, err := c.scriptForRegister(name, owner) if err != nil { return nil, err } @@ -205,7 +225,7 @@ func (c *Contract) RegisterTransaction(name string, owner util.Uint160) (*transa // 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 := scriptForRegister(name, owner) + script, err := c.scriptForRegister(name, owner) if err != nil { return nil, err } @@ -216,14 +236,14 @@ func (c *Contract) RegisterUnsigned(name string, owner util.Uint160) (*transacti // 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(Hash, "renew", name) + 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(Hash, "renew", name) + return c.actor.MakeCall(c.hash, "renew", name) } // RenewUnsigned creates a transaction invoking `renew` method of the contract. @@ -231,21 +251,21 @@ func (c *Contract) RenewTransaction(name string) (*transaction.Transaction, erro // 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(Hash, "renew", nil, name) + return c.actor.MakeUnsignedCall(c.hash, "renew", nil, name) } // Renew_2 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_2(name string, years *big.Int) (util.Uint256, uint32, error) { - return c.actor.SendCall(Hash, "renew", name, years) + return c.actor.SendCall(c.hash, "renew", name, years) } // Renew_2Transaction 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) Renew_2Transaction(name string, years *big.Int) (*transaction.Transaction, error) { - return c.actor.MakeCall(Hash, "renew", name, years) + return c.actor.MakeCall(c.hash, "renew", name, years) } // Renew_2Unsigned creates a transaction invoking `renew` method of the contract. @@ -253,21 +273,21 @@ func (c *Contract) Renew_2Transaction(name string, years *big.Int) (*transaction // 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) Renew_2Unsigned(name string, years *big.Int) (*transaction.Transaction, error) { - return c.actor.MakeUnsignedCall(Hash, "renew", nil, name, years) + 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(Hash, "setAdmin", name, admin) + 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(Hash, "setAdmin", name, admin) + return c.actor.MakeCall(c.hash, "setAdmin", name, admin) } // SetAdminUnsigned creates a transaction invoking `setAdmin` method of the contract. @@ -275,21 +295,21 @@ func (c *Contract) SetAdminTransaction(name string, admin util.Uint160) (*transa // 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(Hash, "setAdmin", nil, name, admin) + 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(Hash, "setRecord", name, typev, data) + 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(Hash, "setRecord", name, typev, data) + return c.actor.MakeCall(c.hash, "setRecord", name, typev, data) } // SetRecordUnsigned creates a transaction invoking `setRecord` method of the contract. @@ -297,21 +317,21 @@ func (c *Contract) SetRecordTransaction(name string, typev *big.Int, data string // 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(Hash, "setRecord", nil, name, typev, data) + 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(Hash, "deleteRecord", name, typev) + 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(Hash, "deleteRecord", name, typev) + return c.actor.MakeCall(c.hash, "deleteRecord", name, typev) } // DeleteRecordUnsigned creates a transaction invoking `deleteRecord` method of the contract. @@ -319,5 +339,171 @@ func (c *Contract) DeleteRecordTransaction(name string, typev *big.Int) (*transa // 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(Hash, "deleteRecord", nil, name, typev) + 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 } diff --git a/cli/smartcontract/testdata/nex/nex.go b/cli/smartcontract/testdata/nex/nex.go index 2ad6941db..296a3d0e3 100644 --- a/cli/smartcontract/testdata/nex/nex.go +++ b/cli/smartcontract/testdata/nex/nex.go @@ -2,17 +2,29 @@ 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 @@ -24,11 +36,11 @@ type Actor interface { nep17.Actor - MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) + 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 ...interface{}) (*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 ...interface{}) (util.Uint256, uint32, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) SendRun(script []byte) (util.Uint256, uint32, error) } @@ -36,6 +48,7 @@ type Actor interface { type ContractReader struct { nep17.TokenReader invoker Invoker + hash util.Uint160 } // Contract implements all contract methods. @@ -43,52 +56,54 @@ 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 { - return &ContractReader{*nep17.NewReader(invoker, Hash), invoker} + 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 nep17t = nep17.New(actor, Hash) - return &Contract{ContractReader{nep17t.TokenReader, actor}, nep17t.TokenWriter, actor} + 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(Hash, "cap")) + 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(Hash, "getMinter")) + 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(Hash, "getOwner")) + 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(Hash, "totalMinted")) + 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(Hash, "changeMinter", newMinter) + 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(Hash, "changeMinter", newMinter) + return c.actor.MakeCall(c.hash, "changeMinter", newMinter) } // ChangeMinterUnsigned creates a transaction invoking `changeMinter` method of the contract. @@ -96,21 +111,21 @@ func (c *Contract) ChangeMinterTransaction(newMinter *keys.PublicKey) (*transact // 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(Hash, "changeMinter", nil, newMinter) + 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(Hash, "changeOwner", newOwner) + 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(Hash, "changeOwner", newOwner) + return c.actor.MakeCall(c.hash, "changeOwner", newOwner) } // ChangeOwnerUnsigned creates a transaction invoking `changeOwner` method of the contract. @@ -118,21 +133,21 @@ func (c *Contract) ChangeOwnerTransaction(newOwner util.Uint160) (*transaction.T // 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(Hash, "changeOwner", nil, newOwner) + 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(Hash, "destroy") + 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(Hash, "destroy") + return c.actor.MakeCall(c.hash, "destroy") } // DestroyUnsigned creates a transaction invoking `destroy` method of the contract. @@ -140,21 +155,21 @@ func (c *Contract) DestroyTransaction() (*transaction.Transaction, error) { // 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(Hash, "destroy", nil) + 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(Hash, "maxSupply") + 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(Hash, "maxSupply") + return c.actor.MakeCall(c.hash, "maxSupply") } // MaxSupplyUnsigned creates a transaction invoking `maxSupply` method of the contract. @@ -162,43 +177,43 @@ func (c *Contract) MaxSupplyTransaction() (*transaction.Transaction, error) { // 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(Hash, "maxSupply", nil) + 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 interface{}) (util.Uint256, uint32, error) { - return c.actor.SendCall(Hash, "mint", from, to, amount, swapId, signature, data) +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 interface{}) (*transaction.Transaction, error) { - return c.actor.MakeCall(Hash, "mint", from, to, amount, swapId, signature, data) +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 interface{}) (*transaction.Transaction, error) { - return c.actor.MakeUnsignedCall(Hash, "mint", nil, from, to, amount, swapId, signature, data) +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(Hash, "update", nef, manifest) + 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(Hash, "update", nef, manifest) + return c.actor.MakeCall(c.hash, "update", nef, manifest) } // UpdateUnsigned creates a transaction invoking `update` method of the contract. @@ -206,21 +221,21 @@ func (c *Contract) UpdateTransaction(nef []byte, manifest []byte) (*transaction. // 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(Hash, "update", nil, nef, manifest) + 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(Hash, "updateCap", newCap) + 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(Hash, "updateCap", newCap) + return c.actor.MakeCall(c.hash, "updateCap", newCap) } // UpdateCapUnsigned creates a transaction invoking `updateCap` method of the contract. @@ -228,5 +243,95 @@ func (c *Contract) UpdateCapTransaction(newCap *big.Int) (*transaction.Transacti // 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(Hash, "updateCap", nil, newCap) + 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 } diff --git a/cli/smartcontract/testdata/nonepiter/iter.go b/cli/smartcontract/testdata/nonepiter/iter.go index c7323fb56..d9fac3f73 100644 --- a/cli/smartcontract/testdata/nonepiter/iter.go +++ b/cli/smartcontract/testdata/nonepiter/iter.go @@ -14,8 +14,8 @@ var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xa // Invoker is used by ContractReader to call various safe methods. type Invoker interface { - Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) - CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...interface{}) (*result.Invoke, error) + 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) } @@ -23,17 +23,18 @@ type Invoker interface { // 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 { - return &ContractReader{invoker} + 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(Hash, "tokens")) + return unwrap.SessionIterator(c.invoker.Call(c.hash, "tokens")) } // TokensExpanded is similar to Tokens (uses the same contract @@ -42,12 +43,12 @@ func (c *ContractReader) Tokens() (uuid.UUID, result.Iterator, error) { // 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(Hash, "tokens", _numOfIteratorItems)) + 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(Hash, "getAllRecords", name)) + return unwrap.SessionIterator(c.invoker.Call(c.hash, "getAllRecords", name)) } // GetAllRecordsExpanded is similar to GetAllRecords (uses the same contract @@ -56,5 +57,5 @@ func (c *ContractReader) GetAllRecords(name string) (uuid.UUID, result.Iterator, // 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(Hash, "getAllRecords", _numOfIteratorItems, name)) + return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "getAllRecords", _numOfIteratorItems, name)) } diff --git a/cli/smartcontract/testdata/notifications/config.yml b/cli/smartcontract/testdata/notifications/config.yml new file mode 100644 index 000000000..ef2866318 --- /dev/null +++ b/cli/smartcontract/testdata/notifications/config.yml @@ -0,0 +1,19 @@ +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 \ No newline at end of file diff --git a/cli/smartcontract/testdata/notifications/config_extended.yml b/cli/smartcontract/testdata/notifications/config_extended.yml new file mode 100644 index 000000000..ac8f06d6d --- /dev/null +++ b/cli/smartcontract/testdata/notifications/config_extended.yml @@ -0,0 +1,47 @@ +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 +namedtypes: + crazyStruct: + base: Struct + name: crazyStruct + fields: + - field: I + base: Integer + - field: B + base: Boolean \ No newline at end of file diff --git a/cli/smartcontract/testdata/notifications/config_guessed.yml b/cli/smartcontract/testdata/notifications/config_guessed.yml new file mode 100644 index 000000000..ef2866318 --- /dev/null +++ b/cli/smartcontract/testdata/notifications/config_guessed.yml @@ -0,0 +1,19 @@ +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 \ No newline at end of file diff --git a/cli/smartcontract/testdata/notifications/notifications.go b/cli/smartcontract/testdata/notifications/notifications.go new file mode 100644 index 000000000..80564a4e4 --- /dev/null +++ b/cli/smartcontract/testdata/notifications/notifications.go @@ -0,0 +1,25 @@ +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{}) +} diff --git a/cli/smartcontract/testdata/notifications/rpcbindings.out b/cli/smartcontract/testdata/notifications/rpcbindings.out new file mode 100644 index 000000000..74c872833 --- /dev/null +++ b/cli/smartcontract/testdata/notifications/rpcbindings.out @@ -0,0 +1,1032 @@ +// Package structs contains RPC wrappers for Notifications contract. +package structs + +import ( + "crypto/elliptic" + "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/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} + +// LedgerBlock is a contract-specific ledger.Block type used by its methods. +type LedgerBlock struct { + Hash util.Uint256 + Version *big.Int + PrevHash util.Uint256 + MerkleRoot util.Uint256 + Timestamp *big.Int + Nonce *big.Int + Index *big.Int + NextConsensus util.Uint160 + TransactionsLength *big.Int +} + +// LedgerBlockSR is a contract-specific ledger.BlockSR type used by its methods. +type LedgerBlockSR struct { + Hash util.Uint256 + Version *big.Int + PrevHash util.Uint256 + MerkleRoot util.Uint256 + Timestamp *big.Int + Nonce *big.Int + Index *big.Int + NextConsensus util.Uint160 + TransactionsLength *big.Int + PrevStateRoot util.Uint256 +} + +// LedgerTransaction is a contract-specific ledger.Transaction type used by its methods. +type LedgerTransaction struct { + Hash util.Uint256 + Version *big.Int + Nonce *big.Int + Sender util.Uint160 + SysFee *big.Int + NetFee *big.Int + ValidUntilBlock *big.Int + Script []byte +} + +// LedgerTransactionSigner is a contract-specific ledger.TransactionSigner type used by its methods. +type LedgerTransactionSigner struct { + Account util.Uint160 + Scopes *big.Int + AllowedContracts []util.Uint160 + AllowedGroups keys.PublicKeys + Rules []*LedgerWitnessRule +} + +// LedgerWitnessCondition is a contract-specific ledger.WitnessCondition type used by its methods. +type LedgerWitnessCondition struct { + Type *big.Int + Value any +} + +// LedgerWitnessRule is a contract-specific ledger.WitnessRule type used by its methods. +type LedgerWitnessRule struct { + Action *big.Int + Condition *LedgerWitnessCondition +} + +// 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 +} + +// 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) +} + +// itemToLedgerBlock converts stack item into *LedgerBlock. +func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { + if err != nil { + return nil, err + } + var res = new(LedgerBlock) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerBlock from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerBlock) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 9 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.PrevHash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field PrevHash: %w", err) + } + + index++ + res.MerkleRoot, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field MerkleRoot: %w", err) + } + + index++ + res.Timestamp, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Timestamp: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Index, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Index: %w", err) + } + + index++ + res.NextConsensus, 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 NextConsensus: %w", err) + } + + index++ + res.TransactionsLength, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field TransactionsLength: %w", err) + } + + return nil +} + +// itemToLedgerBlockSR converts stack item into *LedgerBlockSR. +func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) { + if err != nil { + return nil, err + } + var res = new(LedgerBlockSR) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerBlockSR from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerBlockSR) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 10 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.PrevHash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field PrevHash: %w", err) + } + + index++ + res.MerkleRoot, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field MerkleRoot: %w", err) + } + + index++ + res.Timestamp, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Timestamp: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Index, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Index: %w", err) + } + + index++ + res.NextConsensus, 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 NextConsensus: %w", err) + } + + index++ + res.TransactionsLength, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field TransactionsLength: %w", err) + } + + index++ + res.PrevStateRoot, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field PrevStateRoot: %w", err) + } + + return nil +} + +// itemToLedgerTransaction converts stack item into *LedgerTransaction. +func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction, error) { + if err != nil { + return nil, err + } + var res = new(LedgerTransaction) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerTransaction from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerTransaction) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 8 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Sender, 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 Sender: %w", err) + } + + index++ + res.SysFee, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field SysFee: %w", err) + } + + index++ + res.NetFee, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field NetFee: %w", err) + } + + index++ + res.ValidUntilBlock, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field ValidUntilBlock: %w", err) + } + + index++ + res.Script, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field Script: %w", err) + } + + return nil +} + +// itemToLedgerTransactionSigner converts stack item into *LedgerTransactionSigner. +func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTransactionSigner, error) { + if err != nil { + return nil, err + } + var res = new(LedgerTransactionSigner) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerTransactionSigner from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerTransactionSigner) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 5 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Account, 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 Account: %w", err) + } + + index++ + res.Scopes, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Scopes: %w", err) + } + + index++ + res.AllowedContracts, 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 + } (arr[index]) + if err != nil { + return fmt.Errorf("field AllowedContracts: %w", err) + } + + index++ + res.AllowedGroups, err = func (item stackitem.Item) (keys.PublicKeys, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make(keys.PublicKeys, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, 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 AllowedGroups: %w", err) + } + + index++ + res.Rules, err = func (item stackitem.Item) ([]*LedgerWitnessRule, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*LedgerWitnessRule, len(arr)) + for i := range res { + res[i], err = itemToLedgerWitnessRule(arr[i], 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 Rules: %w", err) + } + + return nil +} + +// itemToLedgerWitnessCondition converts stack item into *LedgerWitnessCondition. +func itemToLedgerWitnessCondition(item stackitem.Item, err error) (*LedgerWitnessCondition, error) { + if err != nil { + return nil, err + } + var res = new(LedgerWitnessCondition) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerWitnessCondition from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerWitnessCondition) 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.Type, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Type: %w", err) + } + + index++ + res.Value, err = arr[index].Value(), error(nil) + if err != nil { + return fmt.Errorf("field Value: %w", err) + } + + return nil +} + +// itemToLedgerWitnessRule converts stack item into *LedgerWitnessRule. +func itemToLedgerWitnessRule(item stackitem.Item, err error) (*LedgerWitnessRule, error) { + if err != nil { + return nil, err + } + var res = new(LedgerWitnessRule) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerWitnessRule from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerWitnessRule) 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.Action, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Action: %w", err) + } + + index++ + res.Condition, err = itemToLedgerWitnessCondition(arr[index], nil) + if err != nil { + return fmt.Errorf("field Condition: %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[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 +} diff --git a/cli/smartcontract/testdata/notifications/rpcbindings_extended.out b/cli/smartcontract/testdata/notifications/rpcbindings_extended.out new file mode 100755 index 000000000..8a71c6b32 --- /dev/null +++ b/cli/smartcontract/testdata/notifications/rpcbindings_extended.out @@ -0,0 +1,1128 @@ +// Package structs contains RPC wrappers for Notifications contract. +package structs + +import ( + "crypto/elliptic" + "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/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 +} + +// LedgerBlock is a contract-specific ledger.Block type used by its methods. +type LedgerBlock struct { + Hash util.Uint256 + Version *big.Int + PrevHash util.Uint256 + MerkleRoot util.Uint256 + Timestamp *big.Int + Nonce *big.Int + Index *big.Int + NextConsensus util.Uint160 + TransactionsLength *big.Int +} + +// LedgerBlockSR is a contract-specific ledger.BlockSR type used by its methods. +type LedgerBlockSR struct { + Hash util.Uint256 + Version *big.Int + PrevHash util.Uint256 + MerkleRoot util.Uint256 + Timestamp *big.Int + Nonce *big.Int + Index *big.Int + NextConsensus util.Uint160 + TransactionsLength *big.Int + PrevStateRoot util.Uint256 +} + +// LedgerTransaction is a contract-specific ledger.Transaction type used by its methods. +type LedgerTransaction struct { + Hash util.Uint256 + Version *big.Int + Nonce *big.Int + Sender util.Uint160 + SysFee *big.Int + NetFee *big.Int + ValidUntilBlock *big.Int + Script []byte +} + +// LedgerTransactionSigner is a contract-specific ledger.TransactionSigner type used by its methods. +type LedgerTransactionSigner struct { + Account util.Uint160 + Scopes *big.Int + AllowedContracts []util.Uint160 + AllowedGroups keys.PublicKeys + Rules []*LedgerWitnessRule +} + +// LedgerWitnessCondition is a contract-specific ledger.WitnessCondition type used by its methods. +type LedgerWitnessCondition struct { + Type *big.Int + Value any +} + +// LedgerWitnessRule is a contract-specific ledger.WitnessRule type used by its methods. +type LedgerWitnessRule struct { + Action *big.Int + Condition *LedgerWitnessCondition +} + +// 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 +} + +// 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) +} + +// 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 +} + +// itemToLedgerBlock converts stack item into *LedgerBlock. +func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { + if err != nil { + return nil, err + } + var res = new(LedgerBlock) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerBlock from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerBlock) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 9 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.PrevHash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field PrevHash: %w", err) + } + + index++ + res.MerkleRoot, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field MerkleRoot: %w", err) + } + + index++ + res.Timestamp, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Timestamp: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Index, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Index: %w", err) + } + + index++ + res.NextConsensus, 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 NextConsensus: %w", err) + } + + index++ + res.TransactionsLength, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field TransactionsLength: %w", err) + } + + return nil +} + +// itemToLedgerBlockSR converts stack item into *LedgerBlockSR. +func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) { + if err != nil { + return nil, err + } + var res = new(LedgerBlockSR) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerBlockSR from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerBlockSR) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 10 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.PrevHash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field PrevHash: %w", err) + } + + index++ + res.MerkleRoot, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field MerkleRoot: %w", err) + } + + index++ + res.Timestamp, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Timestamp: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Index, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Index: %w", err) + } + + index++ + res.NextConsensus, 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 NextConsensus: %w", err) + } + + index++ + res.TransactionsLength, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field TransactionsLength: %w", err) + } + + index++ + res.PrevStateRoot, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field PrevStateRoot: %w", err) + } + + return nil +} + +// itemToLedgerTransaction converts stack item into *LedgerTransaction. +func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction, error) { + if err != nil { + return nil, err + } + var res = new(LedgerTransaction) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerTransaction from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerTransaction) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 8 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Sender, 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 Sender: %w", err) + } + + index++ + res.SysFee, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field SysFee: %w", err) + } + + index++ + res.NetFee, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field NetFee: %w", err) + } + + index++ + res.ValidUntilBlock, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field ValidUntilBlock: %w", err) + } + + index++ + res.Script, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field Script: %w", err) + } + + return nil +} + +// itemToLedgerTransactionSigner converts stack item into *LedgerTransactionSigner. +func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTransactionSigner, error) { + if err != nil { + return nil, err + } + var res = new(LedgerTransactionSigner) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerTransactionSigner from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerTransactionSigner) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 5 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Account, 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 Account: %w", err) + } + + index++ + res.Scopes, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Scopes: %w", err) + } + + index++ + res.AllowedContracts, 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 + } (arr[index]) + if err != nil { + return fmt.Errorf("field AllowedContracts: %w", err) + } + + index++ + res.AllowedGroups, err = func (item stackitem.Item) (keys.PublicKeys, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make(keys.PublicKeys, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, 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 AllowedGroups: %w", err) + } + + index++ + res.Rules, err = func (item stackitem.Item) ([]*LedgerWitnessRule, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*LedgerWitnessRule, len(arr)) + for i := range res { + res[i], err = itemToLedgerWitnessRule(arr[i], 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 Rules: %w", err) + } + + return nil +} + +// itemToLedgerWitnessCondition converts stack item into *LedgerWitnessCondition. +func itemToLedgerWitnessCondition(item stackitem.Item, err error) (*LedgerWitnessCondition, error) { + if err != nil { + return nil, err + } + var res = new(LedgerWitnessCondition) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerWitnessCondition from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerWitnessCondition) 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.Type, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Type: %w", err) + } + + index++ + res.Value, err = arr[index].Value(), error(nil) + if err != nil { + return fmt.Errorf("field Value: %w", err) + } + + return nil +} + +// itemToLedgerWitnessRule converts stack item into *LedgerWitnessRule. +func itemToLedgerWitnessRule(item stackitem.Item, err error) (*LedgerWitnessRule, error) { + if err != nil { + return nil, err + } + var res = new(LedgerWitnessRule) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerWitnessRule from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerWitnessRule) 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.Action, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Action: %w", err) + } + + index++ + res.Condition, err = itemToLedgerWitnessCondition(arr[index], nil) + if err != nil { + return fmt.Errorf("field Condition: %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 +} diff --git a/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out b/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out new file mode 100755 index 000000000..eb5c650b1 --- /dev/null +++ b/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out @@ -0,0 +1,1141 @@ +// Package structs contains RPC wrappers for Notifications contract. +package structs + +import ( + "crypto/elliptic" + "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/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} + +// LedgerBlock is a contract-specific ledger.Block type used by its methods. +type LedgerBlock struct { + Hash util.Uint256 + Version *big.Int + PrevHash util.Uint256 + MerkleRoot util.Uint256 + Timestamp *big.Int + Nonce *big.Int + Index *big.Int + NextConsensus util.Uint160 + TransactionsLength *big.Int +} + +// LedgerBlockSR is a contract-specific ledger.BlockSR type used by its methods. +type LedgerBlockSR struct { + Hash util.Uint256 + Version *big.Int + PrevHash util.Uint256 + MerkleRoot util.Uint256 + Timestamp *big.Int + Nonce *big.Int + Index *big.Int + NextConsensus util.Uint160 + TransactionsLength *big.Int + PrevStateRoot util.Uint256 +} + +// LedgerTransaction is a contract-specific ledger.Transaction type used by its methods. +type LedgerTransaction struct { + Hash util.Uint256 + Version *big.Int + Nonce *big.Int + Sender util.Uint160 + SysFee *big.Int + NetFee *big.Int + ValidUntilBlock *big.Int + Script []byte +} + +// LedgerTransactionSigner is a contract-specific ledger.TransactionSigner type used by its methods. +type LedgerTransactionSigner struct { + Account util.Uint160 + Scopes *big.Int + AllowedContracts []util.Uint160 + AllowedGroups keys.PublicKeys + Rules []*LedgerWitnessRule +} + +// LedgerWitnessCondition is a contract-specific ledger.WitnessCondition type used by its methods. +type LedgerWitnessCondition struct { + Type *big.Int + Value any +} + +// LedgerWitnessRule is a contract-specific ledger.WitnessRule type used by its methods. +type LedgerWitnessRule struct { + Action *big.Int + Condition *LedgerWitnessCondition +} + +// Unnamed is a contract-specific unnamed type used by its methods. +type Unnamed struct { + I *big.Int + B bool +} + +// 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 +} + +// 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) +} + +// itemToLedgerBlock converts stack item into *LedgerBlock. +func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { + if err != nil { + return nil, err + } + var res = new(LedgerBlock) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerBlock from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerBlock) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 9 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.PrevHash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field PrevHash: %w", err) + } + + index++ + res.MerkleRoot, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field MerkleRoot: %w", err) + } + + index++ + res.Timestamp, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Timestamp: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Index, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Index: %w", err) + } + + index++ + res.NextConsensus, 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 NextConsensus: %w", err) + } + + index++ + res.TransactionsLength, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field TransactionsLength: %w", err) + } + + return nil +} + +// itemToLedgerBlockSR converts stack item into *LedgerBlockSR. +func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) { + if err != nil { + return nil, err + } + var res = new(LedgerBlockSR) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerBlockSR from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerBlockSR) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 10 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.PrevHash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field PrevHash: %w", err) + } + + index++ + res.MerkleRoot, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field MerkleRoot: %w", err) + } + + index++ + res.Timestamp, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Timestamp: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Index, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Index: %w", err) + } + + index++ + res.NextConsensus, 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 NextConsensus: %w", err) + } + + index++ + res.TransactionsLength, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field TransactionsLength: %w", err) + } + + index++ + res.PrevStateRoot, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field PrevStateRoot: %w", err) + } + + return nil +} + +// itemToLedgerTransaction converts stack item into *LedgerTransaction. +func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction, error) { + if err != nil { + return nil, err + } + var res = new(LedgerTransaction) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerTransaction from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerTransaction) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 8 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Sender, 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 Sender: %w", err) + } + + index++ + res.SysFee, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field SysFee: %w", err) + } + + index++ + res.NetFee, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field NetFee: %w", err) + } + + index++ + res.ValidUntilBlock, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field ValidUntilBlock: %w", err) + } + + index++ + res.Script, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field Script: %w", err) + } + + return nil +} + +// itemToLedgerTransactionSigner converts stack item into *LedgerTransactionSigner. +func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTransactionSigner, error) { + if err != nil { + return nil, err + } + var res = new(LedgerTransactionSigner) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerTransactionSigner from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerTransactionSigner) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 5 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Account, 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 Account: %w", err) + } + + index++ + res.Scopes, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Scopes: %w", err) + } + + index++ + res.AllowedContracts, 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 + } (arr[index]) + if err != nil { + return fmt.Errorf("field AllowedContracts: %w", err) + } + + index++ + res.AllowedGroups, err = func (item stackitem.Item) (keys.PublicKeys, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make(keys.PublicKeys, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, 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 AllowedGroups: %w", err) + } + + index++ + res.Rules, err = func (item stackitem.Item) ([]*LedgerWitnessRule, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*LedgerWitnessRule, len(arr)) + for i := range res { + res[i], err = itemToLedgerWitnessRule(arr[i], 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 Rules: %w", err) + } + + return nil +} + +// itemToLedgerWitnessCondition converts stack item into *LedgerWitnessCondition. +func itemToLedgerWitnessCondition(item stackitem.Item, err error) (*LedgerWitnessCondition, error) { + if err != nil { + return nil, err + } + var res = new(LedgerWitnessCondition) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerWitnessCondition from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerWitnessCondition) 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.Type, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Type: %w", err) + } + + index++ + res.Value, err = arr[index].Value(), error(nil) + if err != nil { + return fmt.Errorf("field Value: %w", err) + } + + return nil +} + +// itemToLedgerWitnessRule converts stack item into *LedgerWitnessRule. +func itemToLedgerWitnessRule(item stackitem.Item, err error) (*LedgerWitnessRule, error) { + if err != nil { + return nil, err + } + var res = new(LedgerWitnessRule) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerWitnessRule from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerWitnessRule) 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.Action, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Action: %w", err) + } + + index++ + res.Condition, err = itemToLedgerWitnessCondition(arr[index], nil) + if err != nil { + return fmt.Errorf("field Condition: %w", err) + } + + return 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 +} + +// 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 +} diff --git a/cli/smartcontract/testdata/structs/rpcbindings.out b/cli/smartcontract/testdata/structs/rpcbindings.out index a8330072b..32acf3089 100644 --- a/cli/smartcontract/testdata/structs/rpcbindings.out +++ b/cli/smartcontract/testdata/structs/rpcbindings.out @@ -17,7 +17,6 @@ import ( // 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} - // LedgerBlock is a contract-specific ledger.Block type used by its methods. type LedgerBlock struct { Hash util.Uint256 @@ -69,7 +68,7 @@ type LedgerTransactionSigner struct { // LedgerWitnessCondition is a contract-specific ledger.WitnessCondition type used by its methods. type LedgerWitnessCondition struct { Type *big.Int - Value interface{} + Value any } // LedgerWitnessRule is a contract-specific ledger.WitnessRule type used by its methods. @@ -114,7 +113,7 @@ type ManagementManifest struct { ABI *ManagementABI Permissions []*ManagementPermission Trusts []util.Uint160 - Extra interface{} + Extra any } // ManagementMethod is a contract-specific management.Method type used by its methods. @@ -154,40 +153,42 @@ type StructsInternal struct { Map map[*big.Int]keys.PublicKeys Struct *StructsInternal } + // Invoker is used by ContractReader to call various safe methods. type Invoker interface { - Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) + 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 { - return &ContractReader{invoker} + var hash = Hash + return &ContractReader{invoker, hash} } - // Block invokes `block` method of contract. func (c *ContractReader) Block(b *LedgerBlock) (*LedgerBlock, error) { - return itemToLedgerBlock(unwrap.Item(c.invoker.Call(Hash, "block", b))) + return itemToLedgerBlock(unwrap.Item(c.invoker.Call(c.hash, "block", b))) } // Contract invokes `contract` method of contract. func (c *ContractReader) Contract(mc *ManagementContract) (*ManagementContract, error) { - return itemToManagementContract(unwrap.Item(c.invoker.Call(Hash, "contract", mc))) + return itemToManagementContract(unwrap.Item(c.invoker.Call(c.hash, "contract", mc))) } // Struct invokes `struct` method of contract. func (c *ContractReader) Struct(s *StructsInternal) (*StructsInternal, error) { - return itemToStructsInternal(unwrap.Item(c.invoker.Call(Hash, "struct", s))) + return itemToStructsInternal(unwrap.Item(c.invoker.Call(c.hash, "struct", s))) } // Transaction invokes `transaction` method of contract. func (c *ContractReader) Transaction(t *LedgerTransaction) (*LedgerTransaction, error) { - return itemToLedgerTransaction(unwrap.Item(c.invoker.Call(Hash, "transaction", t))) + return itemToLedgerTransaction(unwrap.Item(c.invoker.Call(c.hash, "transaction", t))) } // itemToLedgerBlock converts stack item into *LedgerBlock. @@ -195,16 +196,26 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { if err != nil { return nil, err } + var res = new(LedgerBlock) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerBlock from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerBlock) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 9 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(LedgerBlock) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { b, err := item.TryBytes() @@ -218,13 +229,13 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Hash: %w", err) + return fmt.Errorf("field Hash: %w", err) } index++ res.Version, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Version: %w", err) + return fmt.Errorf("field Version: %w", err) } index++ @@ -240,7 +251,7 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field PrevHash: %w", err) + return fmt.Errorf("field PrevHash: %w", err) } index++ @@ -256,25 +267,25 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field MerkleRoot: %w", err) + return fmt.Errorf("field MerkleRoot: %w", err) } index++ res.Timestamp, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Timestamp: %w", err) + return fmt.Errorf("field Timestamp: %w", err) } index++ res.Nonce, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Nonce: %w", err) + return fmt.Errorf("field Nonce: %w", err) } index++ res.Index, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Index: %w", err) + return fmt.Errorf("field Index: %w", err) } index++ @@ -290,17 +301,16 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field NextConsensus: %w", err) + return fmt.Errorf("field NextConsensus: %w", err) } index++ res.TransactionsLength, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field TransactionsLength: %w", err) + return fmt.Errorf("field TransactionsLength: %w", err) } - - return res, err + return nil } // itemToLedgerBlockSR converts stack item into *LedgerBlockSR. @@ -308,16 +318,26 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) if err != nil { return nil, err } + var res = new(LedgerBlockSR) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerBlockSR from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerBlockSR) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 10 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(LedgerBlockSR) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { b, err := item.TryBytes() @@ -331,13 +351,13 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Hash: %w", err) + return fmt.Errorf("field Hash: %w", err) } index++ res.Version, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Version: %w", err) + return fmt.Errorf("field Version: %w", err) } index++ @@ -353,7 +373,7 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field PrevHash: %w", err) + return fmt.Errorf("field PrevHash: %w", err) } index++ @@ -369,25 +389,25 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field MerkleRoot: %w", err) + return fmt.Errorf("field MerkleRoot: %w", err) } index++ res.Timestamp, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Timestamp: %w", err) + return fmt.Errorf("field Timestamp: %w", err) } index++ res.Nonce, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Nonce: %w", err) + return fmt.Errorf("field Nonce: %w", err) } index++ res.Index, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Index: %w", err) + return fmt.Errorf("field Index: %w", err) } index++ @@ -403,13 +423,13 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field NextConsensus: %w", err) + return fmt.Errorf("field NextConsensus: %w", err) } index++ res.TransactionsLength, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field TransactionsLength: %w", err) + return fmt.Errorf("field TransactionsLength: %w", err) } index++ @@ -425,11 +445,10 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field PrevStateRoot: %w", err) + return fmt.Errorf("field PrevStateRoot: %w", err) } - - return res, err + return nil } // itemToLedgerTransaction converts stack item into *LedgerTransaction. @@ -437,16 +456,26 @@ func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction if err != nil { return nil, err } + var res = new(LedgerTransaction) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerTransaction from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerTransaction) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 8 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(LedgerTransaction) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { b, err := item.TryBytes() @@ -460,19 +489,19 @@ func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Hash: %w", err) + return fmt.Errorf("field Hash: %w", err) } index++ res.Version, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Version: %w", err) + return fmt.Errorf("field Version: %w", err) } index++ res.Nonce, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Nonce: %w", err) + return fmt.Errorf("field Nonce: %w", err) } index++ @@ -488,35 +517,34 @@ func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Sender: %w", err) + return fmt.Errorf("field Sender: %w", err) } index++ res.SysFee, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field SysFee: %w", err) + return fmt.Errorf("field SysFee: %w", err) } index++ res.NetFee, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field NetFee: %w", err) + return fmt.Errorf("field NetFee: %w", err) } index++ res.ValidUntilBlock, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field ValidUntilBlock: %w", err) + return fmt.Errorf("field ValidUntilBlock: %w", err) } index++ res.Script, err = arr[index].TryBytes() if err != nil { - return nil, fmt.Errorf("field Script: %w", err) + return fmt.Errorf("field Script: %w", err) } - - return res, err + return nil } // itemToLedgerTransactionSigner converts stack item into *LedgerTransactionSigner. @@ -524,16 +552,26 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans if err != nil { return nil, err } + var res = new(LedgerTransactionSigner) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerTransactionSigner from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerTransactionSigner) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 5 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(LedgerTransactionSigner) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Account, err = func (item stackitem.Item) (util.Uint160, error) { b, err := item.TryBytes() @@ -547,13 +585,13 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Account: %w", err) + return fmt.Errorf("field Account: %w", err) } index++ res.Scopes, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Scopes: %w", err) + return fmt.Errorf("field Scopes: %w", err) } index++ @@ -582,7 +620,7 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field AllowedContracts: %w", err) + return fmt.Errorf("field AllowedContracts: %w", err) } index++ @@ -611,7 +649,7 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field AllowedGroups: %w", err) + return fmt.Errorf("field AllowedGroups: %w", err) } index++ @@ -630,11 +668,10 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Rules: %w", err) + return fmt.Errorf("field Rules: %w", err) } - - return res, err + return nil } // itemToLedgerWitnessCondition converts stack item into *LedgerWitnessCondition. @@ -642,30 +679,39 @@ func itemToLedgerWitnessCondition(item stackitem.Item, err error) (*LedgerWitnes if err != nil { return nil, err } + var res = new(LedgerWitnessCondition) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerWitnessCondition from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerWitnessCondition) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 2 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(LedgerWitnessCondition) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Type, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Type: %w", err) + return fmt.Errorf("field Type: %w", err) } index++ - res.Value, err = arr[index].Value(), nil + res.Value, err = arr[index].Value(), error(nil) if err != nil { - return nil, fmt.Errorf("field Value: %w", err) + return fmt.Errorf("field Value: %w", err) } - - return res, err + return nil } // itemToLedgerWitnessRule converts stack item into *LedgerWitnessRule. @@ -673,30 +719,39 @@ func itemToLedgerWitnessRule(item stackitem.Item, err error) (*LedgerWitnessRule if err != nil { return nil, err } + var res = new(LedgerWitnessRule) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerWitnessRule from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerWitnessRule) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 2 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(LedgerWitnessRule) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Action, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Action: %w", err) + return fmt.Errorf("field Action: %w", err) } index++ res.Condition, err = itemToLedgerWitnessCondition(arr[index], nil) if err != nil { - return nil, fmt.Errorf("field Condition: %w", err) + return fmt.Errorf("field Condition: %w", err) } - - return res, err + return nil } // itemToManagementABI converts stack item into *ManagementABI. @@ -704,16 +759,26 @@ func itemToManagementABI(item stackitem.Item, err error) (*ManagementABI, error) if err != nil { return nil, err } + var res = new(ManagementABI) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementABI from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *ManagementABI) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 2 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(ManagementABI) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Methods, err = func (item stackitem.Item) ([]*ManagementMethod, error) { arr, ok := item.Value().([]stackitem.Item) @@ -730,7 +795,7 @@ func itemToManagementABI(item stackitem.Item, err error) (*ManagementABI, error) return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Methods: %w", err) + return fmt.Errorf("field Methods: %w", err) } index++ @@ -749,11 +814,10 @@ func itemToManagementABI(item stackitem.Item, err error) (*ManagementABI, error) return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Events: %w", err) + return fmt.Errorf("field Events: %w", err) } - - return res, err + return nil } // itemToManagementContract converts stack item into *ManagementContract. @@ -761,26 +825,36 @@ func itemToManagementContract(item stackitem.Item, err error) (*ManagementContra if err != nil { return nil, err } + var res = new(ManagementContract) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementContract from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *ManagementContract) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 5 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(ManagementContract) - var index = -1 + var ( + index = -1 + err error + ) index++ res.ID, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field ID: %w", err) + return fmt.Errorf("field ID: %w", err) } index++ res.UpdateCounter, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field UpdateCounter: %w", err) + return fmt.Errorf("field UpdateCounter: %w", err) } index++ @@ -796,23 +870,22 @@ func itemToManagementContract(item stackitem.Item, err error) (*ManagementContra return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Hash: %w", err) + return fmt.Errorf("field Hash: %w", err) } index++ res.NEF, err = arr[index].TryBytes() if err != nil { - return nil, fmt.Errorf("field NEF: %w", err) + return fmt.Errorf("field NEF: %w", err) } index++ res.Manifest, err = itemToManagementManifest(arr[index], nil) if err != nil { - return nil, fmt.Errorf("field Manifest: %w", err) + return fmt.Errorf("field Manifest: %w", err) } - - return res, err + return nil } // itemToManagementEvent converts stack item into *ManagementEvent. @@ -820,16 +893,26 @@ func itemToManagementEvent(item stackitem.Item, err error) (*ManagementEvent, er if err != nil { return nil, err } + var res = new(ManagementEvent) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementEvent from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *ManagementEvent) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 2 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(ManagementEvent) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Name, err = func (item stackitem.Item) (string, error) { b, err := item.TryBytes() @@ -842,7 +925,7 @@ func itemToManagementEvent(item stackitem.Item, err error) (*ManagementEvent, er return string(b), nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Name: %w", err) + return fmt.Errorf("field Name: %w", err) } index++ @@ -861,11 +944,10 @@ func itemToManagementEvent(item stackitem.Item, err error) (*ManagementEvent, er return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Params: %w", err) + return fmt.Errorf("field Params: %w", err) } - - return res, err + return nil } // itemToManagementGroup converts stack item into *ManagementGroup. @@ -873,16 +955,26 @@ func itemToManagementGroup(item stackitem.Item, err error) (*ManagementGroup, er if err != nil { return nil, err } + var res = new(ManagementGroup) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementGroup from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *ManagementGroup) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 2 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(ManagementGroup) - var index = -1 + var ( + index = -1 + err error + ) index++ res.PublicKey, err = func (item stackitem.Item) (*keys.PublicKey, error) { b, err := item.TryBytes() @@ -896,17 +988,16 @@ func itemToManagementGroup(item stackitem.Item, err error) (*ManagementGroup, er return k, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field PublicKey: %w", err) + return fmt.Errorf("field PublicKey: %w", err) } index++ res.Signature, err = arr[index].TryBytes() if err != nil { - return nil, fmt.Errorf("field Signature: %w", err) + return fmt.Errorf("field Signature: %w", err) } - - return res, err + return nil } // itemToManagementManifest converts stack item into *ManagementManifest. @@ -914,16 +1005,26 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife if err != nil { return nil, err } + var res = new(ManagementManifest) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementManifest from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *ManagementManifest) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 8 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(ManagementManifest) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Name, err = func (item stackitem.Item) (string, error) { b, err := item.TryBytes() @@ -936,7 +1037,7 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife return string(b), nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Name: %w", err) + return fmt.Errorf("field Name: %w", err) } index++ @@ -955,7 +1056,7 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Groups: %w", err) + return fmt.Errorf("field Groups: %w", err) } index++ @@ -997,7 +1098,7 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Features: %w", err) + return fmt.Errorf("field Features: %w", err) } index++ @@ -1025,13 +1126,13 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field SupportedStandards: %w", err) + return fmt.Errorf("field SupportedStandards: %w", err) } index++ res.ABI, err = itemToManagementABI(arr[index], nil) if err != nil { - return nil, fmt.Errorf("field ABI: %w", err) + return fmt.Errorf("field ABI: %w", err) } index++ @@ -1050,7 +1151,7 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Permissions: %w", err) + return fmt.Errorf("field Permissions: %w", err) } index++ @@ -1079,17 +1180,16 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Trusts: %w", err) + return fmt.Errorf("field Trusts: %w", err) } index++ - res.Extra, err = arr[index].Value(), nil + res.Extra, err = arr[index].Value(), error(nil) if err != nil { - return nil, fmt.Errorf("field Extra: %w", err) + return fmt.Errorf("field Extra: %w", err) } - - return res, err + return nil } // itemToManagementMethod converts stack item into *ManagementMethod. @@ -1097,16 +1197,26 @@ func itemToManagementMethod(item stackitem.Item, err error) (*ManagementMethod, if err != nil { return nil, err } + var res = new(ManagementMethod) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementMethod from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *ManagementMethod) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 5 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(ManagementMethod) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Name, err = func (item stackitem.Item) (string, error) { b, err := item.TryBytes() @@ -1119,7 +1229,7 @@ func itemToManagementMethod(item stackitem.Item, err error) (*ManagementMethod, return string(b), nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Name: %w", err) + return fmt.Errorf("field Name: %w", err) } index++ @@ -1138,29 +1248,28 @@ func itemToManagementMethod(item stackitem.Item, err error) (*ManagementMethod, return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Params: %w", err) + return fmt.Errorf("field Params: %w", err) } index++ res.ReturnType, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field ReturnType: %w", err) + return fmt.Errorf("field ReturnType: %w", err) } index++ res.Offset, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Offset: %w", err) + return fmt.Errorf("field Offset: %w", err) } index++ res.Safe, err = arr[index].TryBool() if err != nil { - return nil, fmt.Errorf("field Safe: %w", err) + return fmt.Errorf("field Safe: %w", err) } - - return res, err + return nil } // itemToManagementParameter converts stack item into *ManagementParameter. @@ -1168,16 +1277,26 @@ func itemToManagementParameter(item stackitem.Item, err error) (*ManagementParam if err != nil { return nil, err } + var res = new(ManagementParameter) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementParameter from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *ManagementParameter) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 2 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(ManagementParameter) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Name, err = func (item stackitem.Item) (string, error) { b, err := item.TryBytes() @@ -1190,17 +1309,16 @@ func itemToManagementParameter(item stackitem.Item, err error) (*ManagementParam return string(b), nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Name: %w", err) + return fmt.Errorf("field Name: %w", err) } index++ res.Type, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Type: %w", err) + return fmt.Errorf("field Type: %w", err) } - - return res, err + return nil } // itemToManagementPermission converts stack item into *ManagementPermission. @@ -1208,16 +1326,26 @@ func itemToManagementPermission(item stackitem.Item, err error) (*ManagementPerm if err != nil { return nil, err } + var res = new(ManagementPermission) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementPermission from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *ManagementPermission) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 2 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(ManagementPermission) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Contract, err = func (item stackitem.Item) (util.Uint160, error) { b, err := item.TryBytes() @@ -1231,7 +1359,7 @@ func itemToManagementPermission(item stackitem.Item, err error) (*ManagementPerm return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Contract: %w", err) + return fmt.Errorf("field Contract: %w", err) } index++ @@ -1259,11 +1387,10 @@ func itemToManagementPermission(item stackitem.Item, err error) (*ManagementPerm return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Methods: %w", err) + return fmt.Errorf("field Methods: %w", err) } - - return res, err + return nil } // itemToStructsInternal converts stack item into *StructsInternal. @@ -1271,32 +1398,42 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er if err != nil { return nil, err } + var res = new(StructsInternal) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of StructsInternal from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *StructsInternal) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 13 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(StructsInternal) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Bool, err = arr[index].TryBool() if err != nil { - return nil, fmt.Errorf("field Bool: %w", err) + return fmt.Errorf("field Bool: %w", err) } index++ res.Int, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Int: %w", err) + return fmt.Errorf("field Int: %w", err) } index++ res.Bytes, err = arr[index].TryBytes() if err != nil { - return nil, fmt.Errorf("field Bytes: %w", err) + return fmt.Errorf("field Bytes: %w", err) } index++ @@ -1311,7 +1448,7 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return string(b), nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field String: %w", err) + return fmt.Errorf("field String: %w", err) } index++ @@ -1327,7 +1464,7 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field H160: %w", err) + return fmt.Errorf("field H160: %w", err) } index++ @@ -1343,7 +1480,7 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field H256: %w", err) + return fmt.Errorf("field H256: %w", err) } index++ @@ -1359,7 +1496,7 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return k, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field PK: %w", err) + return fmt.Errorf("field PK: %w", err) } index++ @@ -1375,13 +1512,13 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return k, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field PubKey: %w", err) + return fmt.Errorf("field PubKey: %w", err) } index++ res.Sign, err = arr[index].TryBytes() if err != nil { - return nil, fmt.Errorf("field Sign: %w", err) + return fmt.Errorf("field Sign: %w", err) } index++ @@ -1400,7 +1537,7 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field ArrOfBytes: %w", err) + return fmt.Errorf("field ArrOfBytes: %w", err) } index++ @@ -1429,7 +1566,7 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field ArrOfH160: %w", err) + return fmt.Errorf("field ArrOfH160: %w", err) } index++ @@ -1476,15 +1613,14 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Map: %w", err) + return fmt.Errorf("field Map: %w", err) } index++ res.Struct, err = itemToStructsInternal(arr[index], nil) if err != nil { - return nil, fmt.Errorf("field Struct: %w", err) + return fmt.Errorf("field Struct: %w", err) } - - return res, err + return nil } diff --git a/cli/smartcontract/testdata/structs/rpcbindings_dynamic_hash.out b/cli/smartcontract/testdata/structs/rpcbindings_dynamic_hash.out new file mode 100755 index 000000000..69558da30 --- /dev/null +++ b/cli/smartcontract/testdata/structs/rpcbindings_dynamic_hash.out @@ -0,0 +1,1622 @@ +// Package structs contains RPC wrappers for Types contract. +package structs + +import ( + "crypto/elliptic" + "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" +) + +// LedgerBlock is a contract-specific ledger.Block type used by its methods. +type LedgerBlock struct { + Hash util.Uint256 + Version *big.Int + PrevHash util.Uint256 + MerkleRoot util.Uint256 + Timestamp *big.Int + Nonce *big.Int + Index *big.Int + NextConsensus util.Uint160 + TransactionsLength *big.Int +} + +// LedgerBlockSR is a contract-specific ledger.BlockSR type used by its methods. +type LedgerBlockSR struct { + Hash util.Uint256 + Version *big.Int + PrevHash util.Uint256 + MerkleRoot util.Uint256 + Timestamp *big.Int + Nonce *big.Int + Index *big.Int + NextConsensus util.Uint160 + TransactionsLength *big.Int + PrevStateRoot util.Uint256 +} + +// LedgerTransaction is a contract-specific ledger.Transaction type used by its methods. +type LedgerTransaction struct { + Hash util.Uint256 + Version *big.Int + Nonce *big.Int + Sender util.Uint160 + SysFee *big.Int + NetFee *big.Int + ValidUntilBlock *big.Int + Script []byte +} + +// LedgerTransactionSigner is a contract-specific ledger.TransactionSigner type used by its methods. +type LedgerTransactionSigner struct { + Account util.Uint160 + Scopes *big.Int + AllowedContracts []util.Uint160 + AllowedGroups keys.PublicKeys + Rules []*LedgerWitnessRule +} + +// LedgerWitnessCondition is a contract-specific ledger.WitnessCondition type used by its methods. +type LedgerWitnessCondition struct { + Type *big.Int + Value any +} + +// LedgerWitnessRule is a contract-specific ledger.WitnessRule type used by its methods. +type LedgerWitnessRule struct { + Action *big.Int + Condition *LedgerWitnessCondition +} + +// ManagementABI is a contract-specific management.ABI type used by its methods. +type ManagementABI struct { + Methods []*ManagementMethod + Events []*ManagementEvent +} + +// ManagementContract is a contract-specific management.Contract type used by its methods. +type ManagementContract struct { + ID *big.Int + UpdateCounter *big.Int + Hash util.Uint160 + NEF []byte + Manifest *ManagementManifest +} + +// ManagementEvent is a contract-specific management.Event type used by its methods. +type ManagementEvent struct { + Name string + Params []*ManagementParameter +} + +// ManagementGroup is a contract-specific management.Group type used by its methods. +type ManagementGroup struct { + PublicKey *keys.PublicKey + Signature []byte +} + +// ManagementManifest is a contract-specific management.Manifest type used by its methods. +type ManagementManifest struct { + Name string + Groups []*ManagementGroup + Features map[string]string + SupportedStandards []string + ABI *ManagementABI + Permissions []*ManagementPermission + Trusts []util.Uint160 + Extra any +} + +// ManagementMethod is a contract-specific management.Method type used by its methods. +type ManagementMethod struct { + Name string + Params []*ManagementParameter + ReturnType *big.Int + Offset *big.Int + Safe bool +} + +// ManagementParameter is a contract-specific management.Parameter type used by its methods. +type ManagementParameter struct { + Name string + Type *big.Int +} + +// ManagementPermission is a contract-specific management.Permission type used by its methods. +type ManagementPermission struct { + Contract util.Uint160 + Methods []string +} + +// StructsInternal is a contract-specific structs.Internal type used by its methods. +type StructsInternal struct { + Bool bool + Int *big.Int + Bytes []byte + String string + H160 util.Uint160 + H256 util.Uint256 + PK *keys.PublicKey + PubKey *keys.PublicKey + Sign []byte + ArrOfBytes [][]byte + ArrOfH160 []util.Uint160 + Map map[*big.Int]keys.PublicKeys + Struct *StructsInternal +} + +// 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} +} + +// Block invokes `block` method of contract. +func (c *ContractReader) Block(b *LedgerBlock) (*LedgerBlock, error) { + return itemToLedgerBlock(unwrap.Item(c.invoker.Call(c.hash, "block", b))) +} + +// Contract invokes `contract` method of contract. +func (c *ContractReader) Contract(mc *ManagementContract) (*ManagementContract, error) { + return itemToManagementContract(unwrap.Item(c.invoker.Call(c.hash, "contract", mc))) +} + +// Struct invokes `struct` method of contract. +func (c *ContractReader) Struct(s *StructsInternal) (*StructsInternal, error) { + return itemToStructsInternal(unwrap.Item(c.invoker.Call(c.hash, "struct", s))) +} + +// Transaction invokes `transaction` method of contract. +func (c *ContractReader) Transaction(t *LedgerTransaction) (*LedgerTransaction, error) { + return itemToLedgerTransaction(unwrap.Item(c.invoker.Call(c.hash, "transaction", t))) +} + +// itemToLedgerBlock converts stack item into *LedgerBlock. +func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { + if err != nil { + return nil, err + } + var res = new(LedgerBlock) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerBlock from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerBlock) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 9 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.PrevHash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field PrevHash: %w", err) + } + + index++ + res.MerkleRoot, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field MerkleRoot: %w", err) + } + + index++ + res.Timestamp, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Timestamp: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Index, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Index: %w", err) + } + + index++ + res.NextConsensus, 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 NextConsensus: %w", err) + } + + index++ + res.TransactionsLength, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field TransactionsLength: %w", err) + } + + return nil +} + +// itemToLedgerBlockSR converts stack item into *LedgerBlockSR. +func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) { + if err != nil { + return nil, err + } + var res = new(LedgerBlockSR) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerBlockSR from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerBlockSR) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 10 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.PrevHash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field PrevHash: %w", err) + } + + index++ + res.MerkleRoot, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field MerkleRoot: %w", err) + } + + index++ + res.Timestamp, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Timestamp: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Index, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Index: %w", err) + } + + index++ + res.NextConsensus, 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 NextConsensus: %w", err) + } + + index++ + res.TransactionsLength, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field TransactionsLength: %w", err) + } + + index++ + res.PrevStateRoot, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field PrevStateRoot: %w", err) + } + + return nil +} + +// itemToLedgerTransaction converts stack item into *LedgerTransaction. +func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction, error) { + if err != nil { + return nil, err + } + var res = new(LedgerTransaction) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerTransaction from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerTransaction) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 8 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Sender, 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 Sender: %w", err) + } + + index++ + res.SysFee, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field SysFee: %w", err) + } + + index++ + res.NetFee, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field NetFee: %w", err) + } + + index++ + res.ValidUntilBlock, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field ValidUntilBlock: %w", err) + } + + index++ + res.Script, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field Script: %w", err) + } + + return nil +} + +// itemToLedgerTransactionSigner converts stack item into *LedgerTransactionSigner. +func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTransactionSigner, error) { + if err != nil { + return nil, err + } + var res = new(LedgerTransactionSigner) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerTransactionSigner from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerTransactionSigner) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 5 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Account, 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 Account: %w", err) + } + + index++ + res.Scopes, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Scopes: %w", err) + } + + index++ + res.AllowedContracts, 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 + } (arr[index]) + if err != nil { + return fmt.Errorf("field AllowedContracts: %w", err) + } + + index++ + res.AllowedGroups, err = func (item stackitem.Item) (keys.PublicKeys, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make(keys.PublicKeys, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, 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 AllowedGroups: %w", err) + } + + index++ + res.Rules, err = func (item stackitem.Item) ([]*LedgerWitnessRule, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*LedgerWitnessRule, len(arr)) + for i := range res { + res[i], err = itemToLedgerWitnessRule(arr[i], 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 Rules: %w", err) + } + + return nil +} + +// itemToLedgerWitnessCondition converts stack item into *LedgerWitnessCondition. +func itemToLedgerWitnessCondition(item stackitem.Item, err error) (*LedgerWitnessCondition, error) { + if err != nil { + return nil, err + } + var res = new(LedgerWitnessCondition) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerWitnessCondition from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerWitnessCondition) 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.Type, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Type: %w", err) + } + + index++ + res.Value, err = arr[index].Value(), error(nil) + if err != nil { + return fmt.Errorf("field Value: %w", err) + } + + return nil +} + +// itemToLedgerWitnessRule converts stack item into *LedgerWitnessRule. +func itemToLedgerWitnessRule(item stackitem.Item, err error) (*LedgerWitnessRule, error) { + if err != nil { + return nil, err + } + var res = new(LedgerWitnessRule) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerWitnessRule from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *LedgerWitnessRule) 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.Action, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Action: %w", err) + } + + index++ + res.Condition, err = itemToLedgerWitnessCondition(arr[index], nil) + if err != nil { + return fmt.Errorf("field Condition: %w", err) + } + + return nil +} + +// itemToManagementABI converts stack item into *ManagementABI. +func itemToManagementABI(item stackitem.Item, err error) (*ManagementABI, error) { + if err != nil { + return nil, err + } + var res = new(ManagementABI) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementABI from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *ManagementABI) 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.Methods, err = func (item stackitem.Item) ([]*ManagementMethod, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementMethod, len(arr)) + for i := range res { + res[i], err = itemToManagementMethod(arr[i], 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 Methods: %w", err) + } + + index++ + res.Events, err = func (item stackitem.Item) ([]*ManagementEvent, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementEvent, len(arr)) + for i := range res { + res[i], err = itemToManagementEvent(arr[i], 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 Events: %w", err) + } + + return nil +} + +// itemToManagementContract converts stack item into *ManagementContract. +func itemToManagementContract(item stackitem.Item, err error) (*ManagementContract, error) { + if err != nil { + return nil, err + } + var res = new(ManagementContract) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementContract from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *ManagementContract) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 5 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.ID, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field ID: %w", err) + } + + index++ + res.UpdateCounter, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field UpdateCounter: %w", err) + } + + index++ + res.Hash, 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 Hash: %w", err) + } + + index++ + res.NEF, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field NEF: %w", err) + } + + index++ + res.Manifest, err = itemToManagementManifest(arr[index], nil) + if err != nil { + return fmt.Errorf("field Manifest: %w", err) + } + + return nil +} + +// itemToManagementEvent converts stack item into *ManagementEvent. +func itemToManagementEvent(item stackitem.Item, err error) (*ManagementEvent, error) { + if err != nil { + return nil, err + } + var res = new(ManagementEvent) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementEvent from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *ManagementEvent) 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.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++ + res.Params, err = func (item stackitem.Item) ([]*ManagementParameter, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementParameter, len(arr)) + for i := range res { + res[i], err = itemToManagementParameter(arr[i], 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 Params: %w", err) + } + + return nil +} + +// itemToManagementGroup converts stack item into *ManagementGroup. +func itemToManagementGroup(item stackitem.Item, err error) (*ManagementGroup, error) { + if err != nil { + return nil, err + } + var res = new(ManagementGroup) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementGroup from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *ManagementGroup) 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.PublicKey, err = func (item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field PublicKey: %w", err) + } + + index++ + res.Signature, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field Signature: %w", err) + } + + return nil +} + +// itemToManagementManifest converts stack item into *ManagementManifest. +func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManifest, error) { + if err != nil { + return nil, err + } + var res = new(ManagementManifest) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementManifest from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *ManagementManifest) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 8 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.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++ + res.Groups, err = func (item stackitem.Item) ([]*ManagementGroup, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementGroup, len(arr)) + for i := range res { + res[i], err = itemToManagementGroup(arr[i], 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 Groups: %w", err) + } + + index++ + res.Features, err = 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 + } (arr[index]) + if err != nil { + return fmt.Errorf("field Features: %w", err) + } + + index++ + res.SupportedStandards, 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[index]) + if err != nil { + return fmt.Errorf("field SupportedStandards: %w", err) + } + + index++ + res.ABI, err = itemToManagementABI(arr[index], nil) + if err != nil { + return fmt.Errorf("field ABI: %w", err) + } + + index++ + res.Permissions, err = func (item stackitem.Item) ([]*ManagementPermission, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementPermission, len(arr)) + for i := range res { + res[i], err = itemToManagementPermission(arr[i], 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 Permissions: %w", err) + } + + index++ + res.Trusts, 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 + } (arr[index]) + if err != nil { + return fmt.Errorf("field Trusts: %w", err) + } + + index++ + res.Extra, err = arr[index].Value(), error(nil) + if err != nil { + return fmt.Errorf("field Extra: %w", err) + } + + return nil +} + +// itemToManagementMethod converts stack item into *ManagementMethod. +func itemToManagementMethod(item stackitem.Item, err error) (*ManagementMethod, error) { + if err != nil { + return nil, err + } + var res = new(ManagementMethod) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementMethod from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *ManagementMethod) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 5 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.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++ + res.Params, err = func (item stackitem.Item) ([]*ManagementParameter, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementParameter, len(arr)) + for i := range res { + res[i], err = itemToManagementParameter(arr[i], 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 Params: %w", err) + } + + index++ + res.ReturnType, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field ReturnType: %w", err) + } + + index++ + res.Offset, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Offset: %w", err) + } + + index++ + res.Safe, err = arr[index].TryBool() + if err != nil { + return fmt.Errorf("field Safe: %w", err) + } + + return nil +} + +// itemToManagementParameter converts stack item into *ManagementParameter. +func itemToManagementParameter(item stackitem.Item, err error) (*ManagementParameter, error) { + if err != nil { + return nil, err + } + var res = new(ManagementParameter) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementParameter from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *ManagementParameter) 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.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++ + res.Type, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Type: %w", err) + } + + return nil +} + +// itemToManagementPermission converts stack item into *ManagementPermission. +func itemToManagementPermission(item stackitem.Item, err error) (*ManagementPermission, error) { + if err != nil { + return nil, err + } + var res = new(ManagementPermission) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementPermission from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *ManagementPermission) 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.Contract, 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 Contract: %w", err) + } + + index++ + res.Methods, 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[index]) + if err != nil { + return fmt.Errorf("field Methods: %w", err) + } + + return nil +} + +// itemToStructsInternal converts stack item into *StructsInternal. +func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, error) { + if err != nil { + return nil, err + } + var res = new(StructsInternal) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of StructsInternal from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *StructsInternal) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 13 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Bool, err = arr[index].TryBool() + if err != nil { + return fmt.Errorf("field Bool: %w", err) + } + + index++ + res.Int, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Int: %w", err) + } + + index++ + res.Bytes, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field Bytes: %w", err) + } + + index++ + res.String, 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 String: %w", err) + } + + index++ + res.H160, 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 H160: %w", err) + } + + index++ + res.H256, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field H256: %w", err) + } + + index++ + res.PK, err = func (item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field PK: %w", err) + } + + index++ + res.PubKey, err = func (item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field PubKey: %w", err) + } + + index++ + res.Sign, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field Sign: %w", err) + } + + index++ + res.ArrOfBytes, err = func (item stackitem.Item) ([][]byte, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([][]byte, len(arr)) + for i := range res { + res[i], err = arr[i].TryBytes() + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field ArrOfBytes: %w", err) + } + + index++ + res.ArrOfH160, 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 + } (arr[index]) + if err != nil { + return fmt.Errorf("field ArrOfH160: %w", err) + } + + index++ + res.Map, err = func (item stackitem.Item) (map[*big.Int]keys.PublicKeys, 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]keys.PublicKeys) + 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) (keys.PublicKeys, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make(keys.PublicKeys, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, 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 Map: %w", err) + } + + index++ + res.Struct, err = itemToStructsInternal(arr[index], nil) + if err != nil { + return fmt.Errorf("field Struct: %w", err) + } + + return nil +} diff --git a/cli/smartcontract/testdata/types/config.yml b/cli/smartcontract/testdata/types/config.yml index b97936c40..98cb40d18 100644 --- a/cli/smartcontract/testdata/types/config.yml +++ b/cli/smartcontract/testdata/types/config.yml @@ -1,3 +1,3 @@ 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"] +safemethods: ["bool", "int", "bytes", "string", "any", "hash160", "hash256", "publicKey", "signature", "bools", "ints", "bytess", "strings", "hash160s", "hash256s", "publicKeys", "signatures", "aAAStrings", "maps", "crazyMaps", "anyMaps"] diff --git a/cli/smartcontract/testdata/types/rpcbindings.out b/cli/smartcontract/testdata/types/rpcbindings.out index 2b419a2ad..ee61838d1 100644 --- a/cli/smartcontract/testdata/types/rpcbindings.out +++ b/cli/smartcontract/testdata/types/rpcbindings.out @@ -18,20 +18,21 @@ var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xa // Invoker is used by ContractReader to call various safe methods. type Invoker interface { - Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) + 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 { - return &ContractReader{invoker} + 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) { @@ -87,37 +88,65 @@ func (c *ContractReader) AAAStrings(s [][][]string) ([][][]string, error) { } return res, nil } (item) - } (unwrap.Item(c.invoker.Call(Hash, "aAAStrings", s))) + } (unwrap.Item(c.invoker.Call(c.hash, "aAAStrings", s))) } // Any invokes `any` method of contract. -func (c *ContractReader) Any(a interface{}) (interface{}, error) { - return func (item stackitem.Item, err error) (interface{}, error) { +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(), nil - } (unwrap.Item(c.invoker.Call(Hash, "any", a))) + 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(Hash, "bool", b)) + 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(Hash, "bools", b)) + 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(Hash, "bytes", b)) + 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(Hash, "bytess", b)) + return unwrap.ArrayOfBytes(c.invoker.Call(c.hash, "bytess", b)) } // CrazyMaps invokes `crazyMaps` method of contract. @@ -208,37 +237,37 @@ func (c *ContractReader) CrazyMaps(m map[*big.Int][]map[string][]util.Uint160) ( } return res, nil } (item) - } (unwrap.Item(c.invoker.Call(Hash, "crazyMaps", m))) + } (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(Hash, "hash160", h)) + 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(Hash, "hash160s", h)) + 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(Hash, "hash256", h)) + 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(Hash, "hash256s", h)) + 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(Hash, "int", i)) + 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(Hash, "ints", i)) + return unwrap.ArrayOfBigInts(c.invoker.Call(c.hash, "ints", i)) } // Maps invokes `maps` method of contract. @@ -284,35 +313,35 @@ func (c *ContractReader) Maps(m map[string]string) (map[string]string, error) { } return res, nil } (item) - } (unwrap.Item(c.invoker.Call(Hash, "maps", m))) + } (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(Hash, "publicKey", k)) + 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(Hash, "publicKeys", k)) + 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(Hash, "signature", s)) + 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(Hash, "signatures", s)) + 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(Hash, "string", s)) + 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(Hash, "strings", s)) + return unwrap.ArrayOfUTF8Strings(c.invoker.Call(c.hash, "strings", s)) } diff --git a/cli/smartcontract/testdata/types/rpcbindings_dynamic_hash.out b/cli/smartcontract/testdata/types/rpcbindings_dynamic_hash.out new file mode 100755 index 000000000..1075e3260 --- /dev/null +++ b/cli/smartcontract/testdata/types/rpcbindings_dynamic_hash.out @@ -0,0 +1,343 @@ +// 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" +) + +// 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)) +} diff --git a/cli/smartcontract/testdata/types/types.go b/cli/smartcontract/testdata/types/types.go index f59aac0e8..fcf91a0be 100644 --- a/cli/smartcontract/testdata/types/types.go +++ b/cli/smartcontract/testdata/types/types.go @@ -20,7 +20,7 @@ func String(s string) string { return "" } -func Any(a interface{}) interface{} { +func Any(a any) any { return nil } @@ -83,3 +83,7 @@ func Maps(m map[string]string) map[string]string { 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 +} diff --git a/cli/smartcontract/testdata/verify.go b/cli/smartcontract/testdata/verify.go index 3f399c9f6..dabc0ab7a 100644 --- a/cli/smartcontract/testdata/verify.go +++ b/cli/smartcontract/testdata/verify.go @@ -9,12 +9,12 @@ func Verify() bool { return true } -func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) { +func OnNEP17Payment(from interop.Hash160, amount int, data any) { } // OnNEP11Payment notifies about NEP-11 payment. You don't call this method directly, // instead it's called by NEP-11 contract when you transfer funds from your address // to the address of this NFT contract. -func OnNEP11Payment(from interop.Hash160, amount int, token []byte, data interface{}) { +func OnNEP11Payment(from interop.Hash160, amount int, token []byte, data any) { runtime.Notify("OnNEP11Payment", from, amount, token, data) } diff --git a/cli/smartcontract/testdata/verifyrpc/verify.go b/cli/smartcontract/testdata/verifyrpc/verify.go index 8b8f07961..6a755aec0 100644 --- a/cli/smartcontract/testdata/verifyrpc/verify.go +++ b/cli/smartcontract/testdata/verifyrpc/verify.go @@ -2,44 +2,54 @@ 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 ...interface{}) (*transaction.Transaction, error) + 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 ...interface{}) (*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 ...interface{}) (util.Uint256, uint32, 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 { - return &Contract{actor} + var hash = Hash + return &Contract{actor, hash} } - -func scriptForVerify() ([]byte, error) { - return smartcontract.CreateCallWithAssertScript(Hash, "verify") +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 := scriptForVerify() + script, err := c.scriptForVerify() if err != nil { return util.Uint256{}, 0, err } @@ -50,7 +60,7 @@ func (c *Contract) Verify() (util.Uint256, uint32, error) { // 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 := scriptForVerify() + script, err := c.scriptForVerify() if err != nil { return nil, err } @@ -62,9 +72,74 @@ func (c *Contract) VerifyTransaction() (*transaction.Transaction, error) { // 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 := scriptForVerify() + 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 +} diff --git a/cli/util/convert.go b/cli/util/convert.go index 1fb910216..71a1c9547 100644 --- a/cli/util/convert.go +++ b/cli/util/convert.go @@ -1,10 +1,14 @@ package util import ( + "encoding/base64" + "encoding/hex" "fmt" + "os" "github.com/nspcc-dev/neo-go/cli/options" vmcli "github.com/nspcc-dev/neo-go/cli/vm" + "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/urfave/cli" ) @@ -44,6 +48,22 @@ func NewCommands() []cli.Command { Action: txDump, Flags: txDumpFlags, }, + { + Name: "ops", + Usage: "Pretty-print VM opcodes of the given base64- or hex- encoded script (base64 is checked first). If the input file is specified, then the script is taken from the file.", + UsageText: "ops [-i path-to-file] [--hex]", + Action: handleOps, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "in, i", + Usage: "input file containing base64- or hex- encoded script representation", + }, + cli.BoolFlag{ + Name: "hex", + Usage: "use hex encoding and do not check base64", + }, + }, + }, }, }, } @@ -57,3 +77,35 @@ func handleParse(ctx *cli.Context) error { fmt.Fprint(ctx.App.Writer, res) return nil } + +func handleOps(ctx *cli.Context) error { + var ( + s string + err error + b []byte + ) + in := ctx.String("in") + if len(in) != 0 { + b, err := os.ReadFile(in) + if err != nil { + return cli.NewExitError(fmt.Errorf("failed to read file: %w", err), 1) + } + s = string(b) + } else { + if !ctx.Args().Present() { + return cli.NewExitError("missing script", 1) + } + s = ctx.Args()[0] + } + b, err = base64.StdEncoding.DecodeString(s) + if err != nil || ctx.Bool("hex") { + b, err = hex.DecodeString(s) + } + if err != nil { + return cli.NewExitError("unknown encoding: base64 or hex are supported", 1) + } + v := vm.New() + v.LoadScript(b) + v.PrintOps(ctx.App.Writer) + return nil +} diff --git a/cli/util/util_test.go b/cli/util/util_test.go index e2f073465..609937fc9 100644 --- a/cli/util/util_test.go +++ b/cli/util/util_test.go @@ -1,10 +1,13 @@ package util_test import ( + "os" + "path/filepath" "testing" "github.com/nspcc-dev/neo-go/internal/testcli" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/stretchr/testify/require" ) func TestUtilConvert(t *testing.T) { @@ -24,3 +27,39 @@ func TestUtilConvert(t *testing.T) { e.CheckNextLine(t, "MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzMDIwMQ==") // string to base64 e.CheckEOF(t) } + +func TestUtilOps(t *testing.T) { + e := testcli.NewExecutor(t, false) + base64Str := "EUA=" + hexStr := "1140" + + check := func(t *testing.T) { + e.CheckNextLine(t, "INDEX.*OPCODE.*PARAMETER") + e.CheckNextLine(t, "PUSH1") + e.CheckNextLine(t, "RET") + e.CheckEOF(t) + } + + e.Run(t, "neo-go", "util", "ops", base64Str) // base64 + check(t) + + e.Run(t, "neo-go", "util", "ops", hexStr) // base64 is checked firstly by default, but it's invalid script if decoded from base64 + e.CheckNextLine(t, "INDEX.*OPCODE.*PARAMETER") + e.CheckNextLine(t, ".*ERROR: incorrect opcode") + e.CheckEOF(t) + + e.Run(t, "neo-go", "util", "ops", "--hex", hexStr) // explicitly specify hex encoding + check(t) + + e.RunWithError(t, "neo-go", "util", "ops", "%&~*") // unknown encoding + + tmp := filepath.Join(t.TempDir(), "script_base64.txt") + require.NoError(t, os.WriteFile(tmp, []byte(base64Str), os.ModePerm)) + e.Run(t, "neo-go", "util", "ops", "--in", tmp) // base64 from file + check(t) + + tmp = filepath.Join(t.TempDir(), "script_hex.txt") + require.NoError(t, os.WriteFile(tmp, []byte(hexStr), os.ModePerm)) + e.Run(t, "neo-go", "util", "ops", "--hex", "--in", tmp) // hex from file + check(t) +} diff --git a/cli/vm/cli.go b/cli/vm/cli.go index d7a3c07f8..ed6b685d9 100644 --- a/cli/vm/cli.go +++ b/cli/vm/cli.go @@ -28,12 +28,14 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/core/native" + "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" @@ -51,7 +53,7 @@ const ( chainKey = "chain" chainCfgKey = "chainCfg" icKey = "ic" - manifestKey = "manifest" + contractStateKey = "contractState" exitFuncKey = "exitFunc" readlineInstanceKey = "readlineKey" printLogoKey = "printLogoKey" @@ -64,6 +66,7 @@ const ( gasFlagFullName = "gas" backwardsFlagFullName = "backwards" diffFlagFullName = "diff" + hashFlagFullName = "hash" ) var ( @@ -76,6 +79,10 @@ var ( Name: gasFlagFullName, Usage: "GAS limit for this execution (integer number, satoshi).", } + hashFlag = cli.StringFlag{ + Name: hashFlagFullName, + Usage: "Smart-contract hash in LE form or address", + } ) var commands = []cli.Command{ @@ -150,10 +157,12 @@ Example: }, { Name: "loadnef", - Usage: "Load a NEF-consistent script into the VM optionally attaching to it provided signers with scopes", - UsageText: `loadnef [--historic ] [--gas ] [, ...]`, - Flags: []cli.Flag{historicFlag, gasFlag}, - Description: ` and parameters are mandatory. + Usage: "Load a NEF (possibly with a contract hash) into the VM optionally using provided scoped signers in the context", + UsageText: `loadnef [--historic ] [--gas ] [--hash ] [] [-- , ...]`, + Flags: []cli.Flag{historicFlag, gasFlag, hashFlag}, + Description: ` parameter is mandatory, parameter (if omitted) will + be guessed from the parameter by replacing '.nef' suffix with '.manifest.json' + suffix. ` + cmdargs.SignersParsingDoc + ` @@ -164,7 +173,7 @@ Example: { Name: "loadbase64", Usage: "Load a base64-encoded script string into the VM optionally attaching to it provided signers with scopes", - UsageText: `loadbase64 [--historic ] [--gas ] [, ...]`, + UsageText: `loadbase64 [--historic ] [--gas ] [-- , ...]`, Flags: []cli.Flag{historicFlag, gasFlag}, Description: ` is mandatory parameter. @@ -177,7 +186,7 @@ Example: { Name: "loadhex", Usage: "Load a hex-encoded script string into the VM optionally attaching to it provided signers with scopes", - UsageText: `loadhex [--historic ] [--gas ] [, ...]`, + UsageText: `loadhex [--historic ] [--gas ] [-- , ...]`, Flags: []cli.Flag{historicFlag, gasFlag}, Description: ` is mandatory parameter. @@ -189,9 +198,9 @@ Example: }, { Name: "loadgo", - Usage: "Compile and load a Go file with the manifest into the VM optionally attaching to it provided signers with scopes", - UsageText: `loadgo [--historic ] [--gas ] [, ...]`, - Flags: []cli.Flag{historicFlag, gasFlag}, + Usage: "Compile and load a Go file with the manifest into the VM optionally attaching to it provided signers with scopes and setting provided hash", + UsageText: `loadgo [--historic ] [--gas ] [--hash ] [-- , ...]`, + Flags: []cli.Flag{historicFlag, gasFlag, hashFlag}, Description: ` is mandatory parameter. ` + cmdargs.SignersParsingDoc + ` @@ -220,7 +229,7 @@ Example: { Name: "loaddeployed", Usage: "Load deployed contract into the VM from chain optionally attaching to it provided signers with scopes", - UsageText: `loaddeployed [--historic ] [--gas ] [, ...]`, + UsageText: `loaddeployed [--historic ] [--gas ] [-- , ...]`, Flags: []cli.Flag{historicFlag, gasFlag}, Description: `Load deployed contract into the VM from chain optionally attaching to it provided signers with scopes. If '--historic' flag specified, then the historic contract state (historic script and manifest) will be loaded. @@ -483,11 +492,11 @@ func NewWithConfig(printLogotype bool, onExit func(int), c *readline.Config, cfg shell: ctl, } - vmcli.shell.Metadata = map[string]interface{}{ + vmcli.shell.Metadata = map[string]any{ chainKey: chain, chainCfgKey: cfg, icKey: ic, - manifestKey: new(manifest.Manifest), + contractStateKey: new(state.ContractBase), exitFuncKey: exitF, readlineInstanceKey: l, printLogoKey: printLogotype, @@ -520,8 +529,8 @@ func getInteropContextFromContext(app *cli.App) *interop.Context { return app.Metadata[icKey].(*interop.Context) } -func getManifestFromContext(app *cli.App) *manifest.Manifest { - return app.Metadata[manifestKey].(*manifest.Manifest) +func getContractStateFromContext(app *cli.App) *state.ContractBase { + return app.Metadata[contractStateKey].(*state.ContractBase) } func getPrintLogoFromContext(app *cli.App) bool { @@ -532,8 +541,8 @@ func setInteropContextInContext(app *cli.App, ic *interop.Context) { app.Metadata[icKey] = ic } -func setManifestInContext(app *cli.App, m *manifest.Manifest) { - app.Metadata[manifestKey] = m +func setContractStateInContext(app *cli.App, cs *state.ContractBase) { + app.Metadata[contractStateKey] = cs } func checkVMIsReady(app *cli.App) bool { @@ -669,12 +678,49 @@ func prepareVM(c *cli.Context, tx *transaction.Transaction) error { return nil } +func getHashFlag(c *cli.Context) (util.Uint160, error) { + if !c.IsSet(hashFlagFullName) { + return util.Uint160{}, nil + } + h, err := flags.ParseAddress(c.String(hashFlagFullName)) + if err != nil { + return util.Uint160{}, fmt.Errorf("failed to parse contract hash: %w", err) + } + return h, nil +} + func handleLoadNEF(c *cli.Context) error { args := c.Args() - if len(args) < 2 { - return fmt.Errorf("%w: ", ErrMissingParameter) + if len(args) < 1 { + return fmt.Errorf("%w: is required", ErrMissingParameter) } - b, err := os.ReadFile(args[0]) + nefFile := args[0] + var ( + manifestFile string + signersStartOffset int + ) + if len(args) == 2 { + manifestFile = args[1] + } else if len(args) == 3 { + if args[1] != cmdargs.CosignersSeparator { + return fmt.Errorf("%w: `%s` was expected as the second parameter, got %s", ErrInvalidParameter, cmdargs.CosignersSeparator, args[1]) + } + signersStartOffset = 2 + } else if len(args) > 3 { + if args[1] == cmdargs.CosignersSeparator { + signersStartOffset = 2 + } else { + manifestFile = args[1] + if args[2] != cmdargs.CosignersSeparator { + return fmt.Errorf("%w: `%s` was expected as the third parameter, got %s", ErrInvalidParameter, cmdargs.CosignersSeparator, args[2]) + } + signersStartOffset = 3 + } + } + if len(manifestFile) == 0 { + manifestFile = strings.TrimSuffix(nefFile, ".nef") + ".manifest.json" + } + b, err := os.ReadFile(nefFile) if err != nil { return err } @@ -682,24 +728,34 @@ func handleLoadNEF(c *cli.Context) error { if err != nil { return fmt.Errorf("failed to decode NEF file: %w", err) } - m, err := getManifestFromFile(args[1]) + m, err := getManifestFromFile(manifestFile) if err != nil { return fmt.Errorf("failed to read manifest: %w", err) } var signers []transaction.Signer - if len(args) > 2 { - signers, err = cmdargs.ParseSigners(c.Args()[2:]) + if signersStartOffset != 0 && len(args) > signersStartOffset { + signers, err = cmdargs.ParseSigners(c.Args()[signersStartOffset:]) if err != nil { - return fmt.Errorf("%w: %v", ErrInvalidParameter, err) //nolint:errorlint // errorlint: non-wrapping format verb for fmt.Errorf. Use `%w` to format errors + return fmt.Errorf("%w: failed to parse signers: %v", ErrInvalidParameter, err) //nolint:errorlint // errorlint: non-wrapping format verb for fmt.Errorf. Use `%w` to format errors } } err = prepareVM(c, createFakeTransaction(nef.Script, signers)) if err != nil { return err } + h, err := getHashFlag(c) + if err != nil { + return err + } + cs := &state.ContractBase{ + Hash: h, + NEF: nef, + Manifest: *m, + } + setContractStateInContext(c.App, cs) + v := getVMFromContext(c.App) fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", v.Context().LenInstr()) - setManifestInContext(c.App, m) changePrompt(c.App) return nil } @@ -715,7 +771,13 @@ func handleLoadBase64(c *cli.Context) error { } var signers []transaction.Signer if len(args) > 1 { - signers, err = cmdargs.ParseSigners(args[1:]) + if args[1] != cmdargs.CosignersSeparator { + return fmt.Errorf("%w: `%s` was expected as the second parameter, got %s", ErrInvalidParameter, cmdargs.CosignersSeparator, args[1]) + } + if len(args) < 3 { + return fmt.Errorf("%w: signers expected after `%s`, got none", ErrInvalidParameter, cmdargs.CosignersSeparator) + } + signers, err = cmdargs.ParseSigners(args[2:]) if err != nil { return fmt.Errorf("%w: %v", ErrInvalidParameter, err) //nolint:errorlint // errorlint: non-wrapping format verb for fmt.Errorf. Use `%w` to format errors } @@ -749,7 +811,13 @@ func handleLoadHex(c *cli.Context) error { } var signers []transaction.Signer if len(args) > 1 { - signers, err = cmdargs.ParseSigners(args[1:]) + if args[1] != cmdargs.CosignersSeparator { + return fmt.Errorf("%w: `%s` was expected as the second parameter, got %s", ErrInvalidParameter, cmdargs.CosignersSeparator, args[1]) + } + if len(args) < 3 { + return fmt.Errorf("%w: signers expected after `%s`, got none", ErrInvalidParameter, cmdargs.CosignersSeparator) + } + signers, err = cmdargs.ParseSigners(args[2:]) if err != nil { return fmt.Errorf("%w: %v", ErrInvalidParameter, err) //nolint:errorlint // errorlint: non-wrapping format verb for fmt.Errorf. Use `%w` to format errors } @@ -771,7 +839,7 @@ func handleLoadGo(c *cli.Context) error { } name := strings.TrimSuffix(args[0], ".go") - b, di, err := compiler.CompileWithOptions(args[0], nil, &compiler.Options{Name: name}) + ne, di, err := compiler.CompileWithOptions(args[0], nil, &compiler.Options{Name: name}) if err != nil { return fmt.Errorf("failed to compile: %w", err) } @@ -783,18 +851,34 @@ func handleLoadGo(c *cli.Context) error { } var signers []transaction.Signer if len(args) > 1 { - signers, err = cmdargs.ParseSigners(args[1:]) + if args[1] != cmdargs.CosignersSeparator { + return fmt.Errorf("%w: `%s` was expected as the second parameter, got %s", ErrInvalidParameter, cmdargs.CosignersSeparator, args[1]) + } + if len(args) < 3 { + return fmt.Errorf("%w: signers expected after `%s`, got none", ErrInvalidParameter, cmdargs.CosignersSeparator) + } + signers, err = cmdargs.ParseSigners(args[2:]) if err != nil { return fmt.Errorf("%w: %v", ErrInvalidParameter, err) //nolint:errorlint // errorlint: non-wrapping format verb for fmt.Errorf. Use `%w` to format errors } } - err = prepareVM(c, createFakeTransaction(b.Script, signers)) + err = prepareVM(c, createFakeTransaction(ne.Script, signers)) if err != nil { return err } + h, err := getHashFlag(c) + if err != nil { + return err + } + cs := &state.ContractBase{ + Hash: h, + NEF: *ne, + Manifest: *m, + } + setContractStateInContext(c.App, cs) + v := getVMFromContext(c.App) - setManifestInContext(c.App, m) fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", v.Context().LenInstr()) changePrompt(c.App) return nil @@ -849,7 +933,8 @@ func handleLoadDeployed(c *cli.Context) error { if !c.Args().Present() { return errors.New("contract hash, address or ID is mandatory argument") } - hashOrID := c.Args().Get(0) + args := c.Args() + hashOrID := args[0] ic := getInteropContextFromContext(c.App) h, err := flags.ParseAddress(hashOrID) if err != nil { @@ -868,8 +953,14 @@ func handleLoadDeployed(c *cli.Context) error { } var signers []transaction.Signer - if len(c.Args()) > 1 { - signers, err = cmdargs.ParseSigners(c.Args()[1:]) + if len(args) > 1 { + if args[1] != cmdargs.CosignersSeparator { + return fmt.Errorf("%w: %s was expected as the second parameter, got %s", ErrInvalidParameter, cmdargs.CosignersSeparator, args[1]) + } + if len(args) < 3 { + return fmt.Errorf("%w: signers expected after `%s`, got none", ErrInvalidParameter, cmdargs.CosignersSeparator) + } + signers, err = cmdargs.ParseSigners(args[2:]) if err != nil { return fmt.Errorf("%w: %v", ErrInvalidParameter, err) //nolint:errorlint // errorlint: non-wrapping format verb for fmt.Errorf. Use `%w` to format errors } @@ -884,7 +975,7 @@ func handleLoadDeployed(c *cli.Context) error { ic.VM.GasLimit = gasLimit ic.VM.LoadScriptWithHash(cs.NEF.Script, cs.Hash, callflag.All) fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", ic.VM.Context().LenInstr()) - setManifestInContext(c.App, &cs.Manifest) + setContractStateInContext(c.App, &cs.ContractBase) changePrompt(c.App) return nil } @@ -939,9 +1030,9 @@ func resetInteropContext(app *cli.App, tx *transaction.Transaction, height ...ui return nil } -// resetManifest removes manifest from app context. -func resetManifest(app *cli.App) { - setManifestInContext(app, nil) +// resetContractState removes loaded contract state from app context. +func resetContractState(app *cli.App) { + setContractStateInContext(app, nil) } // resetState resets state of the app (clear interop context and manifest) so that it's ready @@ -951,7 +1042,7 @@ func resetState(app *cli.App, tx *transaction.Transaction, height ...uint32) err if err != nil { return err } - resetManifest(app) + resetContractState(app) return nil } @@ -970,7 +1061,7 @@ func getManifestFromFile(name string) (*manifest.Manifest, error) { func handleRun(c *cli.Context) error { v := getVMFromContext(c.App) - m := getManifestFromContext(c.App) + cs := getContractStateFromContext(c.App) args := c.Args() if len(args) != 0 { var ( @@ -978,6 +1069,7 @@ func handleRun(c *cli.Context) error { offset int err error runCurrent = args[0] != "_" + hasRet bool ) _, scParams, err := cmdargs.ParseParams(args[1:], true) @@ -992,27 +1084,35 @@ func handleRun(c *cli.Context) error { } } if runCurrent { - if m == nil { - return fmt.Errorf("manifest is not loaded; either use 'run' command to run loaded script from the start or use 'loadgo' and 'loadnef' commands to provide manifest") + if cs == nil { + return fmt.Errorf("manifest is not loaded; either use 'run' command to run loaded script from the start or use 'loadgo', 'loadnef' or 'loaddeployed' commands to provide manifest") } - md := m.ABI.GetMethod(args[0], len(params)) + md := cs.Manifest.ABI.GetMethod(args[0], len(params)) if md == nil { return fmt.Errorf("%w: method not found", ErrInvalidParameter) } + hasRet = md.ReturnType != smartcontract.VoidType offset = md.Offset + var initOff = -1 + if initMD := cs.Manifest.ABI.GetMethod(manifest.MethodInit, 0); initMD != nil { + initOff = initMD.Offset + } + + // Clear context loaded by 'loadgo', 'loadnef' or 'loaddeployed' to properly handle LoadNEFMethod. + // At the same time, preserve previously set gas limit and the set of breakpoints. + ic := getInteropContextFromContext(c.App) + gasLimit := v.GasLimit + breaks := v.Context().BreakPoints() // We ensure that there's a context loaded. + ic.ReuseVM(v) + v.GasLimit = gasLimit + v.LoadNEFMethod(&cs.NEF, util.Uint160{}, cs.Hash, callflag.All, hasRet, offset, initOff, nil) + for _, bp := range breaks { + v.AddBreakPoint(bp) + } } for i := len(params) - 1; i >= 0; i-- { v.Estack().PushVal(params[i]) } - if runCurrent { - if !v.Ready() { - return errors.New("no program loaded") - } - v.Context().Jump(offset) - if initMD := m.ABI.GetMethod(manifest.MethodInit, 0); initMD != nil { - v.Call(initMD.Offset) - } - } } runVMWithHandling(c) changePrompt(c.App) diff --git a/cli/vm/cli_test.go b/cli/vm/cli_test.go index 2b6e7db45..4ccc29606 100644 --- a/cli/vm/cli_test.go +++ b/cli/vm/cli_test.go @@ -18,6 +18,7 @@ import ( "time" "github.com/chzyer/readline" + "github.com/nspcc-dev/neo-go/cli/cmdargs" "github.com/nspcc-dev/neo-go/cli/paramcontext" "github.com/nspcc-dev/neo-go/internal/basicchain" "github.com/nspcc-dev/neo-go/internal/random" @@ -180,9 +181,9 @@ func (e *executor) checkError(t *testing.T, expectedErr error) { require.True(t, strings.HasPrefix(line, expected), fmt.Errorf("expected `%s`, got `%s`", expected, line)) } -func (e *executor) checkStack(t *testing.T, items ...interface{}) { +func (e *executor) checkStack(t *testing.T, items ...any) { d := json.NewDecoder(e.out) - var actual interface{} + var actual any require.NoError(t, d.Decode(&actual)) rawActual, err := json.Marshal(actual) require.NoError(t, err) @@ -210,7 +211,7 @@ func (e *executor) checkEvents(t *testing.T, isKeywordExpected bool, events ...s e.checkNextLine(t, "Events:") } d := json.NewDecoder(e.out) - var actual interface{} + var actual any require.NoError(t, d.Decode(&actual)) rawActual, err := json.Marshal(actual) require.NoError(t, err) @@ -249,9 +250,9 @@ func (e *executor) checkChange(t *testing.T, c storageChange) { } } -func (e *executor) checkSlot(t *testing.T, items ...interface{}) { +func (e *executor) checkSlot(t *testing.T, items ...any) { d := json.NewDecoder(e.out) - var actual interface{} + var actual any require.NoError(t, d.Decode(&actual)) rawActual, err := json.Marshal(actual) require.NoError(t, err) @@ -280,6 +281,97 @@ func (e *executor) checkSlot(t *testing.T, items ...interface{}) { require.NoError(t, err) } +func TestRun_WithNewVMContextAndBreakpoints(t *testing.T) { + t.Run("contract without init", func(t *testing.T) { + src := `package kek + func Main(a, b int) int { + var c = a + b + return c + 5 + }` + tmpDir := t.TempDir() + filename := prepareLoadgoSrc(t, tmpDir, src) + + e := newTestVMCLI(t) + e.runProgWithTimeout(t, 10*time.Second, + "loadgo "+filename, + "break 8", + "run main 3 5", + "run", + ) + + e.checkNextLine(t, "READY: loaded \\d* instructions") + e.checkNextLine(t, "breakpoint added at instruction 8") + e.checkNextLine(t, "at breakpoint 8 (PUSH5)*") + e.checkStack(t, 13) + }) + t.Run("contract with init", func(t *testing.T) { + src := `package kek + var I = 5 + func Main(a, b int) int { + var c = a + b + return c + I + }` + + tmpDir := t.TempDir() + filename := prepareLoadgoSrc(t, tmpDir, src) + + e := newTestVMCLI(t) + e.runProgWithTimeout(t, 10*time.Second, + "loadgo "+filename, + "break 10", + "run main 3 5", + "run", + ) + + e.checkNextLine(t, "READY: loaded \\d* instructions") + e.checkNextLine(t, "breakpoint added at instruction 10") + e.checkNextLine(t, "at breakpoint 10 (ADD)*") + e.checkStack(t, 13) + }) +} + +// prepareLoadgoSrc prepares provided SC source file for loading into VM via `loadgo` command. +func prepareLoadgoSrc(t *testing.T, tmpDir, src string) string { + filename := filepath.Join(tmpDir, "vmtestcontract.go") + require.NoError(t, os.WriteFile(filename, []byte(src), os.ModePerm)) + filename = "'" + filename + "'" + wd, err := os.Getwd() + require.NoError(t, err) + goMod := []byte(`module test.example/kek +require ( + github.com/nspcc-dev/neo-go/pkg/interop v0.0.0 +) +replace github.com/nspcc-dev/neo-go/pkg/interop => ` + filepath.Join(wd, "../../pkg/interop") + ` +go 1.18`) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "go.mod"), goMod, os.ModePerm)) + return filename +} + +// prepareLoadnefSrc compiles provided SC source and prepares NEF and manifest for loading into VM +// via `loadnef` command. It returns the name of manifest and NEF files ready to be used in CLI +// commands. +func prepareLoadnefSrc(t *testing.T, tmpDir, src string) (string, string) { + config.Version = "0.92.0-test" + + nefFile, di, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil) + require.NoError(t, err) + filename := filepath.Join(tmpDir, "vmtestcontract.nef") + rawNef, err := nefFile.Bytes() + require.NoError(t, err) + require.NoError(t, os.WriteFile(filename, rawNef, os.ModePerm)) + m, err := di.ConvertToManifest(&compiler.Options{}) + require.NoError(t, err) + manifestFile := filepath.Join(tmpDir, "vmtestcontract.manifest.json") + rawManifest, err := json.Marshal(m) + require.NoError(t, err) + require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm)) + + manifestFile = "'" + manifestFile + "'" + filename = "'" + filename + "'" + + return manifestFile, filename +} + func TestLoad(t *testing.T) { script := []byte{byte(opcode.PUSH3), byte(opcode.PUSH4), byte(opcode.ADD)} @@ -298,21 +390,27 @@ func TestLoad(t *testing.T) { "loadhex", "loadhex notahex", "loadhex "+hex.EncodeToString(script), - "loadhex "+hex.EncodeToString(checkWitnessScript)+" "+ownerAddress, // owner:DefaultScope => true + "loadhex "+hex.EncodeToString(checkWitnessScript)+" "+cmdargs.CosignersSeparator, + "loadhex "+hex.EncodeToString(checkWitnessScript)+" "+"not-a-separator", + "loadhex "+hex.EncodeToString(checkWitnessScript)+" "+cmdargs.CosignersSeparator+" "+"not-a-signer", + "loadhex "+hex.EncodeToString(checkWitnessScript)+" "+cmdargs.CosignersSeparator+" "+ownerAddress, // owner:DefaultScope => true "run", - "loadhex "+hex.EncodeToString(checkWitnessScript)+" "+ownerAddress+":None", // owner:None => false + "loadhex "+hex.EncodeToString(checkWitnessScript)+" "+cmdargs.CosignersSeparator+" "+ownerAddress+":None", // owner:None => false "run", - "loadhex "+hex.EncodeToString(checkWitnessScript)+" "+ownerAcc.StringLE(), // ownerLE:DefaultScope => true + "loadhex "+hex.EncodeToString(checkWitnessScript)+" "+cmdargs.CosignersSeparator+" "+ownerAcc.StringLE(), // ownerLE:DefaultScope => true "run", - "loadhex "+hex.EncodeToString(checkWitnessScript)+" 0x"+ownerAcc.StringLE(), // owner0xLE:DefaultScope => true + "loadhex "+hex.EncodeToString(checkWitnessScript)+" "+cmdargs.CosignersSeparator+" "+"0x"+ownerAcc.StringLE(), // owner0xLE:DefaultScope => true "run", - "loadhex "+hex.EncodeToString(checkWitnessScript)+" "+sideAcc.StringLE(), // sideLE:DefaultScope => false + "loadhex "+hex.EncodeToString(checkWitnessScript)+" "+cmdargs.CosignersSeparator+" "+sideAcc.StringLE(), // sideLE:DefaultScope => false "run", ) e.checkError(t, ErrMissingParameter) e.checkError(t, ErrInvalidParameter) e.checkNextLine(t, "READY: loaded 3 instructions") + e.checkError(t, ErrInvalidParameter) + e.checkError(t, ErrInvalidParameter) + e.checkError(t, ErrInvalidParameter) e.checkNextLine(t, "READY: loaded \\d+ instructions") e.checkStack(t, true) e.checkNextLine(t, "READY: loaded \\d+ instructions") @@ -330,21 +428,27 @@ func TestLoad(t *testing.T) { "loadbase64", "loadbase64 not_a_base64", "loadbase64 "+base64.StdEncoding.EncodeToString(script), - "loadbase64 "+base64.StdEncoding.EncodeToString(checkWitnessScript)+" "+ownerAddress, // owner:DefaultScope => true + "loadbase64 "+base64.StdEncoding.EncodeToString(checkWitnessScript)+" "+cmdargs.CosignersSeparator, + "loadbase64 "+base64.StdEncoding.EncodeToString(checkWitnessScript)+" "+"not-a-separator", + "loadbase64 "+base64.StdEncoding.EncodeToString(checkWitnessScript)+" "+cmdargs.CosignersSeparator+" not-a-signer", + "loadbase64 "+base64.StdEncoding.EncodeToString(checkWitnessScript)+" "+cmdargs.CosignersSeparator+" "+ownerAddress, // owner:DefaultScope => true "run", - "loadbase64 "+base64.StdEncoding.EncodeToString(checkWitnessScript)+" "+ownerAddress+":None", // owner:None => false + "loadbase64 "+base64.StdEncoding.EncodeToString(checkWitnessScript)+" "+cmdargs.CosignersSeparator+" "+ownerAddress+":None", // owner:None => false "run", - "loadbase64 "+base64.StdEncoding.EncodeToString(checkWitnessScript)+" "+ownerAcc.StringLE(), // ownerLE:DefaultScope => true + "loadbase64 "+base64.StdEncoding.EncodeToString(checkWitnessScript)+" "+cmdargs.CosignersSeparator+" "+ownerAcc.StringLE(), // ownerLE:DefaultScope => true "run", - "loadbase64 "+base64.StdEncoding.EncodeToString(checkWitnessScript)+" 0x"+ownerAcc.StringLE(), // owner0xLE:DefaultScope => true + "loadbase64 "+base64.StdEncoding.EncodeToString(checkWitnessScript)+" "+cmdargs.CosignersSeparator+" "+"0x"+ownerAcc.StringLE(), // owner0xLE:DefaultScope => true "run", - "loadbase64 "+base64.StdEncoding.EncodeToString(checkWitnessScript)+" "+sideAcc.StringLE(), // sideLE:DefaultScope => false + "loadbase64 "+base64.StdEncoding.EncodeToString(checkWitnessScript)+" "+cmdargs.CosignersSeparator+" "+sideAcc.StringLE(), // sideLE:DefaultScope => false "run", ) e.checkError(t, ErrMissingParameter) e.checkError(t, ErrInvalidParameter) e.checkNextLine(t, "READY: loaded 3 instructions") + e.checkError(t, ErrInvalidParameter) + e.checkError(t, ErrInvalidParameter) + e.checkError(t, ErrInvalidParameter) e.checkNextLine(t, "READY: loaded \\d+ instructions") e.checkStack(t, true) e.checkNextLine(t, "READY: loaded \\d+ instructions") @@ -365,19 +469,13 @@ func TestLoad(t *testing.T) { return a * b } }` - tmpDir := t.TempDir() - checkLoadgo := func(t *testing.T, tName, cName, cErrName string) { - t.Run("loadgo "+tName, func(t *testing.T) { - filename := filepath.Join(tmpDir, cName) - require.NoError(t, os.WriteFile(filename, []byte(src), os.ModePerm)) - filename = "'" + filename + "'" + t.Run("loadgo", func(t *testing.T) { + tmpDir := t.TempDir() + + checkLoadgo := func(t *testing.T, cName, cErrName string) { + filename := prepareLoadgoSrc(t, tmpDir, src) filenameErr := filepath.Join(tmpDir, cErrName) - require.NoError(t, os.WriteFile(filenameErr, []byte(src+"invalid_token"), os.ModePerm)) - filenameErr = "'" + filenameErr + "'" - goMod := []byte(`module test.example/vmcli -go 1.17`) - require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "go.mod"), goMod, os.ModePerm)) e := newTestVMCLI(t) e.runProgWithTimeout(t, 10*time.Second, @@ -390,120 +488,117 @@ go 1.17`) e.checkNextLine(t, "Error:") e.checkNextLine(t, "READY: loaded \\d* instructions") e.checkStack(t, 8) + } + + t.Run("simple", func(t *testing.T) { + checkLoadgo(t, "vmtestcontract.go", "vmtestcontract_err.go") + }) + t.Run("utf-8 with spaces", func(t *testing.T) { + checkLoadgo(t, "тестовый контракт.go", "тестовый контракт с ошибкой.go") }) - } - checkLoadgo(t, "simple", "vmtestcontract.go", "vmtestcontract_err.go") - checkLoadgo(t, "utf-8 with spaces", "тестовый контракт.go", "тестовый контракт с ошибкой.go") - - prepareLoadgoSrc := func(t *testing.T, srcAllowNotify string) string { - filename := filepath.Join(tmpDir, "vmtestcontract.go") - require.NoError(t, os.WriteFile(filename, []byte(srcAllowNotify), os.ModePerm)) - filename = "'" + filename + "'" - wd, err := os.Getwd() - require.NoError(t, err) - goMod := []byte(`module test.example/kek -require ( - github.com/nspcc-dev/neo-go/pkg/interop v0.0.0 -) -replace github.com/nspcc-dev/neo-go/pkg/interop => ` + filepath.Join(wd, "../../pkg/interop") + ` -go 1.17`) - require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "go.mod"), goMod, os.ModePerm)) - return filename - } - t.Run("loadgo, check calling flags", func(t *testing.T) { - srcAllowNotify := `package kek + t.Run("check calling flags", func(t *testing.T) { + srcAllowNotify := `package kek import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" func Main() int { runtime.Log("Hello, world!") return 1 } ` - filename := prepareLoadgoSrc(t, srcAllowNotify) + filename := prepareLoadgoSrc(t, tmpDir, srcAllowNotify) - e := newTestVMCLI(t) - e.runProg(t, - "loadgo "+filename, - "run main") - e.checkNextLine(t, "READY: loaded \\d* instructions") - e.checkStack(t, 1) - }) - t.Run("loadgo, check signers", func(t *testing.T) { - srcCheckWitness := `package kek + e := newTestVMCLI(t) + e.runProg(t, + "loadgo "+filename, + "run main") + e.checkNextLine(t, "READY: loaded \\d* instructions") + e.checkStack(t, 1) + }) + t.Run("check signers", func(t *testing.T) { + srcCheckWitness := `package kek import ( "github.com/nspcc-dev/neo-go/pkg/interop/runtime" - "github.com/nspcc-dev/neo-go/pkg/interop/util" + "github.com/nspcc-dev/neo-go/pkg/interop/lib/address" ) func Main() bool { - var owner = util.FromAddress("` + ownerAddress + `") + var owner = address.ToHash160("` + ownerAddress + `") return runtime.CheckWitness(owner) } ` - filename := prepareLoadgoSrc(t, srcCheckWitness) - t.Run("address", func(t *testing.T) { - e := newTestVMCLI(t) - e.runProg(t, - "loadgo "+filename+" "+ownerAddress, // owner:DefaultScope => true - "run main", - "loadgo "+filename+" "+ownerAddress+":None", // owner:None => false - "run main") - e.checkNextLine(t, "READY: loaded \\d+ instructions") - e.checkStack(t, true) - e.checkNextLine(t, "READY: loaded \\d+ instructions") - e.checkStack(t, false) - }) - t.Run("string LE", func(t *testing.T) { - e := newTestVMCLI(t) - e.runProg(t, - "loadgo "+filename+" "+ownerAcc.StringLE(), // ownerLE:DefaultScope => true - "run main", - "loadgo "+filename+" 0x"+ownerAcc.StringLE(), // owner0xLE:DefaultScope => true - "run main") - e.checkNextLine(t, "READY: loaded \\d+ instructions") - e.checkStack(t, true) - e.checkNextLine(t, "READY: loaded \\d+ instructions") - e.checkStack(t, true) - }) - t.Run("nonwitnessed signer", func(t *testing.T) { - e := newTestVMCLI(t) - e.runProg(t, - "loadgo "+filename+" "+sideAcc.StringLE(), // sideLE:DefaultScope => false - "run main") - e.checkNextLine(t, "READY: loaded \\d+ instructions") - e.checkStack(t, false) + filename := prepareLoadgoSrc(t, tmpDir, srcCheckWitness) + t.Run("invalid", func(t *testing.T) { + e := newTestVMCLI(t) + e.runProg(t, + "loadgo "+filename+" "+cmdargs.CosignersSeparator, + "loadgo "+filename+" "+"not-a-separator", + "loadgo "+filename+" "+cmdargs.CosignersSeparator+" not-a-signer", + ) + e.checkError(t, ErrInvalidParameter) + e.checkError(t, ErrInvalidParameter) + e.checkError(t, ErrInvalidParameter) + }) + t.Run("address", func(t *testing.T) { + e := newTestVMCLI(t) + e.runProg(t, + "loadgo "+filename+" "+cmdargs.CosignersSeparator+" "+ownerAddress, // owner:DefaultScope => true + "run main", + "loadgo "+filename+" "+cmdargs.CosignersSeparator+" "+ownerAddress+":None", // owner:None => false + "run main") + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, true) + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, false) + }) + t.Run("string LE", func(t *testing.T) { + e := newTestVMCLI(t) + e.runProg(t, + "loadgo "+filename+" "+cmdargs.CosignersSeparator+" "+ownerAcc.StringLE(), // ownerLE:DefaultScope => true + "run main", + "loadgo "+filename+" "+cmdargs.CosignersSeparator+" "+"0x"+ownerAcc.StringLE(), // owner0xLE:DefaultScope => true + "run main") + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, true) + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, true) + }) + t.Run("nonwitnessed signer", func(t *testing.T) { + e := newTestVMCLI(t) + e.runProg(t, + "loadgo "+filename+" "+cmdargs.CosignersSeparator+" "+sideAcc.StringLE(), // sideLE:DefaultScope => false + "run main") + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, false) + }) }) }) + t.Run("loadnef", func(t *testing.T) { - config.Version = "0.92.0-test" + tmpDir := t.TempDir() - nefFile, di, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil) - require.NoError(t, err) - filename := filepath.Join(tmpDir, "vmtestcontract.nef") - rawNef, err := nefFile.Bytes() - require.NoError(t, err) - require.NoError(t, os.WriteFile(filename, rawNef, os.ModePerm)) - m, err := di.ConvertToManifest(&compiler.Options{}) - require.NoError(t, err) - manifestFile := filepath.Join(tmpDir, "vmtestcontract.manifest.json") - rawManifest, err := json.Marshal(m) - require.NoError(t, err) - require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm)) + manifestFile, nefFile := prepareLoadnefSrc(t, tmpDir, src) filenameErr := filepath.Join(tmpDir, "vmtestcontract_err.nef") - require.NoError(t, os.WriteFile(filenameErr, append([]byte{1, 2, 3, 4}, rawNef...), os.ModePerm)) + require.NoError(t, os.WriteFile(filenameErr, []byte{1, 2, 3, 4}, os.ModePerm)) notExists := filepath.Join(tmpDir, "notexists.json") - - manifestFile = "'" + manifestFile + "'" - filename = "'" + filename + "'" filenameErr = "'" + filenameErr + "'" e := newTestVMCLI(t) e.runProg(t, "loadnef", "loadnef "+filenameErr+" "+manifestFile, - "loadnef "+filename+" "+notExists, - "loadnef "+filename+" "+filename, - "loadnef "+filename+" "+manifestFile, - "run main add 3 5") + "loadnef "+nefFile+" "+notExists, + "loadnef "+nefFile+" "+nefFile, + "loadnef "+nefFile+" "+manifestFile, + "run main add 3 5", + "loadnef "+nefFile, + "run main add 3 5", + "loadnef "+nefFile+" "+cmdargs.CosignersSeparator, + "loadnef "+nefFile+" "+manifestFile+" "+cmdargs.CosignersSeparator, + "loadnef "+nefFile+" "+manifestFile+" "+"not-a-separator", + "loadnef "+nefFile+" "+cmdargs.CosignersSeparator+" "+util.Uint160{1, 2, 3}.StringLE(), + "run main add 3 5", + "loadnef "+nefFile+" "+manifestFile+" "+cmdargs.CosignersSeparator+" "+util.Uint160{1, 2, 3}.StringLE(), + "run main add 3 5", + ) e.checkError(t, ErrMissingParameter) e.checkNextLine(t, "Error:") @@ -511,6 +606,81 @@ go 1.17`) e.checkNextLine(t, "Error:") e.checkNextLine(t, "READY: loaded \\d* instructions") e.checkStack(t, 8) + e.checkNextLine(t, "READY: loaded \\d* instructions") + e.checkStack(t, 8) + e.checkNextLine(t, "Error:") // manifest missing, missing signer after -- + e.checkNextLine(t, "Error:") // manifest present, missing signer after -- + e.checkNextLine(t, "Error:") // manifest present, invalid separator + e.checkNextLine(t, "READY: loaded \\d* instructions") // manifest missing, signer present, OK + e.checkStack(t, 8) + e.checkNextLine(t, "READY: loaded \\d* instructions") // manifest present, signer present, OK + e.checkStack(t, 8) + }) +} + +func TestLoad_RunWithCALLT(t *testing.T) { + // Our smart compiler will generate CALLT instruction for the following StdLib call: + src := `package kek + import "github.com/nspcc-dev/neo-go/pkg/interop/native/std" + func Main() int { + return std.Atoi("123", 10) + }` + + t.Run("loadgo", func(t *testing.T) { + tmp := t.TempDir() + filename := prepareLoadgoSrc(t, tmp, src) + e := newTestVMCLI(t) + e.runProg(t, + "loadgo "+filename, + "run main", + ) + e.checkNextLine(t, "READY: loaded \\d* instructions") + e.checkStack(t, 123) + }) + + t.Run("loadnef", func(t *testing.T) { + tmpDir := t.TempDir() + manifestFile, nefFile := prepareLoadnefSrc(t, tmpDir, src) + + e := newTestVMCLI(t) + e.runProg(t, + "loadnef "+nefFile+" "+manifestFile, + "run main", + ) + e.checkNextLine(t, "READY: loaded \\d* instructions") + e.checkStack(t, 123) + }) + + t.Run("loaddeployed", func(t *testing.T) { + // We'll use `Runtime example` example contract which has a call to native Management + // inside performed via CALLT instruction (`destroy` method). + e := newTestVMClIWithState(t) + var ( + cH util.Uint160 + cName = "Runtime example" + bc = e.cli.chain + ) + for i := int32(1); ; i++ { + h, err := bc.GetContractScriptHash(i) + if err != nil { + break + } + cs := bc.GetContractState(h) + if cs == nil { + break + } + if cs.Manifest.Name == cName { + cH = cs.Hash + break + } + } + require.NotEmpty(t, cH, fmt.Sprintf("failed to locate `%s` example contract with CALLT usage in the simple chain", cName)) + e.runProg(t, + "loaddeployed "+cH.StringLE()+" -- NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB:Global", // the contract's owner got from the contract's code. + "run destroy", + ) + e.checkNextLine(t, "READY: loaded \\d* instructions") + e.checkStack(t) // Nothing on stack, successful execution. }) } @@ -532,7 +702,7 @@ func TestRunWithDifferentArguments(t *testing.T) { func GetString(arg string) string { return arg } - func GetArr(arg []interface{}) []interface{}{ + func GetArr(arg []any) []any{ return arg }` @@ -933,7 +1103,7 @@ func TestEvents(t *testing.T) { script := io.NewBufBinWriter() h, err := e.cli.chain.GetContractScriptHash(2) // examples/runtime/runtime.go require.NoError(t, err) - emit.AppCall(script.BinWriter, h, "notify", callflag.All, []interface{}{true, 5}) + emit.AppCall(script.BinWriter, h, "notify", callflag.All, []any{true, 5}) e.runProg(t, "loadhex "+hex.EncodeToString(script.Bytes()), "run", @@ -1153,24 +1323,27 @@ func TestLoaddeployed(t *testing.T) { "run get 1", "loaddeployed --gas 420000 "+h.StringLE(), // gas-limited "run get 1", - "loaddeployed 0x"+h.StringLE(), // hash LE with 0x prefix + "loaddeployed "+"0x"+h.StringLE(), // hash LE with 0x prefix "run get 1", "loaddeployed 1", // contract ID "run get 1", "loaddeployed --historic 2 1", // historic state, check that hash is properly set "run get 1", // Check signers parsing: - "loaddeployed 2 "+ownerAddress, // check witness (owner:DefautScope => true) + "loaddeployed 2 "+cmdargs.CosignersSeparator, + "loaddeployed 2 "+"not-a-separator", + "loaddeployed 2 "+cmdargs.CosignersSeparator+" not-a-signer", + "loaddeployed 2 "+cmdargs.CosignersSeparator+" "+ownerAddress, // check witness (owner:DefautScope => true) "run checkWitness", - "loaddeployed 2 "+ownerAddress+":None", // check witness (owner:None => false) + "loaddeployed 2 "+cmdargs.CosignersSeparator+" "+ownerAddress+":None", // check witness (owner:None => false) "run checkWitness", - "loaddeployed 2 "+ownerAddress+":CalledByEntry", // check witness (owner:CalledByEntry => true) + "loaddeployed 2 "+cmdargs.CosignersSeparator+" "+ownerAddress+":CalledByEntry", // check witness (owner:CalledByEntry => true) "run checkWitness", - "loaddeployed 2 "+ownerAcc.StringLE()+":CalledByEntry", // check witness (ownerLE:CalledByEntry => true) + "loaddeployed 2 "+cmdargs.CosignersSeparator+" "+ownerAcc.StringLE()+":CalledByEntry", // check witness (ownerLE:CalledByEntry => true) "run checkWitness", - "loaddeployed 2 0x"+ownerAcc.StringLE()+":CalledByEntry", // check witness (owner0xLE:CalledByEntry => true) + "loaddeployed 2 "+cmdargs.CosignersSeparator+" "+"0x"+ownerAcc.StringLE()+":CalledByEntry", // check witness (owner0xLE:CalledByEntry => true) "run checkWitness", - "loaddeployed 2 "+sideAcc.StringLE()+":Global", // check witness (sideLE:Global => false) + "loaddeployed 2 "+cmdargs.CosignersSeparator+" "+sideAcc.StringLE()+":Global", // check witness (sideLE:Global => false) "run checkWitness", "loaddeployed", // missing argument "exit", @@ -1186,6 +1359,9 @@ func TestLoaddeployed(t *testing.T) { e.checkNextLine(t, "READY: loaded \\d+ instructions") e.checkStack(t, []byte{1}) // Check signers parsing: + e.checkError(t, ErrInvalidParameter) + e.checkError(t, ErrInvalidParameter) + e.checkError(t, ErrInvalidParameter) e.checkNextLine(t, "READY: loaded \\d+ instructions") // check witness of owner:DefaultScope e.checkStack(t, true) e.checkNextLine(t, "READY: loaded \\d+ instructions") // check witness of owner:None diff --git a/cli/wallet/wallet.go b/cli/wallet/wallet.go index 212092f47..f5051efb8 100644 --- a/cli/wallet/wallet.go +++ b/cli/wallet/wallet.go @@ -985,7 +985,12 @@ func newAccountFromWIF(w io.Writer, wif string, scrypt keys.ScryptParams, label if pass != nil { phrase = *pass } - if label != nil { + if label == nil { + name, err = readAccountName() + if err != nil { + return nil, fmt.Errorf("failed to read account label: %w", err) + } + } else { name = *label } // note: NEP2 strings always have length of 58 even though @@ -1024,12 +1029,6 @@ func newAccountFromWIF(w io.Writer, wif string, scrypt keys.ScryptParams, label } fmt.Fprintln(w, "Provided WIF was unencrypted. Wallet can contain only encrypted keys.") - if label == nil { - name, err = readAccountName() - if err != nil { - return nil, fmt.Errorf("failed to read account label: %w", err) - } - } if pass == nil { phrase, err = readNewPassword() if err != nil { diff --git a/cli/wallet/wallet_test.go b/cli/wallet/wallet_test.go index 7425237e9..f3387a336 100644 --- a/cli/wallet/wallet_test.go +++ b/cli/wallet/wallet_test.go @@ -356,9 +356,26 @@ func TestWalletInit(t *testing.T) { t.Run("InvalidPassword", func(t *testing.T) { e.In.WriteString("password1\r") e.RunWithError(t, "neo-go", "wallet", "import", "--wallet", walletPath, - "--wif", acc.EncryptedWIF) + "--wif", acc.EncryptedWIF, "--name", "acc1") }) + e.In.WriteString("somepass\r") + e.Run(t, "neo-go", "wallet", "import", "--wallet", walletPath, + "--wif", acc.EncryptedWIF, "--name", "acc1") + + w, err := wallet.NewWalletFromFile(walletPath) + require.NoError(t, err) + actual := w.GetAccount(acc.PrivateKey().GetScriptHash()) + require.NotNil(t, actual) + require.Equal(t, "acc1", actual.Label) + require.NoError(t, actual.Decrypt("somepass", w.Scrypt)) + }) + t.Run("EncryptedWIF with name specified via input", func(t *testing.T) { + acc, err := wallet.NewAccount() + require.NoError(t, err) + require.NoError(t, acc.Encrypt("somepass", keys.NEP2ScryptParams())) + + e.In.WriteString("acc2\r") e.In.WriteString("somepass\r") e.Run(t, "neo-go", "wallet", "import", "--wallet", walletPath, "--wif", acc.EncryptedWIF) @@ -367,6 +384,7 @@ func TestWalletInit(t *testing.T) { require.NoError(t, err) actual := w.GetAccount(acc.PrivateKey().GetScriptHash()) require.NotNil(t, actual) + require.Equal(t, "acc2", actual.Label) require.NoError(t, actual.Decrypt("somepass", w.Scrypt)) }) t.Run("EncryptedWIF with wallet config", func(t *testing.T) { @@ -388,12 +406,13 @@ func TestWalletInit(t *testing.T) { e.In.WriteString(pass + "\r") } e.Run(t, "neo-go", "wallet", "import", "--wallet-config", configPath, - "--wif", acc.EncryptedWIF) + "--wif", acc.EncryptedWIF, "--name", "acc3"+configPass) w, err := wallet.NewWalletFromFile(walletPath) require.NoError(t, err) actual := w.GetAccount(acc.PrivateKey().GetScriptHash()) require.NotNil(t, actual) + require.Equal(t, "acc3"+configPass, actual.Label) require.NoError(t, actual.Decrypt(pass, w.Scrypt)) } t.Run("config password mismatch", func(t *testing.T) { diff --git a/docs/compiler.md b/docs/compiler.md index 59b7759dc..118b94d92 100644 --- a/docs/compiler.md +++ b/docs/compiler.md @@ -80,6 +80,14 @@ $ go mod tidy By default, the filename will be the name of your .go file with the .nef extension, the file will be located in the same directory with your Go contract. +Along with the compiled contract and if the contract configuration file +`contract.yml` exist, the following files will be generated: +* smart-contract manifest file (`contract.manifest.json`) that is needed to deploy + the contract to the network +* bindings configuration file (`contract.bindings.yml`) that is needed to generate + code-based or RPC contract bindings +All of them will be located in the same directory with your Go contract. + If you want another location for your compiled contract: ``` @@ -464,10 +472,113 @@ and structures. Notice that structured types returned by methods can't be Null at the moment (see #2795). ``` -$ ./bin/neo-go contract compile -i contract.go --config contract.yml -o contract.nef --manifest manifest.json --bindings contract.bindings.yml +$ ./bin/neo-go contract compile -i contract.go --config contract.yml -o contract.nef --manifest manifest.json --bindings contract.bindings.yml --guess-eventtypes $ ./bin/neo-go contract generate-rpcwrapper --manifest manifest.json --config contract.bindings.yml --out rpcwrapper.go --hash 0x1b4357bff5a01bdf2a6581247cf9ed1e24629176 ``` +Contract-specific RPC-bindings generated by "generate-rpcwrapper" command include +structure wrappers for each event declared in the contract manifest as far as the +set of helpers that allow to retrieve emitted event from the application log or +from stackitem. By default, event wrappers builder use event structure that was +described in the manifest. Since the type data available in the manifest is +limited, in some cases the resulting generated event structure may use generic +go types. Go contracts can make use of additional type data from bindings +configuration file generated during compilation. Like for any other contract +types, this can cover arrays, maps and structures. To reach the maximum +resemblance between the emitted events and the generated event wrappers, we +recommend either to fill in the extended events type information in the contract +configuration file before the compilation or to use `--guess-eventtypes` +compilation option. + +If using `--guess-eventtypes` compilation option, event parameter types will be +guessed from the arguments of `runtime.Notify` calls for each emitted event. If +multiple calls of `runtime.Notify` are found, then argument types will be checked +for matching (guessed types must be the same across the particular event usages). +After that, the extended types binding configuration will be generated according +to the emitted events parameter types. `--guess-eventtypes` compilation option +is able to recognize those events that has a constant name known at a compilation +time and do not include variadic arguments usage. Thus, use this option if your +contract suites these requirements. Otherwise, we recommend to manually specify +extended event parameter types information in the contract configuration file. + +Extended event parameter type information can be provided manually via contract +configuration file under the `events` section. Each event parameter specified in +this section may be supplied with additional parameter type information specified +under `extendedtype` subsection. The extended type information (`ExtendedType`) +has the following structure: + +| Field | Type | Required | Meaning | +|-------------|---------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------| +| `base` | Any valid [NEP-14 parameter type](https://github.com/neo-project/proposals/blob/master/nep-14.mediawiki#parametertype) except `Void`. | Always required. | The base type of a parameter, e.g. `Array` for go structures and any nested arrays, `Map` for nested maps, `Hash160` for 160-bits integers, etc. | +| `name` | `string` | Required for structures, omitted for arrays, interfaces and maps. | Name of a structure that will be used in the resulting RPC binding. | +| `interface` | `string` | Required for `InteropInterface`-based types, currently `iterator` only is supported. | Underlying value of the `InteropInterface`. | +| `key` | Any simple [NEP-14 parameter type](https://github.com/neo-project/proposals/blob/master/nep-14.mediawiki#parametertype). | Required for `Map`-based types. | Key type for maps. | +| `value` | `ExtendedType`. | Required for iterators, arrays and maps. | Value type of iterators, arrays and maps. | +| `fields` | Array of `FieldExtendedType`. | Required for structures. | Ordered type data for structure fields. | + +The structure's field extended information (`FieldExtendedType`) has the following structure: + +| Field | Type | Required | Meaning | +|------------------------|----------------|------------------|-----------------------------------------------------------------------------| +| `field` | `string` | Always required. | Name of the structure field that will be used in the resulting RPC binding. | +| Inlined `ExtendedType` | `ExtendedType` | Always required. | The extended type information about structure field. | + + +Any named structures used in the `ExtendedType` description must be manually +specified in the contract configuration file under top-level `namedtypes` section +in the form of `map[string]ExtendedType`, where the map key is a name of the +described named structure that matches the one provided in the `name` field of +the event parameter's extended type. + +Here's the example of manually-created contract configuration file that uses +extended types for event parameters description: + +``` +name: "HelloWorld contract" +supportedstandards: [] +events: + - name: Some simple notification + parameters: + - name: intP + type: Integer + - name: boolP + type: Boolean + - name: stringP + type: String + - name: Structure notification + parameters: + - name: structure parameter + type: Array + extendedtype: + base: Array + name: transferData + - name: Map of structures notification + parameters: + - name: map parameter + type: Map + extendedtype: + base: Map + key: Integer + value: + base: Array + name: transferData + - name: Iterator notification + parameters: + - name: data + type: InteropInterface + extendedtype: + base: InteropInterface + interface: iterator +namedtypes: + transferData: + base: Array + fields: + - field: IntField + base: Integer + - field: BoolField + base: Boolean +``` + ## Smart contract examples Some examples are provided in the [examples directory](../examples). For more diff --git a/docs/node-configuration.md b/docs/node-configuration.md index 95a389b7c..86e938d86 100644 --- a/docs/node-configuration.md +++ b/docs/node-configuration.md @@ -86,7 +86,9 @@ where: sender stored in a local pool. - `MaxPeers` (`int`) is the maximum numbers of peers that can be connected to the server. - `MinPeers` (`int`) is the minimum number of peers for normal operation; when the node has - less than this number of peers it tries to connect with some new ones. + less than this number of peers it tries to connect with some new ones. Note that consensus + node won't start the consensus process until at least `MinPeers` number of peers are + connected. - `PingInterval` (`Duration`) is the interval used in pinging mechanism for syncing blocks. - `PingTimeout` (`Duration`) is the time to wait for pong (response for sent ping request). diff --git a/docs/release-instruction.md b/docs/release-instruction.md index 6cb72a126..fc7c0dc36 100644 --- a/docs/release-instruction.md +++ b/docs/release-instruction.md @@ -56,11 +56,13 @@ Press the "Publish release" button. New release created at the previous step triggers automatic builds (if not, start them manually from the Build GitHub workflow), so wait for them to -finish. Then download currently supported binaries (at the time of writing -that's `neo-go-darwin-arm64`, `neo-go-linux-amd64`, `neo-go-linux-arm64` and -`neo-go-windows-amd64`), unpack archives and add resulting binaries (named in -the same way as archives) to the previously created release via "Edit release" -button. +finish. Built binaries should be automatically attached to the release as an +asset, check it on the release page. If binaries weren't attached after building +workflow completion, then submit the bug, download currently supported binaries +(at the time of writing they are `neo-go-darwin-arm64`, `neo-go-linux-amd64`, +`neo-go-linux-arm64` and `neo-go-windows-amd64`) from the building job artifacts, +unpack archives and add resulting binaries (named in the same way as archives) +to the previously created release via "Edit release" button. ## Close GitHub milestone diff --git a/examples/engine/go.mod b/examples/engine/go.mod index ec13bbd62..94d41135f 100644 --- a/examples/engine/go.mod +++ b/examples/engine/go.mod @@ -1,5 +1,5 @@ module github.com/nspcc-dev/neo-go/examples/engine -go 1.17 +go 1.18 -require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb +require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a diff --git a/examples/engine/go.sum b/examples/engine/go.sum index a6410fb0d..f665b036a 100644 --- a/examples/engine/go.sum +++ b/examples/engine/go.sum @@ -1,2 +1,2 @@ -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb h1:GFxfkpXEYAbMIr69JpKOsQWeLOaGrd49HNAor8uDW+A= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a h1:63sh46kfKF/g2IE1z/EV8CBEKCVmGJXSSH0ZHqTDGCY= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= diff --git a/examples/events/events.go b/examples/events/events.go index 28254ee4a..cd45ff15f 100644 --- a/examples/events/events.go +++ b/examples/events/events.go @@ -1,6 +1,7 @@ package events import ( + "github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop/runtime" ) @@ -24,6 +25,11 @@ func NotifySomeMap(arg map[string]int) { runtime.Notify("SomeMap", arg) } +// NotifySomeCrazyMap emits notification with complicated Map. +func NotifySomeCrazyMap(arg map[int][]map[string][]interop.Hash160) { + runtime.Notify("SomeCrazyMap", arg) +} + // NotifySomeArray emits notification with Array. func NotifySomeArray(arg []int) { runtime.Notify("SomeArray", arg) diff --git a/examples/events/events.yml b/examples/events/events.yml index f16bc347e..7b3c05a40 100644 --- a/examples/events/events.yml +++ b/examples/events/events.yml @@ -18,6 +18,10 @@ events: parameters: - name: m type: Map + - name: SomeCrazyMap + parameters: + - name: m + type: Map - name: SomeArray parameters: - name: a diff --git a/examples/events/go.mod b/examples/events/go.mod index 10c7e8519..b4175ed11 100644 --- a/examples/events/go.mod +++ b/examples/events/go.mod @@ -1,5 +1,5 @@ module github.com/nspcc-dev/neo-go/examples/events -go 1.17 +go 1.18 -require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb +require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a diff --git a/examples/events/go.sum b/examples/events/go.sum index a6410fb0d..f665b036a 100644 --- a/examples/events/go.sum +++ b/examples/events/go.sum @@ -1,2 +1,2 @@ -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb h1:GFxfkpXEYAbMIr69JpKOsQWeLOaGrd49HNAor8uDW+A= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a h1:63sh46kfKF/g2IE1z/EV8CBEKCVmGJXSSH0ZHqTDGCY= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= diff --git a/examples/iterator/go.mod b/examples/iterator/go.mod index 29706059a..f9a528ba1 100644 --- a/examples/iterator/go.mod +++ b/examples/iterator/go.mod @@ -1,5 +1,5 @@ module github.com/nspcc-dev/neo-go/examples/iterator -go 1.17 +go 1.18 -require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb +require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a diff --git a/examples/iterator/go.sum b/examples/iterator/go.sum index a6410fb0d..f665b036a 100644 --- a/examples/iterator/go.sum +++ b/examples/iterator/go.sum @@ -1,2 +1,2 @@ -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb h1:GFxfkpXEYAbMIr69JpKOsQWeLOaGrd49HNAor8uDW+A= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a h1:63sh46kfKF/g2IE1z/EV8CBEKCVmGJXSSH0ZHqTDGCY= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= diff --git a/examples/iterator/iterator.go b/examples/iterator/iterator.go index fe53881b8..f5e91ea37 100644 --- a/examples/iterator/iterator.go +++ b/examples/iterator/iterator.go @@ -7,7 +7,7 @@ import ( ) // _deploy primes contract's storage with some data to be used later. -func _deploy(_ interface{}, _ bool) { +func _deploy(_ any, _ bool) { ctx := storage.GetContext() // RW context. storage.Put(ctx, "foo1", "1") storage.Put(ctx, "foo2", "2") diff --git a/examples/nft-d/go.mod b/examples/nft-d/go.mod index 101373d3b..5afeb6f09 100644 --- a/examples/nft-d/go.mod +++ b/examples/nft-d/go.mod @@ -1,5 +1,5 @@ module github.com/nspcc-dev/neo-go/examples/nft -go 1.17 +go 1.18 -require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb +require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a diff --git a/examples/nft-d/go.sum b/examples/nft-d/go.sum index a6410fb0d..f665b036a 100644 --- a/examples/nft-d/go.sum +++ b/examples/nft-d/go.sum @@ -1,2 +1,2 @@ -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb h1:GFxfkpXEYAbMIr69JpKOsQWeLOaGrd49HNAor8uDW+A= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a h1:63sh46kfKF/g2IE1z/EV8CBEKCVmGJXSSH0ZHqTDGCY= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= diff --git a/examples/nft-d/nft.go b/examples/nft-d/nft.go index 66afd2ef6..0970c56da 100644 --- a/examples/nft-d/nft.go +++ b/examples/nft-d/nft.go @@ -10,6 +10,7 @@ import ( "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/lib/address" "github.com/nspcc-dev/neo-go/pkg/interop/native/crypto" "github.com/nspcc-dev/neo-go/pkg/interop/native/gas" "github.com/nspcc-dev/neo-go/pkg/interop/native/management" @@ -39,7 +40,7 @@ var ( // contractOwner is a special address that can perform some management // functions on this contract like updating/destroying it and can also // be used for contract address verification. - contractOwner = util.FromAddress("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB") + contractOwner = address.ToHash160("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB") ) // ObjectIdentifier represents NFT structure and contains the container ID and @@ -172,7 +173,7 @@ func tokensOf(ctx storage.Context, holder interop.Hash160) iterator.Iterator { // Transfer token from its owner to another user, if there's one owner of the token. // It will return false if token is shared between multiple owners. -func Transfer(to interop.Hash160, token []byte, data interface{}) bool { +func Transfer(to interop.Hash160, token []byte, data any) bool { if len(to) != interop.Hash160Len { panic("invalid 'to' address") } @@ -215,7 +216,7 @@ func Transfer(to interop.Hash160, token []byte, data interface{}) bool { } // postTransfer emits Transfer event and calls onNEP11Payment if needed. -func postTransfer(from interop.Hash160, to interop.Hash160, token []byte, amount int, data interface{}) { +func postTransfer(from interop.Hash160, to interop.Hash160, token []byte, amount int, data any) { runtime.Notify("Transfer", from, to, amount, token) if management.GetContract(to) != nil { contract.Call(to, "onNEP11Payment", contract.All, from, amount, token, data) @@ -263,7 +264,7 @@ func isTokenValid(ctx storage.Context, tokenID []byte) bool { // TransferDivisible token from its owner to another user, notice that it only has three // parameters because token owner can be deduced from token ID itself. -func TransferDivisible(from, to interop.Hash160, amount int, token []byte, data interface{}) bool { +func TransferDivisible(from, to interop.Hash160, amount int, token []byte, data any) bool { if len(from) != interop.Hash160Len { panic("invalid 'from' address") } @@ -350,7 +351,7 @@ func removeOwner(ctx storage.Context, token []byte, holder interop.Hash160) { // OnNEP17Payment mints tokens if at least 10 GAS is provided. You don't call // this method directly, instead it's called by GAS contract when you transfer // GAS from your address to the address of this NFT contract. -func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) { +func OnNEP17Payment(from interop.Hash160, amount int, data any) { defer func() { if r := recover(); r != nil { runtime.Log(r.(string)) @@ -364,7 +365,7 @@ func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) { if amount < 10_00000000 { panic("minting NFSO costs at least 10 GAS") } - tokenInfo := data.([]interface{}) + tokenInfo := data.([]any) if len(tokenInfo) != 2 { panic("invalid 'data'") } diff --git a/examples/nft-nd-nns/go.mod b/examples/nft-nd-nns/go.mod index 3956674ea..589482cd7 100644 --- a/examples/nft-nd-nns/go.mod +++ b/examples/nft-nd-nns/go.mod @@ -1,10 +1,10 @@ module github.com/nspcc-dev/neo-go/examples/nft-nd-nns -go 1.17 +go 1.18 require ( github.com/nspcc-dev/neo-go v0.99.5-0.20221108145959-8746d9877eb5 - github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb + github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a github.com/stretchr/testify v1.8.0 ) diff --git a/examples/nft-nd-nns/go.sum b/examples/nft-nd-nns/go.sum index 9adab2ac1..619660152 100644 --- a/examples/nft-nd-nns/go.sum +++ b/examples/nft-nd-nns/go.sum @@ -33,44 +33,18 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CityOfZion/neo-go v0.62.1-pre.0.20191114145240-e740fbe708f8/go.mod h1:MJCkWUBhi9pn/CrYO1Q3P687y2KeahrOPS9BD9LDGb0= -github.com/CityOfZion/neo-go v0.70.1-pre.0.20191209120015-fccb0085941e/go.mod h1:0enZl0az8xA6PVkwzEOwPWVJGqlt/GO4hA4kmQ5Xzig= -github.com/CityOfZion/neo-go v0.70.1-pre.0.20191212173117-32ac01130d4c/go.mod h1:JtlHfeqLywZLswKIKFnAp+yzezY4Dji9qlfQKB2OD/I= -github.com/CityOfZion/neo-go v0.71.1-pre.0.20200129171427-f773ec69fb84/go.mod h1:FLI526IrRWHmcsO+mHsCbj64pJZhwQFTLJZu+A4PGOA= -github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= -github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg= -github.com/abiosoft/ishell/v2 v2.0.2/go.mod h1:E4oTCXfo6QjoCart0QYa5m9w4S+deXs/P/9jA77A9Bs= -github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521073959-f0d4d129b7f1/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -80,36 +54,23 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M= -github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -121,9 +82,7 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-redis/redis v6.10.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -136,7 +95,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -154,10 +112,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -169,9 +125,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -183,14 +137,12 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= @@ -199,12 +151,8 @@ github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= @@ -213,28 +161,15 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -242,60 +177,33 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254/go.mod h1:w1Ln2aT+dBlPhLnuZhBV+DfPEdS2CHWWLp5JTScY3bw= -github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA51EGfb5GS/HOv7VdmngNRTssSeQ729dvGY= -github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk= -github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ= -github.com/nspcc-dev/dbft v0.0.0-20210721160347-1b03241391ac/go.mod h1:U8MSnEShH+o5hexfWJdze6uMFJteP0ko7J2frO7Yu1Y= github.com/nspcc-dev/dbft v0.0.0-20221020093431-31c1bbdc74f2 h1:2soBy8en5W4/1Gvbog8RyVpEbarGWZwPxppZjffWzZE= -github.com/nspcc-dev/dbft v0.0.0-20221020093431-31c1bbdc74f2/go.mod h1:g9xisXmX9NP9MjioaTe862n9SlZTrP+6PVUWLBYOr98= -github.com/nspcc-dev/go-ordered-json v0.0.0-20210915112629-e1b6cce73d02/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U= github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 h1:n4ZaFCKt1pQJd7PXoMJabZWK9ejjbLOVrkl/lOUmshg= github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U= github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y= -github.com/nspcc-dev/hrw v1.0.9/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkPG06MU= -github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg= -github.com/nspcc-dev/neo-go v0.98.0/go.mod h1:E3cc1x6RXSXrJb2nDWXTXjnXk3rIqVN8YdFyWv+FrqM= github.com/nspcc-dev/neo-go v0.99.5-0.20221108145959-8746d9877eb5 h1:NCIUxkLRB3ovLzM1lvQA6wBNn8fuY7dQx4cMJKLuaAs= github.com/nspcc-dev/neo-go v0.99.5-0.20221108145959-8746d9877eb5/go.mod h1:aWrWJZBYO+9kYC4+qJXvEjySW1WIyPnrHpmdrzd5mJY= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220927123257-24c107e3a262/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb h1:GFxfkpXEYAbMIr69JpKOsQWeLOaGrd49HNAor8uDW+A= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= -github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211201134523-3604d96f3fe1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a h1:63sh46kfKF/g2IE1z/EV8CBEKCVmGJXSSH0ZHqTDGCY= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= github.com/nspcc-dev/neofs-api-go/v2 v2.11.1 h1:SVqc523pZsSaS9vnPS1mm3VV6b6xY0gvdA0uYJ/GWZQ= -github.com/nspcc-dev/neofs-api-go/v2 v2.11.1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs= -github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA= -github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw= -github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw= github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4= -github.com/nspcc-dev/neofs-crypto v0.4.0/go.mod h1:6XJ8kbXgOfevbI2WMruOtI+qUJXNwSGM/E9eClXxPHs= -github.com/nspcc-dev/neofs-sdk-go v0.0.0-20211201182451-a5b61c4f6477/go.mod h1:dfMtQWmBHYpl9Dez23TGtIUKiFvCIxUZq/CkSIhEpz4= github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659 h1:rpMCoRa7expLc9gMiOP724gz6YSykZzmMALR/CmiwnU= -github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659/go.mod h1:/jay1lr3w7NQd/VDBkEhkJmDmyPNsu4W+QV2obsUV40= -github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE= github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= -github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -304,7 +212,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= @@ -316,7 +223,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= @@ -324,15 +230,12 @@ github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8 github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= @@ -341,24 +244,20 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs= github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/twmb/murmur3 v1.1.5 h1:i9OLS9fkuLzBXjt6dptlAEyk58fJsSTXbRg3SgVyqgk= github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74 h1:JwtAtbp7r/7QSyGz8mKUbYJBg2+6Cd7OjM8o/GNOcVo= @@ -366,12 +265,6 @@ github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74/go.mod h1 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= -github.com/yuin/gopher-lua v0.0.0-20191128022950-c6266f4fe8d7/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -379,31 +272,21 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.18.1 h1:CSUJ2mjFszzEWt4CdKISEuChVIXGBn3lAPwkRGyVrc4= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -437,10 +320,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -472,15 +353,10 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -496,19 +372,15 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -517,18 +389,14 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -546,21 +414,16 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210429154555-c04ba851c2a4/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -576,7 +439,6 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180318012157-96caea41033d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -618,8 +480,6 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -672,7 +532,6 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= @@ -692,10 +551,7 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E= -google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -708,10 +564,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/abiosoft/ishell.v2 v2.0.0/go.mod h1:sFp+cGtH6o4s1FtpVPTMcHq2yue+c4DGOVohJCPUzwY= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -723,7 +577,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/examples/nft-nd-nns/nns.go b/examples/nft-nd-nns/nns.go index 5a6800d47..b1dacb7a5 100644 --- a/examples/nft-nd-nns/nns.go +++ b/examples/nft-nd-nns/nns.go @@ -79,7 +79,7 @@ func Update(nef []byte, manifest string) { } // _deploy initializes defaults (total supply and registration price) on contract deploy. -func _deploy(data interface{}, isUpdate bool) { +func _deploy(data any, isUpdate bool) { if isUpdate { return } @@ -112,10 +112,10 @@ func OwnerOf(tokenID []byte) interop.Hash160 { } // Properties returns domain name and expiration date of the specified domain. -func Properties(tokenID []byte) map[string]interface{} { +func Properties(tokenID []byte) map[string]any { ctx := storage.GetReadOnlyContext() ns := getNameState(ctx, tokenID) - return map[string]interface{}{ + return map[string]any{ "name": ns.Name, "expiration": ns.Expiration, "admin": ns.Admin, @@ -151,7 +151,7 @@ func TokensOf(owner interop.Hash160) iterator.Iterator { } // Transfer transfers domain with the specified name to new owner. -func Transfer(to interop.Hash160, tokenID []byte, data interface{}) bool { +func Transfer(to interop.Hash160, tokenID []byte, data any) bool { if !isValid(to) { panic(`invalid receiver`) } @@ -398,7 +398,7 @@ func updateBalance(ctx storage.Context, tokenId []byte, acc interop.Hash160, dif // postTransfer sends Transfer notification to the network and calls onNEP11Payment // method. -func postTransfer(from, to interop.Hash160, tokenID []byte, data interface{}) { +func postTransfer(from, to interop.Hash160, tokenID []byte, data any) { runtime.Notify("Transfer", from, to, 1, tokenID) if management.GetContract(to) != nil { contract.Call(to, "onNEP11Payment", contract.All, from, 1, tokenID, data) diff --git a/examples/nft-nd-nns/nns_test.go b/examples/nft-nd-nns/nns_test.go index 9032bb9d2..5d4362105 100644 --- a/examples/nft-nd-nns/nns_test.go +++ b/examples/nft-nd-nns/nns_test.go @@ -190,8 +190,8 @@ func TestRegisterAndRenew(t *testing.T) { t.Run("invalid token ID", func(t *testing.T) { c.InvokeFail(t, "token not found", "properties", "not.exists") c.InvokeFail(t, "token not found", "ownerOf", "not.exists") - c.InvokeFail(t, "invalid conversion", "properties", []interface{}{}) - c.InvokeFail(t, "invalid conversion", "ownerOf", []interface{}{}) + c.InvokeFail(t, "invalid conversion", "properties", []any{}) + c.InvokeFail(t, "invalid conversion", "ownerOf", []any{}) }) // Renew @@ -294,7 +294,7 @@ func TestSetGetRecord(t *testing.T) { {Type: nns.AAAA, Name: "2001::13.1.68.3", ShouldFail: true}, } for _, testCase := range testCases { - args := []interface{}{"neo.com", int64(testCase.Type), testCase.Name} + args := []any{"neo.com", int64(testCase.Type), testCase.Name} t.Run(testCase.Name, func(t *testing.T) { if testCase.ShouldFail { c.InvokeFail(t, "", "setRecord", args...) @@ -377,7 +377,7 @@ func TestTransfer(t *testing.T) { ctr = neotest.CompileSource(t, e.CommitteeHash, strings.NewReader(`package foo import "github.com/nspcc-dev/neo-go/pkg/interop" - func OnNEP11Payment(from interop.Hash160, amount int, token []byte, data interface{}) {}`), + func OnNEP11Payment(from interop.Hash160, amount int, token []byte, data any) {}`), &compiler.Options{Name: "foo"}) e.DeployContract(t, ctr, nil) cTo.Invoke(t, true, "transfer", ctr.Hash, []byte("neo.com"), nil) @@ -404,7 +404,7 @@ func TestTokensOf(t *testing.T) { testTokensOf(t, c, [][]byte{}, util.Uint160{}.BytesBE()) // empty hash is a valid hash still } -func testTokensOf(t *testing.T, c *neotest.ContractInvoker, result [][]byte, args ...interface{}) { +func testTokensOf(t *testing.T, c *neotest.ContractInvoker, result [][]byte, args ...any) { method := "tokensOf" if len(args) == 0 { method = "tokens" diff --git a/examples/nft-nd/go.mod b/examples/nft-nd/go.mod index a759fd464..c6b8048c1 100644 --- a/examples/nft-nd/go.mod +++ b/examples/nft-nd/go.mod @@ -1,5 +1,5 @@ module github.com/nspcc-dev/neo-go/examples/nft-nd -go 1.17 +go 1.18 -require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb +require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a diff --git a/examples/nft-nd/go.sum b/examples/nft-nd/go.sum index a6410fb0d..f665b036a 100644 --- a/examples/nft-nd/go.sum +++ b/examples/nft-nd/go.sum @@ -1,2 +1,2 @@ -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb h1:GFxfkpXEYAbMIr69JpKOsQWeLOaGrd49HNAor8uDW+A= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a h1:63sh46kfKF/g2IE1z/EV8CBEKCVmGJXSSH0ZHqTDGCY= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= diff --git a/examples/nft-nd/nft.go b/examples/nft-nd/nft.go index f4f87b743..96796049d 100644 --- a/examples/nft-nd/nft.go +++ b/examples/nft-nd/nft.go @@ -11,6 +11,7 @@ import ( "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/lib/address" "github.com/nspcc-dev/neo-go/pkg/interop/native/crypto" "github.com/nspcc-dev/neo-go/pkg/interop/native/gas" "github.com/nspcc-dev/neo-go/pkg/interop/native/management" @@ -35,7 +36,7 @@ var ( // contractOwner is a special address that can perform some management // functions on this contract like updating/destroying it and can also // be used for contract address verification. - contractOwner = util.FromAddress("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB") + contractOwner = address.ToHash160("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB") ) // Symbol returns token symbol, it's HASHY. @@ -173,7 +174,7 @@ func OwnerOf(token []byte) interop.Hash160 { // Transfer token from its owner to another user, notice that it only has three // parameters because token owner can be deduced from token ID itself. -func Transfer(to interop.Hash160, token []byte, data interface{}) bool { +func Transfer(to interop.Hash160, token []byte, data any) bool { if len(to) != 20 { panic("invalid 'to' address") } @@ -199,7 +200,7 @@ func Transfer(to interop.Hash160, token []byte, data interface{}) bool { } // postTransfer emits Transfer event and calls onNEP11Payment if needed. -func postTransfer(from interop.Hash160, to interop.Hash160, token []byte, data interface{}) { +func postTransfer(from interop.Hash160, to interop.Hash160, token []byte, data any) { runtime.Notify("Transfer", from, to, 1, token) if management.GetContract(to) != nil { contract.Call(to, "onNEP11Payment", contract.All, from, 1, token, data) @@ -209,7 +210,7 @@ func postTransfer(from interop.Hash160, to interop.Hash160, token []byte, data i // OnNEP17Payment mints tokens if at least 10 GAS is provided. You don't call // this method directly, instead it's called by GAS contract when you transfer // GAS from your address to the address of this NFT contract. -func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) { +func OnNEP17Payment(from interop.Hash160, amount int, data any) { defer func() { if r := recover(); r != nil { runtime.Log(r.(string)) diff --git a/examples/oracle/go.mod b/examples/oracle/go.mod index fea68d385..224b6c5b7 100644 --- a/examples/oracle/go.mod +++ b/examples/oracle/go.mod @@ -1,5 +1,5 @@ module github.com/nspcc-dev/neo-go/examples/oracle -go 1.17 +go 1.18 -require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb +require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a diff --git a/examples/oracle/go.sum b/examples/oracle/go.sum index a6410fb0d..f665b036a 100644 --- a/examples/oracle/go.sum +++ b/examples/oracle/go.sum @@ -1,2 +1,2 @@ -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb h1:GFxfkpXEYAbMIr69JpKOsQWeLOaGrd49HNAor8uDW+A= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a h1:63sh46kfKF/g2IE1z/EV8CBEKCVmGJXSSH0ZHqTDGCY= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= diff --git a/examples/oracle/oracle.go b/examples/oracle/oracle.go index 62e711454..5c5807351 100644 --- a/examples/oracle/oracle.go +++ b/examples/oracle/oracle.go @@ -23,7 +23,7 @@ func FilteredRequest(url string, filter []byte) { // OracleCallback is called by Oracle native contract when request is finished. // It either throws an error (if the result is not successful) or logs the data // got as a result. -func OracleCallback(url string, data interface{}, code int, res []byte) { +func OracleCallback(url string, data any, code int, res []byte) { // This function shouldn't be called directly, we only expect oracle native // contract to be calling it. callingHash := runtime.GetCallingScriptHash() diff --git a/examples/runtime/go.mod b/examples/runtime/go.mod index 6c54bb35d..8dc6ae388 100644 --- a/examples/runtime/go.mod +++ b/examples/runtime/go.mod @@ -1,5 +1,5 @@ module github.com/nspcc-dev/neo-go/examples/runtime -go 1.17 +go 1.18 -require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb +require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a diff --git a/examples/runtime/go.sum b/examples/runtime/go.sum index a6410fb0d..f665b036a 100644 --- a/examples/runtime/go.sum +++ b/examples/runtime/go.sum @@ -1,2 +1,2 @@ -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb h1:GFxfkpXEYAbMIr69JpKOsQWeLOaGrd49HNAor8uDW+A= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a h1:63sh46kfKF/g2IE1z/EV8CBEKCVmGJXSSH0ZHqTDGCY= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= diff --git a/examples/runtime/runtime.go b/examples/runtime/runtime.go index 9ecdd559d..a98d39d29 100644 --- a/examples/runtime/runtime.go +++ b/examples/runtime/runtime.go @@ -1,14 +1,14 @@ package runtimecontract import ( + "github.com/nspcc-dev/neo-go/pkg/interop/lib/address" "github.com/nspcc-dev/neo-go/pkg/interop/native/management" "github.com/nspcc-dev/neo-go/pkg/interop/runtime" - "github.com/nspcc-dev/neo-go/pkg/interop/util" ) var ( // Check if the invoker of the contract is the specified owner - owner = util.FromAddress("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB") + owner = address.ToHash160("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB") ) // init is transformed into _initialize method that is called whenever contract @@ -22,7 +22,7 @@ func init() { // _deploy is called after contract deployment or update, it'll be called // in deployment transaction and if call update method of this contract. -func _deploy(_ interface{}, isUpdate bool) { +func _deploy(_ any, isUpdate bool) { if isUpdate { Log("_deploy method called after contract update") return @@ -46,7 +46,7 @@ func Log(message string) { } // Notify emits an event with the specified data. -func Notify(event interface{}) { +func Notify(event any) { runtime.Notify("Event", event) } @@ -65,7 +65,7 @@ func Verify() bool { // Destroy destroys the contract, only the owner can do that. func Destroy() { - if !Verify() { + if !CheckWitness() { panic("only owner can destroy") } management.Destroy() @@ -74,7 +74,7 @@ func Destroy() { // Update updates the contract, only the owner can do that. _deploy will be called // after update. func Update(nef, manifest []byte) { - if !Verify() { + if !CheckWitness() { panic("only owner can update") } management.Update(nef, manifest) diff --git a/examples/storage/go.mod b/examples/storage/go.mod index 58c91afd2..df0640441 100644 --- a/examples/storage/go.mod +++ b/examples/storage/go.mod @@ -1,5 +1,5 @@ module github.com/nspcc-dev/neo-go/examples/storage -go 1.17 +go 1.18 -require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb +require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a diff --git a/examples/storage/go.sum b/examples/storage/go.sum index a6410fb0d..f665b036a 100644 --- a/examples/storage/go.sum +++ b/examples/storage/go.sum @@ -1,2 +1,2 @@ -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb h1:GFxfkpXEYAbMIr69JpKOsQWeLOaGrd49HNAor8uDW+A= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a h1:63sh46kfKF/g2IE1z/EV8CBEKCVmGJXSSH0ZHqTDGCY= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= diff --git a/examples/storage/storage.go b/examples/storage/storage.go index 7f7c1c267..42416dd82 100644 --- a/examples/storage/storage.go +++ b/examples/storage/storage.go @@ -29,12 +29,12 @@ func PutDefault(value []byte) []byte { } // Get returns the value at the passed key. -func Get(key []byte) interface{} { +func Get(key []byte) any { return storage.Get(ctx, key) } // GetDefault returns the value at the default key. -func GetDefault() interface{} { +func GetDefault() any { return storage.Get(ctx, defaultKey) } diff --git a/examples/timer/go.mod b/examples/timer/go.mod index f1c4823f8..7f8d9efcc 100644 --- a/examples/timer/go.mod +++ b/examples/timer/go.mod @@ -1,5 +1,5 @@ module github.com/nspcc-dev/neo-go/examples/timer -go 1.17 +go 1.18 -require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb +require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a diff --git a/examples/timer/go.sum b/examples/timer/go.sum index a6410fb0d..f665b036a 100644 --- a/examples/timer/go.sum +++ b/examples/timer/go.sum @@ -1,2 +1,2 @@ -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb h1:GFxfkpXEYAbMIr69JpKOsQWeLOaGrd49HNAor8uDW+A= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a h1:63sh46kfKF/g2IE1z/EV8CBEKCVmGJXSSH0ZHqTDGCY= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= diff --git a/examples/timer/timer.go b/examples/timer/timer.go index 3e09d39ba..fcc37ef46 100644 --- a/examples/timer/timer.go +++ b/examples/timer/timer.go @@ -3,10 +3,10 @@ package timer import ( "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/lib/address" "github.com/nspcc-dev/neo-go/pkg/interop/native/std" "github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/storage" - "github.com/nspcc-dev/neo-go/pkg/interop/util" ) const defaultTicks = 3 @@ -16,7 +16,7 @@ var ( // ctx holds storage context for contract methods ctx storage.Context // Check if the invoker of the contract is the specified owner - owner = util.FromAddress("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB") + owner = address.ToHash160("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB") // ticksKey is a storage key for ticks counter ticksKey = []byte("ticks") ) @@ -25,7 +25,7 @@ func init() { ctx = storage.GetContext() } -func _deploy(_ interface{}, isUpdate bool) { +func _deploy(_ any, isUpdate bool) { if isUpdate { ticksLeft := storage.Get(ctx, ticksKey).(int) + 1 storage.Put(ctx, ticksKey, ticksLeft) diff --git a/examples/token/go.mod b/examples/token/go.mod index de2cffb75..936db6f72 100644 --- a/examples/token/go.mod +++ b/examples/token/go.mod @@ -1,5 +1,5 @@ module github.com/nspcc-dev/neo-go/examples/token -go 1.17 +go 1.18 -require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb +require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a diff --git a/examples/token/go.sum b/examples/token/go.sum index a6410fb0d..f665b036a 100644 --- a/examples/token/go.sum +++ b/examples/token/go.sum @@ -1,2 +1,2 @@ -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb h1:GFxfkpXEYAbMIr69JpKOsQWeLOaGrd49HNAor8uDW+A= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a h1:63sh46kfKF/g2IE1z/EV8CBEKCVmGJXSSH0ZHqTDGCY= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= diff --git a/examples/token/nep17/nep17.go b/examples/token/nep17/nep17.go index 7d4154766..dc4702d5a 100644 --- a/examples/token/nep17/nep17.go +++ b/examples/token/nep17/nep17.go @@ -46,7 +46,7 @@ func (t Token) BalanceOf(ctx storage.Context, holder []byte) int { } // Transfer token from one user to another -func (t Token) Transfer(ctx storage.Context, from, to interop.Hash160, amount int, data interface{}) bool { +func (t Token) Transfer(ctx storage.Context, from, to interop.Hash160, amount int, data any) bool { amountFrom := t.CanTransfer(ctx, from, to, amount) if amountFrom == -1 { return false diff --git a/examples/token/token.go b/examples/token/token.go index 7cede5d3f..47f4308bb 100644 --- a/examples/token/token.go +++ b/examples/token/token.go @@ -3,8 +3,8 @@ package tokencontract import ( "github.com/nspcc-dev/neo-go/examples/token/nep17" "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/lib/address" "github.com/nspcc-dev/neo-go/pkg/interop/storage" - "github.com/nspcc-dev/neo-go/pkg/interop/util" ) const ( @@ -13,7 +13,7 @@ const ( ) var ( - owner = util.FromAddress("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB") + owner = address.ToHash160("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB") token nep17.Token ctx storage.Context ) @@ -53,7 +53,7 @@ func BalanceOf(holder interop.Hash160) int { } // Transfer token from one user to another -func Transfer(from interop.Hash160, to interop.Hash160, amount int, data interface{}) bool { +func Transfer(from interop.Hash160, to interop.Hash160, amount int, data any) bool { return token.Transfer(ctx, from, to, amount, data) } diff --git a/go.mod b/go.mod index 92f8ded24..e23b8dcdc 100644 --- a/go.mod +++ b/go.mod @@ -2,18 +2,19 @@ module github.com/nspcc-dev/neo-go require ( github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e + github.com/consensys/gnark-crypto v0.9.1 github.com/davecgh/go-spew v1.1.1 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 - github.com/google/uuid v1.2.0 + github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.4.2 - github.com/hashicorp/golang-lru v0.5.4 + github.com/hashicorp/golang-lru v0.6.0 github.com/holiman/uint256 v1.2.0 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/mr-tron/base58 v1.2.0 - github.com/nspcc-dev/dbft v0.0.0-20230315155759-60347b1563e7 + github.com/nspcc-dev/dbft v0.0.0-20230515113611-25db6ba61d5c github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 - github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb - github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659 + github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a + github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.9 github.com/nspcc-dev/rfc6979 v0.2.0 github.com/pierrec/lz4 v2.6.1+incompatible github.com/pmezard/go-difflib v1.0.0 @@ -23,26 +24,31 @@ require ( github.com/twmb/murmur3 v1.1.5 github.com/urfave/cli v1.22.5 go.etcd.io/bbolt v1.3.7 - go.uber.org/atomic v1.9.0 + go.uber.org/atomic v1.10.0 go.uber.org/zap v1.24.0 golang.org/x/crypto v0.4.0 golang.org/x/term v0.5.0 golang.org/x/text v0.7.0 - golang.org/x/tools v0.1.12 + golang.org/x/tools v0.2.0 gopkg.in/yaml.v3 v3.0.1 ) require ( + github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12 // indirect github.com/benbjohnson/clock v1.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/consensys/bavard v0.1.13 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect + github.com/frankban/quicktest v1.14.5 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.1 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/nspcc-dev/hrw v1.0.9 // indirect - github.com/nspcc-dev/neofs-api-go/v2 v2.11.1 // indirect + github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 // indirect github.com/nspcc-dev/neofs-crypto v0.4.0 // indirect + github.com/nspcc-dev/tzhash v1.7.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.37.0 // indirect @@ -50,14 +56,16 @@ require ( github.com/russross/blackfriday/v2 v2.0.1 // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - go.uber.org/multierr v1.6.0 // indirect - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/exp v0.0.0-20221227203929-1b447090c38c // indirect + golang.org/x/mod v0.6.0 // indirect golang.org/x/net v0.7.0 // indirect golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.5.0 // indirect - google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 // indirect - google.golang.org/grpc v1.41.0 // indirect + golang.org/x/sys v0.8.0 // indirect + google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect + google.golang.org/grpc v1.53.0 // indirect google.golang.org/protobuf v1.28.1 // indirect + rsc.io/tmplfunc v0.0.3 // indirect ) -go 1.17 +go 1.18 diff --git a/go.sum b/go.sum index d5c9f78be..df335fabf 100644 --- a/go.sum +++ b/go.sum @@ -33,47 +33,24 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CityOfZion/neo-go v0.62.1-pre.0.20191114145240-e740fbe708f8/go.mod h1:MJCkWUBhi9pn/CrYO1Q3P687y2KeahrOPS9BD9LDGb0= -github.com/CityOfZion/neo-go v0.70.1-pre.0.20191209120015-fccb0085941e/go.mod h1:0enZl0az8xA6PVkwzEOwPWVJGqlt/GO4hA4kmQ5Xzig= -github.com/CityOfZion/neo-go v0.70.1-pre.0.20191212173117-32ac01130d4c/go.mod h1:JtlHfeqLywZLswKIKFnAp+yzezY4Dji9qlfQKB2OD/I= -github.com/CityOfZion/neo-go v0.71.1-pre.0.20200129171427-f773ec69fb84/go.mod h1:FLI526IrRWHmcsO+mHsCbj64pJZhwQFTLJZu+A4PGOA= -github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= -github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg= -github.com/abiosoft/ishell/v2 v2.0.2/go.mod h1:E4oTCXfo6QjoCart0QYa5m9w4S+deXs/P/9jA77A9Bs= -github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521073959-f0d4d129b7f1/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= +github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12 h1:npHgfD4Tl2WJS3AJaMUi5ynGDPUBfkg3U3fCzDyXZ+4= +github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= @@ -82,35 +59,28 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWs github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.9.1 h1:mru55qKdWl3E035hAoh1jj9d7hVnYY5pfb6tmovSmII= +github.com/consensys/gnark-crypto v0.9.1/go.mod h1:a2DQL4+5ywF6safEeZFEPGRiiGbjzGFRUN2sg06VuU4= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M= -github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= -github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= +github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= +github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -122,9 +92,7 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-redis/redis v6.10.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -137,7 +105,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -155,10 +122,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -170,9 +135,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -184,28 +148,23 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= +github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= @@ -216,82 +175,61 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254/go.mod h1:w1Ln2aT+dBlPhLnuZhBV+DfPEdS2CHWWLp5JTScY3bw= -github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA51EGfb5GS/HOv7VdmngNRTssSeQ729dvGY= -github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk= -github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ= -github.com/nspcc-dev/dbft v0.0.0-20210721160347-1b03241391ac/go.mod h1:U8MSnEShH+o5hexfWJdze6uMFJteP0ko7J2frO7Yu1Y= -github.com/nspcc-dev/dbft v0.0.0-20230315155759-60347b1563e7 h1:XP4vSXovxQHgbjHwmrbT/2sPFhSwzEy/HbcNs3kMzIo= -github.com/nspcc-dev/dbft v0.0.0-20230315155759-60347b1563e7/go.mod h1:IsUsZqxQkav23pFrCyZEibz0VukpI787XnbcsFkekI8= -github.com/nspcc-dev/go-ordered-json v0.0.0-20210915112629-e1b6cce73d02/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U= +github.com/nspcc-dev/dbft v0.0.0-20230515113611-25db6ba61d5c h1:uyK5aLbAhrnZtnvobJLN24gGUrlxIJAAFqiWl+liZuo= +github.com/nspcc-dev/dbft v0.0.0-20230515113611-25db6ba61d5c/go.mod h1:kjBC9F8L25GR+kIHy/1KgG/KfcoGnVwIiyovgq1uszk= github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 h1:n4ZaFCKt1pQJd7PXoMJabZWK9ejjbLOVrkl/lOUmshg= github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U= github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y= github.com/nspcc-dev/hrw v1.0.9/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkPG06MU= -github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg= -github.com/nspcc-dev/neo-go v0.98.0/go.mod h1:E3cc1x6RXSXrJb2nDWXTXjnXk3rIqVN8YdFyWv+FrqM= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb h1:GFxfkpXEYAbMIr69JpKOsQWeLOaGrd49HNAor8uDW+A= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= -github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211201134523-3604d96f3fe1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs= -github.com/nspcc-dev/neofs-api-go/v2 v2.11.1 h1:SVqc523pZsSaS9vnPS1mm3VV6b6xY0gvdA0uYJ/GWZQ= -github.com/nspcc-dev/neofs-api-go/v2 v2.11.1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs= -github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA= -github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw= -github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a h1:63sh46kfKF/g2IE1z/EV8CBEKCVmGJXSSH0ZHqTDGCY= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= +github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 h1:jhuN8Ldqz7WApvUJRFY0bjRXE1R3iCkboMX5QVZhHVk= +github.com/nspcc-dev/neofs-api-go/v2 v2.14.0/go.mod h1:DRIr0Ic1s+6QgdqmNFNLIqMqd7lNMJfYwkczlm1hDtM= github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4= github.com/nspcc-dev/neofs-crypto v0.4.0/go.mod h1:6XJ8kbXgOfevbI2WMruOtI+qUJXNwSGM/E9eClXxPHs= -github.com/nspcc-dev/neofs-sdk-go v0.0.0-20211201182451-a5b61c4f6477/go.mod h1:dfMtQWmBHYpl9Dez23TGtIUKiFvCIxUZq/CkSIhEpz4= -github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659 h1:rpMCoRa7expLc9gMiOP724gz6YSykZzmMALR/CmiwnU= -github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659/go.mod h1:/jay1lr3w7NQd/VDBkEhkJmDmyPNsu4W+QV2obsUV40= -github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= +github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.9 h1:uIQlWUUo5n/e8rLFGm14zIValcpXU1HWuwaoXUAHt5Q= +github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.9/go.mod h1:fTsdTU/M9rvv/f9jlp7vHOm3DRp+NSfjfTv9NohrKTE= github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE= github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= +github.com/nspcc-dev/tzhash v1.7.0 h1:/+aL33NC7y5OIGnY2kYgjZt8mg7LVGFMdj/KAJLndnk= +github.com/nspcc-dev/tzhash v1.7.0/go.mod h1:Dnx9LUlOLr5paL2Rtc96x0PPs8D9eIkUtowt1n+KQus= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -300,7 +238,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= @@ -312,7 +249,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= @@ -320,16 +256,14 @@ github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8 github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= @@ -346,18 +280,15 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs= github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/twmb/murmur3 v1.1.5 h1:i9OLS9fkuLzBXjt6dptlAEyk58fJsSTXbRg3SgVyqgk= github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74 h1:JwtAtbp7r/7QSyGz8mKUbYJBg2+6Cd7OjM8o/GNOcVo= @@ -365,38 +296,27 @@ github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74/go.mod h1 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= -github.com/yuin/gopher-lua v0.0.0-20191128022950-c6266f4fe8d7/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -408,6 +328,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20221227203929-1b447090c38c h1:Govq2W3bnHJimHT2ium65kXcI7ZzTniZHcFATnLJM0Q= +golang.org/x/exp v0.0.0-20221227203929-1b447090c38c/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -428,8 +350,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -461,11 +383,10 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -482,17 +403,15 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -501,18 +420,14 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -529,8 +444,6 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -538,10 +451,11 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210429154555-c04ba851c2a4/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -550,11 +464,11 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180318012157-96caea41033d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -570,7 +484,6 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -596,8 +509,8 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -647,13 +560,14 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -666,9 +580,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -681,9 +594,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/abiosoft/ishell.v2 v2.0.0/go.mod h1:sFp+cGtH6o4s1FtpVPTMcHq2yue+c4DGOVohJCPUzwY= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -693,14 +605,12 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -712,3 +622,5 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/internal/basicchain/basic.go b/internal/basicchain/basic.go index ef9a3d9a4..370986563 100644 --- a/internal/basicchain/basic.go +++ b/internal/basicchain/basic.go @@ -187,7 +187,7 @@ func Init(t *testing.T, rootpath string, e *neotest.Executor) { _, _, _ = deployContractFromPriv0(t, verifyPath, "Verify", verifyCfg, VerifyContractID) // Block #8: deposit some GAS to notary contract for priv0. - transferTxH = gasPriv0Invoker.Invoke(t, true, "transfer", priv0ScriptHash, notaryHash, 10_0000_0000, []interface{}{priv0ScriptHash, int64(e.Chain.BlockHeight() + 1000)}) + transferTxH = gasPriv0Invoker.Invoke(t, true, "transfer", priv0ScriptHash, notaryHash, 10_0000_0000, []any{priv0ScriptHash, int64(e.Chain.BlockHeight() + 1000)}) t.Logf("notaryDepositTxPriv0: %v", transferTxH.StringLE()) // Block #9: designate new Notary node. @@ -195,7 +195,7 @@ func Init(t *testing.T, rootpath string, e *neotest.Executor) { require.NoError(t, err) require.NoError(t, ntr.Accounts[0].Decrypt("one", ntr.Scrypt)) designateSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", - int64(noderoles.P2PNotary), []interface{}{ntr.Accounts[0].PublicKey().Bytes()}) + int64(noderoles.P2PNotary), []any{ntr.Accounts[0].PublicKey().Bytes()}) t.Logf("Designated Notary node: %s", hex.EncodeToString(ntr.Accounts[0].PublicKey().Bytes())) // Block #10: push verification contract with arguments into the chain. @@ -252,7 +252,7 @@ func Init(t *testing.T, rootpath string, e *neotest.Executor) { containerID := util.Uint256{1, 2, 3} objectID := util.Uint256{4, 5, 6} txGas0toNFSH := gasPriv0Invoker.Invoke(t, true, "transfer", - priv0ScriptHash, nfsHash, 10_0000_0000, []interface{}{containerID.BytesBE(), objectID.BytesBE()}) // block #18 + priv0ScriptHash, nfsHash, 10_0000_0000, []any{containerID.BytesBE(), objectID.BytesBE()}) // block #18 res = e.GetTxExecResult(t, txGas0toNFSH) require.Equal(t, 2, len(res.Events)) // GAS transfer + NFSO transfer tokenID, err = res.Events[1].Item.Value().([]stackitem.Item)[3].TryBytes() diff --git a/internal/basicchain/testdata/storage/storage_contract.go b/internal/basicchain/testdata/storage/storage_contract.go index 9cb875c64..f98772b8e 100644 --- a/internal/basicchain/testdata/storage/storage_contract.go +++ b/internal/basicchain/testdata/storage/storage_contract.go @@ -16,7 +16,7 @@ const valuesCount = 255 // valuesPrefix is the prefix values are stored by. var valuesPrefix = []byte{0x01} -func _deploy(data interface{}, isUpdate bool) { +func _deploy(data any, isUpdate bool) { if !isUpdate { ctx := storage.GetContext() for i := 0; i < valuesCount; i++ { diff --git a/internal/basicchain/testdata/test_contract.go b/internal/basicchain/testdata/test_contract.go index e6b98ab57..7b0a7387a 100644 --- a/internal/basicchain/testdata/test_contract.go +++ b/internal/basicchain/testdata/test_contract.go @@ -3,12 +3,12 @@ package testdata import ( "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/lib/address" "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger" "github.com/nspcc-dev/neo-go/pkg/interop/native/management" "github.com/nspcc-dev/neo-go/pkg/interop/native/neo" "github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/storage" - "github.com/nspcc-dev/neo-go/pkg/interop/util" ) const ( @@ -16,7 +16,7 @@ const ( decimals = 2 ) -var owner = util.FromAddress("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB") +var owner = address.ToHash160("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB") func Init() bool { ctx := storage.GetContext() @@ -27,7 +27,7 @@ func Init() bool { return true } -func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) { +func OnNEP17Payment(from interop.Hash160, amount int, data any) { curr := runtime.GetExecutingScriptHash() balance := neo.BalanceOf(curr) if ledger.CurrentIndex() >= 100 { @@ -47,7 +47,7 @@ func Verify() bool { return true } -func Transfer(from, to interop.Hash160, amount int, data interface{}) bool { +func Transfer(from, to interop.Hash160, amount int, data any) bool { ctx := storage.GetContext() if len(from) != 20 { runtime.Log("invalid 'from' address") diff --git a/internal/basicchain/testdata/verify/verification_contract.go b/internal/basicchain/testdata/verify/verification_contract.go index a12bcb186..d03b15615 100644 --- a/internal/basicchain/testdata/verify/verification_contract.go +++ b/internal/basicchain/testdata/verify/verification_contract.go @@ -1,6 +1,7 @@ package verify import ( + "github.com/nspcc-dev/neo-go/pkg/interop/lib/address" "github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/util" ) @@ -9,6 +10,6 @@ import ( // It returns true iff it is signed by Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn (id-0 private key from testchain). func Verify() bool { tx := runtime.GetScriptContainer() - addr := util.FromAddress("Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn") + addr := address.ToHash160("Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn") return util.Equals(string(tx.Sender), string(addr)) } diff --git a/internal/contracts/oracle_contract/go.mod b/internal/contracts/oracle_contract/go.mod index fea68d385..1d36a6782 100644 --- a/internal/contracts/oracle_contract/go.mod +++ b/internal/contracts/oracle_contract/go.mod @@ -1,5 +1,5 @@ -module github.com/nspcc-dev/neo-go/examples/oracle +module github.com/nspcc-dev/neo-go/internal/examples/oracle -go 1.17 +go 1.18 -require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb +require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a diff --git a/internal/contracts/oracle_contract/go.sum b/internal/contracts/oracle_contract/go.sum index a6410fb0d..f665b036a 100644 --- a/internal/contracts/oracle_contract/go.sum +++ b/internal/contracts/oracle_contract/go.sum @@ -1,2 +1,2 @@ -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb h1:GFxfkpXEYAbMIr69JpKOsQWeLOaGrd49HNAor8uDW+A= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a h1:63sh46kfKF/g2IE1z/EV8CBEKCVmGJXSSH0ZHqTDGCY= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= diff --git a/internal/contracts/oracle_contract/oracle.go b/internal/contracts/oracle_contract/oracle.go index da7ebe990..94e2b22ac 100644 --- a/internal/contracts/oracle_contract/oracle.go +++ b/internal/contracts/oracle_contract/oracle.go @@ -12,22 +12,22 @@ import ( // RequestURL accepts a complete set of parameters to make an oracle request and // performs it. -func RequestURL(url string, filter []byte, callback string, userData interface{}, gasForResponse int) { +func RequestURL(url string, filter []byte, callback string, userData any, gasForResponse int) { oracle.Request(url, filter, callback, userData, gasForResponse) } // Handle is a response handler that writes response data to the storage. -func Handle(url string, data interface{}, code int, res []byte) { +func Handle(url string, data any, code int, res []byte) { // ABORT if len(data) == 2, some tests use this feature. if data != nil && len(data.(string)) == 2 { util.Abort() } - params := []interface{}{url, data, code, res} + params := []any{url, data, code, res} storage.Put(storage.GetContext(), "lastOracleResponse", std.Serialize(params)) } // HandleRecursive invokes oracle.finish again to test Oracle reentrance. -func HandleRecursive(url string, data interface{}, code int, res []byte) { +func HandleRecursive(url string, data any, code int, res []byte) { // Regular safety check. callingHash := runtime.GetCallingScriptHash() if !callingHash.Equals(oracle.Hash) { diff --git a/internal/fakechain/fakechain.go b/internal/fakechain/fakechain.go index 96296ac45..51e3a8a0a 100644 --- a/internal/fakechain/fakechain.go +++ b/internal/fakechain/fakechain.go @@ -28,7 +28,7 @@ type FakeChain struct { blocksCh []chan *block.Block Blockheight uint32 PoolTxF func(*transaction.Transaction) error - poolTxWithData func(*transaction.Transaction, interface{}, *mempool.Pool) error + poolTxWithData func(*transaction.Transaction, any, *mempool.Pool) error blocks map[util.Uint256]*block.Block hdrHashes map[uint32]util.Uint256 txs map[util.Uint256]*transaction.Transaction @@ -62,9 +62,9 @@ func NewFakeChainWithCustomCfg(protocolCfg func(c *config.Blockchain)) *FakeChai protocolCfg(&cfg) } return &FakeChain{ - Pool: mempool.New(10, 0, false), + Pool: mempool.New(10, 0, false, nil), PoolTxF: func(*transaction.Transaction) error { return nil }, - poolTxWithData: func(*transaction.Transaction, interface{}, *mempool.Pool) error { return nil }, + poolTxWithData: func(*transaction.Transaction, any, *mempool.Pool) error { return nil }, blocks: make(map[util.Uint256]*block.Block), hdrHashes: make(map[uint32]util.Uint256), txs: make(map[util.Uint256]*transaction.Transaction), @@ -149,7 +149,7 @@ func (chain *FakeChain) GetMaxVerificationGAS() int64 { } // PoolTxWithData implements the Blockchainer interface. -func (chain *FakeChain) PoolTxWithData(t *transaction.Transaction, data interface{}, mp *mempool.Pool, feer mempool.Feer, verificationFunction func(t *transaction.Transaction, data interface{}) error) error { +func (chain *FakeChain) PoolTxWithData(t *transaction.Transaction, data any, mp *mempool.Pool, feer mempool.Feer, verificationFunction func(t *transaction.Transaction, data any) error) error { return chain.poolTxWithData(t, data, mp) } diff --git a/internal/testchain/transaction.go b/internal/testchain/transaction.go index 630902ac8..cce8dd49c 100644 --- a/internal/testchain/transaction.go +++ b/internal/testchain/transaction.go @@ -76,6 +76,7 @@ func NewDeployTx(bc Ledger, name string, sender util.Uint160, r gio.Reader, conf 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 { diff --git a/internal/testserdes/testing.go b/internal/testserdes/testing.go index 3cec4801e..87645c176 100644 --- a/internal/testserdes/testing.go +++ b/internal/testserdes/testing.go @@ -12,7 +12,7 @@ import ( // MarshalUnmarshalJSON checks if the expected stays the same after // marshal/unmarshal via JSON. -func MarshalUnmarshalJSON(t *testing.T, expected, actual interface{}) { +func MarshalUnmarshalJSON(t *testing.T, expected, actual any) { data, err := json.Marshal(expected) require.NoError(t, err) require.NoError(t, json.Unmarshal(data, actual)) @@ -21,7 +21,7 @@ func MarshalUnmarshalJSON(t *testing.T, expected, actual interface{}) { // MarshalUnmarshalYAML checks if the expected stays the same after // marshal/unmarshal via YAML. -func MarshalUnmarshalYAML(t *testing.T, expected, actual interface{}) { +func MarshalUnmarshalYAML(t *testing.T, expected, actual any) { data, err := yaml.Marshal(expected) require.NoError(t, err) require.NoError(t, yaml.Unmarshal(data, actual)) diff --git a/pkg/compiler/analysis.go b/pkg/compiler/analysis.go index f725bc771..0dfefd2e6 100644 --- a/pkg/compiler/analysis.go +++ b/pkg/compiler/analysis.go @@ -24,10 +24,6 @@ var ( var ( // Go language builtin functions. goBuiltins = []string{"len", "append", "panic", "make", "copy", "recover", "delete"} - // Custom builtin utility functions. - customBuiltins = []string{ - "FromAddress", - } // Custom builtin utility functions that contain some meaningful code inside and // require code generation using standard rules, but sometimes (depending on // the expression usage condition) may be optimized at compile time. @@ -642,18 +638,6 @@ func isGoBuiltin(name string) bool { return false } -func isCustomBuiltin(f *funcScope) bool { - if !isInteropPath(f.pkg.Path()) { - return false - } - for _, n := range customBuiltins { - if f.name == n { - return true - } - } - return false -} - func isPotentialCustomBuiltin(f *funcScope, expr ast.Expr) bool { if !isInteropPath(f.pkg.Path()) { return false diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 50edd84c9..4ee702626 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -110,7 +110,7 @@ type codegen struct { docIndex map[string]int // emittedEvents contains all events emitted by the contract. - emittedEvents map[string][][]string + emittedEvents map[string][]EmittedEventInfo // invokedContracts contains invoked methods of other contracts. invokedContracts map[util.Uint160][]string @@ -458,10 +458,10 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl, pkg *types. } else { f, ok = c.funcs[c.getFuncNameFromDecl("", decl)] if ok { - // If this function is a syscall or builtin we will not convert it to bytecode. + // If this function is a syscall we will not convert it to bytecode. // If it's a potential custom builtin then it needs more specific usages research, // thus let's emit the code for it. - if isSyscall(f) || isCustomBuiltin(f) { + if isSyscall(f) { return f } c.setLabel(f.label) @@ -978,7 +978,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { f, ok = c.funcs[name] if ok { f.selector = fun.X - isBuiltin = isCustomBuiltin(f) || isPotentialCustomBuiltin(f, n) + isBuiltin = isPotentialCustomBuiltin(f, n) if canInline(f.pkg.Path(), f.decl.Name.Name, isBuiltin) { c.inlineCall(f, n) return nil @@ -1926,7 +1926,7 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) { c.emitStoreByIndex(varGlobal, c.exceptionIndex) case "delete": emit.Opcodes(c.prog.BinWriter, opcode.REMOVE) - case "FromAddress", "ToHash160": + case "ToHash160": // We can be sure that this is an ast.BasicLit just containing a simple // address string. Note that the string returned from calling Value will // contain double quotes that need to be stripped. @@ -1946,7 +1946,7 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) { // transformArgs returns a list of function arguments // which should be put on stack. // There are special cases for builtins: -// 1. With FromAddress and with ToHash160 in case if it behaves like builtin, +// 1. With ToHash160 in case if it behaves like builtin, // parameter conversion is happening at compile-time so there is no need to // push parameters on stack and perform an actual call // 2. With panic, the generated code depends on the fact if an argument was nil or a string; @@ -1954,7 +1954,7 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) { func transformArgs(fs *funcScope, fun ast.Expr, isBuiltin bool, args []ast.Expr) []ast.Expr { switch f := fun.(type) { case *ast.SelectorExpr: - if f.Sel.Name == "FromAddress" || (isBuiltin && f.Sel.Name == "ToHash160") { + if isBuiltin && f.Sel.Name == "ToHash160" { return args[1:] } if fs != nil && isSyscall(fs) { @@ -2269,7 +2269,7 @@ func newCodegen(info *buildInfo, pkg *packages.Package) *codegen { initEndOffset: -1, deployEndOffset: -1, - emittedEvents: make(map[string][][]string), + emittedEvents: make(map[string][]EmittedEventInfo), invokedContracts: make(map[util.Uint160][]string), sequencePoints: make(map[string][]DebugSeqPoint), } diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index c8f67e2b3..a7826a552 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -18,6 +18,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest/standard" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/rpcbinding" "github.com/nspcc-dev/neo-go/pkg/util" "golang.org/x/tools/go/packages" "gopkg.in/yaml.v3" @@ -52,14 +53,26 @@ type Options struct { // This setting has effect only if manifest is emitted. NoPermissionsCheck bool + // GuessEventTypes specifies if types of runtime notifications need to be guessed + // from the usage context. These types are used for RPC binding generation only and + // can be defined for events with name known at the compilation time and without + // variadic args usages. If some type is specified via config file, then the config's + // one is preferable. Currently, event's parameter type is defined from the first + // occurrence of event call. + GuessEventTypes bool + // Name is a contract's name to be written to manifest. Name string // SourceURL is a contract's source URL to be written to manifest. SourceURL string - // Runtime notifications. - ContractEvents []manifest.Event + // Runtime notifications declared in the contract configuration file. + ContractEvents []HybridEvent + + // DeclaredNamedTypes is the set of named types that were declared in the + // contract configuration type and are the part of manifest events. + DeclaredNamedTypes map[string]binding.ExtendedType // The list of standards supported by the contract. ContractSupportedStandards []string @@ -78,6 +91,23 @@ type Options struct { BindingsFile string } +// HybridEvent represents the description of event emitted by the contract squashed +// with extended event's parameters description. We have it as a separate type for +// the user's convenience. It is applied for the smart contract configuration file +// only. +type HybridEvent struct { + Name string `json:"name"` + Parameters []HybridParameter `json:"parameters"` +} + +// HybridParameter contains the manifest's event parameter description united with +// the extended type description for this parameter. It is applied for the smart +// contract configuration file only. +type HybridParameter struct { + manifest.Parameter `yaml:",inline"` + ExtendedType *binding.ExtendedType `yaml:"extendedtype,omitempty"` +} + type buildInfo struct { config *packages.Config program []*packages.Package @@ -122,7 +152,7 @@ func (c *codegen) fillImportMap(f *ast.File, pkg *packages.Package) { } } -func getBuildInfo(name string, src interface{}) (*buildInfo, error) { +func getBuildInfo(name string, src any) (*buildInfo, error) { dir, err := filepath.Abs(name) if err != nil { return nil, err @@ -309,6 +339,78 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { if len(di.NamedTypes) > 0 { cfg.NamedTypes = di.NamedTypes } + for name, et := range o.DeclaredNamedTypes { + if _, ok := cfg.NamedTypes[name]; ok { + return nil, fmt.Errorf("configured declared named type intersects with the contract's one: `%s`", name) + } + cfg.NamedTypes[name] = et + } + for _, e := range o.ContractEvents { + eStructName := rpcbinding.ToEventBindingName(e.Name) + for _, p := range e.Parameters { + pStructName := rpcbinding.ToParameterBindingName(p.Name) + if p.ExtendedType != nil { + pName := eStructName + "." + pStructName + cfg.Types[pName] = *p.ExtendedType + } + } + } + if o.GuessEventTypes { + if len(di.EmittedEvents) > 0 { + for eventName, eventUsages := range di.EmittedEvents { + var manifestEvent HybridEvent + for _, e := range o.ContractEvents { + if e.Name == eventName { + manifestEvent = e + break + } + } + if len(manifestEvent.Name) == 0 { + return nil, fmt.Errorf("inconsistent usages of event `%s`: not declared in the contract config", eventName) + } + exampleUsage := eventUsages[0] + for _, usage := range eventUsages { + if len(usage.Params) != len(manifestEvent.Parameters) { + return nil, fmt.Errorf("inconsistent usages of event `%s` against config: number of params mismatch: %d vs %d", eventName, len(exampleUsage.Params), len(manifestEvent.Parameters)) + } + for i, actual := range usage.Params { + mParam := manifestEvent.Parameters[i] + // TODO: see the TestCompile_GuessEventTypes, "SC parameter type mismatch" section, + // do we want to compare with actual.RealType? The conversion code is emitted by the + // compiler for it, so we expect the parameter to be of the proper type. + if !(mParam.Type == smartcontract.AnyType || actual.TypeSC == mParam.Type) { + return nil, fmt.Errorf("inconsistent usages of event `%s` against config: SC type of param #%d mismatch: %s vs %s", eventName, i, actual.TypeSC, mParam.Type) + } + expected := exampleUsage.Params[i] + if !actual.ExtendedType.Equals(expected.ExtendedType) { + return nil, fmt.Errorf("inconsistent usages of event `%s`: extended type of param #%d mismatch", eventName, i) + } + } + } + eBindingName := rpcbinding.ToEventBindingName(eventName) + for typeName, extType := range exampleUsage.ExtTypes { + if _, ok := cfg.NamedTypes[typeName]; !ok { + cfg.NamedTypes[typeName] = extType + } + } + + for _, p := range exampleUsage.Params { + pBindingName := rpcbinding.ToParameterBindingName(p.Name) + pname := eBindingName + "." + pBindingName + if p.RealType.TypeName != "" { + if _, ok := cfg.Overrides[pname]; !ok { + cfg.Overrides[pname] = p.RealType + } + } + if p.ExtendedType != nil { + if _, ok := cfg.Types[pname]; !ok { + cfg.Types[pname] = *p.ExtendedType + } + } + } + } + } + } data, err := yaml.Marshal(&cfg) if err != nil { return nil, fmt.Errorf("can't marshal bindings configuration: %w", err) @@ -366,24 +468,23 @@ func CreateManifest(di *DebugInfo, o *Options) (*manifest.Manifest, error) { } if !o.NoEventsCheck { for name := range di.EmittedEvents { - ev := m.ABI.GetEvent(name) - if ev == nil { + expected := m.ABI.GetEvent(name) + if expected == nil { return nil, fmt.Errorf("event '%s' is emitted but not specified in manifest", name) } - argsList := di.EmittedEvents[name] - for i := range argsList { - if len(argsList[i]) != len(ev.Parameters) { + for _, emitted := range di.EmittedEvents[name] { + if len(emitted.Params) != len(expected.Parameters) { return nil, fmt.Errorf("event '%s' should have %d parameters but has %d", - name, len(ev.Parameters), len(argsList[i])) + name, len(expected.Parameters), len(emitted.Params)) } - for j := range ev.Parameters { - if ev.Parameters[j].Type == smartcontract.AnyType { + for j := range expected.Parameters { + if expected.Parameters[j].Type == smartcontract.AnyType { continue } - expected := ev.Parameters[j].Type.String() - if argsList[i][j] != expected { + expectedT := expected.Parameters[j].Type + if emitted.Params[j].TypeSC != expectedT { return nil, fmt.Errorf("event '%s' should have '%s' as type of %d parameter, "+ - "got: %s", name, expected, j+1, argsList[i][j]) + "got: %s", name, expectedT, j+1, emitted.Params[j].TypeSC) } } } diff --git a/pkg/compiler/compiler_test.go b/pkg/compiler/compiler_test.go index 142c33110..288aaf3c7 100644 --- a/pkg/compiler/compiler_test.go +++ b/pkg/compiler/compiler_test.go @@ -102,25 +102,25 @@ func TestOnPayableChecks(t *testing.T) { t.Run("NEP-11, good", func(t *testing.T) { src := `package payable import "github.com/nspcc-dev/neo-go/pkg/interop" - func OnNEP11Payment(from interop.Hash160, amount int, tokenID []byte, data interface{}) {}` + func OnNEP11Payment(from interop.Hash160, amount int, tokenID []byte, data any) {}` require.NoError(t, compileAndCheck(t, src)) }) t.Run("NEP-11, bad", func(t *testing.T) { src := `package payable import "github.com/nspcc-dev/neo-go/pkg/interop" - func OnNEP11Payment(from interop.Hash160, amount int, oldParam string, tokenID []byte, data interface{}) {}` + func OnNEP11Payment(from interop.Hash160, amount int, oldParam string, tokenID []byte, data any) {}` require.Error(t, compileAndCheck(t, src)) }) t.Run("NEP-17, good", func(t *testing.T) { src := `package payable import "github.com/nspcc-dev/neo-go/pkg/interop" - func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) {}` + func OnNEP17Payment(from interop.Hash160, amount int, data any) {}` require.NoError(t, compileAndCheck(t, src)) }) t.Run("NEP-17, bad", func(t *testing.T) { src := `package payable import "github.com/nspcc-dev/neo-go/pkg/interop" - func OnNEP17Payment(from interop.Hash160, amount int, data interface{}, extra int) {}` + func OnNEP17Payment(from interop.Hash160, amount int, data any, extra int) {}` require.Error(t, compileAndCheck(t, src)) }) } @@ -159,16 +159,16 @@ func TestEventWarnings(t *testing.T) { }) t.Run("wrong parameter number", func(t *testing.T) { _, err = compiler.CreateManifest(di, &compiler.Options{ - ContractEvents: []manifest.Event{{Name: "Event"}}, + ContractEvents: []compiler.HybridEvent{{Name: "Event"}}, Name: "payable", }) require.Error(t, err) }) t.Run("wrong parameter type", func(t *testing.T) { _, err = compiler.CreateManifest(di, &compiler.Options{ - ContractEvents: []manifest.Event{{ + ContractEvents: []compiler.HybridEvent{{ Name: "Event", - Parameters: []manifest.Parameter{manifest.NewParameter("number", smartcontract.StringType)}, + Parameters: []compiler.HybridParameter{{Parameter: manifest.NewParameter("number", smartcontract.StringType)}}, }}, Name: "payable", }) @@ -176,9 +176,9 @@ func TestEventWarnings(t *testing.T) { }) t.Run("any parameter type", func(t *testing.T) { _, err = compiler.CreateManifest(di, &compiler.Options{ - ContractEvents: []manifest.Event{{ + ContractEvents: []compiler.HybridEvent{{ Name: "Event", - Parameters: []manifest.Parameter{manifest.NewParameter("number", smartcontract.AnyType)}, + Parameters: []compiler.HybridParameter{{Parameter: manifest.NewParameter("number", smartcontract.AnyType)}}, }}, Name: "payable", }) @@ -186,9 +186,9 @@ func TestEventWarnings(t *testing.T) { }) t.Run("good", func(t *testing.T) { _, err = compiler.CreateManifest(di, &compiler.Options{ - ContractEvents: []manifest.Event{{ + ContractEvents: []compiler.HybridEvent{{ Name: "Event", - Parameters: []manifest.Parameter{manifest.NewParameter("number", smartcontract.IntegerType)}, + Parameters: []compiler.HybridParameter{{Parameter: manifest.NewParameter("number", smartcontract.IntegerType)}}, }}, Name: "payable", }) @@ -224,7 +224,7 @@ func TestEventWarnings(t *testing.T) { require.Error(t, err) _, err = compiler.CreateManifest(di, &compiler.Options{ - ContractEvents: []manifest.Event{{Name: "Event"}}, + ContractEvents: []compiler.HybridEvent{{Name: "Event"}}, Name: "eventTest", }) require.NoError(t, err) @@ -234,7 +234,7 @@ func TestEventWarnings(t *testing.T) { src := `package payable import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" func Main() { - runtime.Notify("Event", []interface{}{1}...) + runtime.Notify("Event", []any{1}...) }` _, di, err := compiler.CompileWithOptions("eventTest.go", strings.NewReader(src), nil) @@ -242,9 +242,9 @@ func TestEventWarnings(t *testing.T) { _, err = compiler.CreateManifest(di, &compiler.Options{ Name: "eventTest", - ContractEvents: []manifest.Event{{ + ContractEvents: []compiler.HybridEvent{{ Name: "Event", - Parameters: []manifest.Parameter{manifest.NewParameter("number", smartcontract.IntegerType)}, + Parameters: []compiler.HybridParameter{{Parameter: manifest.NewParameter("number", smartcontract.IntegerType)}}, }}, }) require.NoError(t, err) @@ -260,7 +260,7 @@ func TestNotifyInVerify(t *testing.T) { t.Run(name, func(t *testing.T) { src := fmt.Sprintf(srcTmpl, name) _, _, err := compiler.CompileWithOptions("eventTest.go", strings.NewReader(src), - &compiler.Options{ContractEvents: []manifest.Event{{Name: "Event"}}}) + &compiler.Options{ContractEvents: []compiler.HybridEvent{{Name: "Event"}}}) require.Error(t, err) t.Run("suppress", func(t *testing.T) { @@ -407,7 +407,7 @@ func TestUnnamedParameterCheck(t *testing.T) { t.Run("interface", func(t *testing.T) { src := ` package testcase - func OnNEP17Payment(h string, i int, _ interface{}){} + func OnNEP17Payment(h string, i int, _ any){} ` _, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil) require.Error(t, err) @@ -416,7 +416,7 @@ func TestUnnamedParameterCheck(t *testing.T) { t.Run("a set of unnamed params", func(t *testing.T) { src := ` package testcase - func OnNEP17Payment(_ string, _ int, _ interface{}){} + func OnNEP17Payment(_ string, _ int, _ any){} ` _, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil) require.Error(t, err) @@ -447,11 +447,19 @@ func TestUnnamedParameterCheck(t *testing.T) { _, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil) require.NoError(t, err) }) + t.Run("good, use any keyword", func(t *testing.T) { + src := ` + package testcase + func OnNEP17Payment(s string, i int, iface any){} + ` + _, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil) + require.NoError(t, err) + }) t.Run("method with unnamed params", func(t *testing.T) { src := ` package testcase type A int - func (rsv A) OnNEP17Payment(_ string, _ int, iface interface{}){} + func (rsv A) OnNEP17Payment(_ string, _ int, iface any){} ` _, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil) require.NoError(t, err) // it's OK for exported method to have unnamed params as it won't be included into manifest diff --git a/pkg/compiler/convert_test.go b/pkg/compiler/convert_test.go index eba50b332..0995dc685 100644 --- a/pkg/compiler/convert_test.go +++ b/pkg/compiler/convert_test.go @@ -16,7 +16,7 @@ import ( type convertTestCase struct { returnType string argValue string - result interface{} + result any } func getFunctionName(typ string) string { @@ -85,7 +85,7 @@ func TestTypeAssertion(t *testing.T) { src := `package foo func Main() int { a := []byte{1} - var u interface{} + var u any u = a return u.(int) }` @@ -95,7 +95,7 @@ func TestTypeAssertion(t *testing.T) { src := `package foo func Main() int { a := []byte{1} - var u interface{} + var u any u = a var ret = u.(int) return ret @@ -106,7 +106,7 @@ func TestTypeAssertion(t *testing.T) { src := `package foo func Main() int { a := []byte{1} - var u interface{} + var u any u = a var ret int ret = u.(int) @@ -118,7 +118,7 @@ func TestTypeAssertion(t *testing.T) { src := `package foo func Main() int { a := []byte{1} - var u interface{} + var u any u = a ret := u.(int) return ret @@ -132,7 +132,7 @@ func TestTypeAssertionWithOK(t *testing.T) { src := `package foo func Main() bool { a := 1 - var u interface{} + var u any u = a var _, ok = u.(int) // *ast.GenDecl return ok @@ -145,7 +145,7 @@ func TestTypeAssertionWithOK(t *testing.T) { src := `package foo func Main() bool { a := 1 - var u interface{} + var u any u = a var ok bool _, ok = u.(int) // *ast.AssignStmt @@ -159,7 +159,7 @@ func TestTypeAssertionWithOK(t *testing.T) { src := `package foo func Main() bool { a := 1 - var u interface{} + var u any u = a _, ok := u.(int) // *ast.AssignStmt return ok @@ -231,7 +231,7 @@ func TestInterfaceTypeConversion(t *testing.T) { src := `package foo func Main() int { a := 1 - b := interface{}(a).(int) + b := any(a).(int) return b }` eval(t, src, big.NewInt(1)) diff --git a/pkg/compiler/debug.go b/pkg/compiler/debug.go index 9785de401..a583fc3bd 100644 --- a/pkg/compiler/debug.go +++ b/pkg/compiler/debug.go @@ -29,9 +29,14 @@ type DebugInfo struct { // NamedTypes are exported structured types that have some name (even // if the original structure doesn't) and a number of internal fields. NamedTypes map[string]binding.ExtendedType `json:"-"` - Events []EventDebugInfo `json:"events"` - // EmittedEvents contains events occurring in code. - EmittedEvents map[string][][]string `json:"-"` + // Events are the events that contract is allowed to emit and that have to + // be presented in the resulting contract manifest and debug info file. + Events []EventDebugInfo `json:"events"` + // EmittedEvents contains events occurring in code, i.e. events emitted + // via runtime.Notify(...) call in the contract code if they have constant + // names and doesn't have ellipsis arguments. EmittedEvents are not related + // to the debug info and are aimed to serve bindings generation. + EmittedEvents map[string][]EmittedEventInfo `json:"-"` // InvokedContracts contains foreign contract invocations. InvokedContracts map[util.Uint160][]string `json:"-"` // StaticVariables contains a list of static variable names and types. @@ -103,7 +108,7 @@ type DebugRange struct { End uint16 } -// DebugParam represents the variables's name and type. +// DebugParam represents the variable's name and type. type DebugParam struct { Name string `json:"name"` Type string `json:"type"` @@ -112,6 +117,14 @@ type DebugParam struct { TypeSC smartcontract.ParamType `json:"-"` } +// EmittedEventInfo describes information about single emitted event got from +// the contract code. It has the map of extended types used as the parameters to +// runtime.Notify(...) call (if any) and the parameters info itself. +type EmittedEventInfo struct { + ExtTypes map[string]binding.ExtendedType + Params []DebugParam +} + func (c *codegen) saveSequencePoint(n ast.Node) { name := "init" if c.scope != nil { @@ -308,7 +321,7 @@ func scAndVMInteropTypeFromExpr(named *types.Named, isPointer bool) (smartcontra } return smartcontract.InteropInterfaceType, stackitem.InteropT, - binding.Override{TypeName: "interface{}"}, + binding.Override{TypeName: "any"}, &binding.ExtendedType{Base: smartcontract.InteropInterfaceType, Interface: "iterator"} // Temporarily all interops are iterators. } @@ -318,7 +331,7 @@ func (c *codegen) scAndVMTypeFromExpr(typ ast.Expr, exts map[string]binding.Exte func (c *codegen) scAndVMTypeFromType(t types.Type, exts map[string]binding.ExtendedType) (smartcontract.ParamType, stackitem.Type, binding.Override, *binding.ExtendedType) { if t == nil { - return smartcontract.AnyType, stackitem.AnyT, binding.Override{TypeName: "interface{}"}, nil + return smartcontract.AnyType, stackitem.AnyT, binding.Override{TypeName: "any"}, nil } var isPtr bool @@ -357,7 +370,7 @@ func (c *codegen) scAndVMTypeFromType(t types.Type, exts map[string]binding.Exte over.TypeName = "string" return smartcontract.StringType, stackitem.ByteArrayT, over, nil default: - over.TypeName = "interface{}" + over.TypeName = "any" return smartcontract.AnyType, stackitem.AnyT, over, nil } case *types.Map: @@ -373,10 +386,12 @@ func (c *codegen) scAndVMTypeFromType(t types.Type, exts map[string]binding.Exte over.TypeName = "map[" + t.Key().String() + "]" + over.TypeName return smartcontract.MapType, stackitem.MapT, over, et case *types.Struct: + var extName string if isNamed { over.Package = named.Obj().Pkg().Path() over.TypeName = named.Obj().Pkg().Name() + "." + named.Obj().Name() _ = c.genStructExtended(t, over.TypeName, exts) + extName = over.TypeName } else { name := "unnamed" if exts != nil { @@ -385,11 +400,14 @@ func (c *codegen) scAndVMTypeFromType(t types.Type, exts map[string]binding.Exte } _ = c.genStructExtended(t, name, exts) } + // For bindings configurator this structure becomes named in fact. Its name + // is "unnamed[X...X]". + extName = name } return smartcontract.ArrayType, stackitem.StructT, over, &binding.ExtendedType{ // Value-less, refer to exts. Base: smartcontract.ArrayType, - Name: over.TypeName, + Name: extName, } case *types.Slice: @@ -412,7 +430,7 @@ func (c *codegen) scAndVMTypeFromType(t types.Type, exts map[string]binding.Exte } return smartcontract.ArrayType, stackitem.ArrayT, over, et default: - over.TypeName = "interface{}" + over.TypeName = "any" return smartcontract.AnyType, stackitem.AnyT, over, nil } } @@ -580,9 +598,20 @@ func (di *DebugInfo) ConvertToManifest(o *Options) (*manifest.Manifest, error) { if o.ContractSupportedStandards != nil { result.SupportedStandards = o.ContractSupportedStandards } + events := make([]manifest.Event, len(o.ContractEvents)) + for i, e := range o.ContractEvents { + params := make([]manifest.Parameter, len(e.Parameters)) + for j, p := range e.Parameters { + params[j] = p.Parameter + } + events[i] = manifest.Event{ + Name: o.ContractEvents[i].Name, + Parameters: params, + } + } result.ABI = manifest.ABI{ Methods: methods, - Events: o.ContractEvents, + Events: events, } if result.ABI.Events == nil { result.ABI.Events = make([]manifest.Event, 0) diff --git a/pkg/compiler/debug_test.go b/pkg/compiler/debug_test.go index dbe382561..4861e29b2 100644 --- a/pkg/compiler/debug_test.go +++ b/pkg/compiler/debug_test.go @@ -65,7 +65,7 @@ func MethodParams(addr interop.Hash160, h interop.Hash256, type MyStruct struct {} func (ms MyStruct) MethodOnStruct() { } func (ms *MyStruct) MethodOnPointerToStruct() { } -func _deploy(data interface{}, isUpdate bool) { x := 1; _ = x } +func _deploy(data any, isUpdate bool) { x := 1; _ = x } ` ne, d, err := CompileWithOptions("foo.go", strings.NewReader(src), nil) diff --git a/pkg/compiler/for_test.go b/pkg/compiler/for_test.go index c7bd36cc1..456678f92 100644 --- a/pkg/compiler/for_test.go +++ b/pkg/compiler/for_test.go @@ -32,7 +32,7 @@ func TestEntryPointWithArgs(t *testing.T) { src := ` package foo - func Main(args []interface{}) int { + func Main(args []any) int { return 2 + args[1].(int) } ` @@ -44,7 +44,7 @@ func TestEntryPointWithMethodAndArgs(t *testing.T) { src := ` package foo - func Main(method string, args []interface{}) int { + func Main(method string, args []any) int { if method == "foobar" { return 2 + args[1].(int) } diff --git a/pkg/compiler/function_call_test.go b/pkg/compiler/function_call_test.go index c409108be..1c37269c7 100644 --- a/pkg/compiler/function_call_test.go +++ b/pkg/compiler/function_call_test.go @@ -89,10 +89,10 @@ func TestNotAssignedFunctionCall(t *testing.T) { }) t.Run("Builtin", func(t *testing.T) { src := `package foo - import "github.com/nspcc-dev/neo-go/pkg/interop/util" + import "github.com/nspcc-dev/neo-go/pkg/interop/lib/address" func Main() int { - util.FromAddress("NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8") - util.FromAddress("NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8") + address.ToHash160("NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8") + address.ToHash160("NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8") return 1 }` eval(t, src, big.NewInt(1)) @@ -173,6 +173,21 @@ func TestFunctionCallWithInterfaceType(t *testing.T) { eval(t, src, big.NewInt(10)) } +func TestFunctionCallWithAnyKeywordType(t *testing.T) { + src := ` + package testcase + func Main() any { + x := getSomeInteger(10) + return x + } + + func getSomeInteger(x any) any { + return x + } + ` + eval(t, src, big.NewInt(10)) +} + func TestFunctionCallMultiArg(t *testing.T) { src := ` package testcase @@ -312,7 +327,7 @@ func TestJumpOptimize(t *testing.T) { var a int _ = a } - func _deploy(_ interface{}, upd bool) { + func _deploy(_ any, upd bool) { if true {} else {} t := upd _ = t diff --git a/pkg/compiler/global_test.go b/pkg/compiler/global_test.go index 7b972dd5d..bc0a617c3 100644 --- a/pkg/compiler/global_test.go +++ b/pkg/compiler/global_test.go @@ -137,9 +137,9 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { func f() int { return 4 }` - eval(t, src, big.NewInt(4), []interface{}{opcode.INITSSLOT, []byte{2}}, // sslot for A and C + eval(t, src, big.NewInt(4), []any{opcode.INITSSLOT, []byte{2}}, // sslot for A and C opcode.PUSH1, opcode.STSFLD0, // store A - []interface{}{opcode.CALL, []byte{10}}, opcode.DROP, // evaluate B and drop + []any{opcode.CALL, []byte{10}}, opcode.DROP, // evaluate B and drop opcode.PUSH3, opcode.STSFLD1, opcode.RET, // store C opcode.LDSFLD0, opcode.LDSFLD1, opcode.ADD, opcode.RET, // Main opcode.PUSH4, opcode.RET) // f @@ -166,7 +166,7 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { func Main() int { return A }` - eval(t, src, big.NewInt(1), []interface{}{opcode.INITSSLOT, []byte{1}}, // sslot for A + eval(t, src, big.NewInt(1), []any{opcode.INITSSLOT, []byte{1}}, // sslot for A opcode.PUSH1, opcode.STSFLD0, opcode.RET, // store A opcode.LDSFLD0, opcode.RET) // Main }) @@ -176,7 +176,7 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { func Main() int { return A + B }` - eval(t, src, big.NewInt(4), []interface{}{opcode.INITSSLOT, []byte{2}}, // sslot for A and B + eval(t, src, big.NewInt(4), []any{opcode.INITSSLOT, []byte{2}}, // sslot for A and B opcode.PUSH1, opcode.STSFLD0, // store A opcode.PUSH3, opcode.STSFLD1, opcode.RET, // store B opcode.LDSFLD0, opcode.LDSFLD1, opcode.ADD, opcode.RET) // Main @@ -190,7 +190,7 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { func Main() int { return A + B + C }` - eval(t, src, big.NewInt(4), []interface{}{opcode.INITSSLOT, []byte{3}}, // sslot for A, B, C + eval(t, src, big.NewInt(4), []any{opcode.INITSSLOT, []byte{3}}, // sslot for A, B, C opcode.PUSH1, opcode.STSFLD0, // store A opcode.PUSH3, opcode.STSFLD1, // store B opcode.PUSH0, opcode.STSFLD2, opcode.RET, // store C @@ -205,9 +205,9 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { func f() int { return 2 }` - eval(t, src, big.NewInt(1), []interface{}{opcode.INITSSLOT, []byte{1}}, // sslot for A + eval(t, src, big.NewInt(1), []any{opcode.INITSSLOT, []byte{1}}, // sslot for A opcode.PUSH1, opcode.STSFLD0, // store A - []interface{}{opcode.CALL, []byte{6}}, opcode.DROP, opcode.RET, // evaluate Unused1 (call to f) and drop its value + []any{opcode.CALL, []byte{6}}, opcode.DROP, opcode.RET, // evaluate Unused1 (call to f) and drop its value opcode.LDSFLD0, opcode.RET, // Main opcode.PUSH2, opcode.RET) // f }) @@ -221,7 +221,7 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { func f() int { return 2 }` - eval(t, src, big.NewInt(1), []interface{}{opcode.CALL, []byte{8}}, opcode.PUSH1, opcode.PACKSTRUCT, opcode.DROP, opcode.RET, // evaluate struct val + eval(t, src, big.NewInt(1), []any{opcode.CALL, []byte{8}}, opcode.PUSH1, opcode.PACKSTRUCT, opcode.DROP, opcode.RET, // evaluate struct val opcode.PUSH1, opcode.RET, // Main opcode.PUSH2, opcode.RET) // f }) @@ -319,11 +319,11 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { func f() int { return 2 }` - eval(t, src, big.NewInt(1), []interface{}{opcode.INITSSLOT, []byte{3}}, // sslot for A + eval(t, src, big.NewInt(1), []any{opcode.INITSSLOT, []byte{3}}, // sslot for A opcode.PUSH1, opcode.STSFLD0, // store A opcode.LDSFLD0, opcode.PUSH1, opcode.ADD, opcode.STSFLD1, // store B opcode.PUSH3, opcode.STSFLD2, // store C - opcode.LDSFLD1, []interface{}{opcode.CALL, []byte{9}}, opcode.ADD, opcode.LDSFLD2, opcode.ADD, opcode.DROP, opcode.RET, // evaluate D and drop + opcode.LDSFLD1, []any{opcode.CALL, []byte{9}}, opcode.ADD, opcode.LDSFLD2, opcode.ADD, opcode.DROP, opcode.RET, // evaluate D and drop opcode.PUSH1, opcode.RET, // Main opcode.PUSH2, opcode.RET) // f }) @@ -337,11 +337,11 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { func f(a int) int { return a }` - eval(t, src, big.NewInt(1), []interface{}{opcode.INITSSLOT, []byte{1}}, // sslot for A + eval(t, src, big.NewInt(1), []any{opcode.INITSSLOT, []byte{1}}, // sslot for A opcode.PUSH1, opcode.STSFLD0, // store A - opcode.LDSFLD0, []interface{}{opcode.CALL, []byte{6}}, opcode.DROP, opcode.RET, // evaluate B and drop + opcode.LDSFLD0, []any{opcode.CALL, []byte{6}}, opcode.DROP, opcode.RET, // evaluate B and drop opcode.PUSH1, opcode.RET, // Main - []interface{}{opcode.INITSLOT, []byte{0, 1}}, opcode.LDARG0, opcode.RET) // f + []any{opcode.INITSLOT, []byte{0, 1}}, opcode.LDARG0, opcode.RET) // f }) t.Run("named, inside multi-specs and multi-vals var declaration", func(t *testing.T) { src := `package foo @@ -355,7 +355,7 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { func f() int { return 5 }` - eval(t, src, big.NewInt(1), opcode.PUSH3, []interface{}{opcode.CALL, []byte{7}}, opcode.ADD, opcode.DROP, opcode.RET, // evaluate and drop A + eval(t, src, big.NewInt(1), opcode.PUSH3, []any{opcode.CALL, []byte{7}}, opcode.ADD, opcode.DROP, opcode.RET, // evaluate and drop A opcode.PUSH1, opcode.RET, // Main opcode.PUSH5, opcode.RET) // f }) @@ -367,7 +367,7 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { func Main() int { return A }` - eval(t, src, big.NewInt(1), []interface{}{opcode.INITSSLOT, []byte{1}}, // sslot for A + eval(t, src, big.NewInt(1), []any{opcode.INITSSLOT, []byte{1}}, // sslot for A opcode.PUSH1, opcode.STSFLD0, opcode.RET, // store A opcode.LDSFLD0, opcode.RET) // Main }) @@ -395,8 +395,8 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { func f() (int, int) { return 3, 4 }` - eval(t, src, big.NewInt(3), []interface{}{opcode.INITSSLOT, []byte{1}}, // sslot for A - []interface{}{opcode.CALL, []byte{7}}, opcode.STSFLD0, opcode.DROP, opcode.RET, // evaluate and store A, drop B + eval(t, src, big.NewInt(3), []any{opcode.INITSSLOT, []byte{1}}, // sslot for A + []any{opcode.CALL, []byte{7}}, opcode.STSFLD0, opcode.DROP, opcode.RET, // evaluate and store A, drop B opcode.LDSFLD0, opcode.RET, // Main opcode.PUSH4, opcode.PUSH3, opcode.RET) // f }) @@ -460,12 +460,12 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { func f(a int) int { return a }` - eval(t, src, big.NewInt(1), []interface{}{opcode.INITSSLOT, []byte{2}}, // sslot for A, B + eval(t, src, big.NewInt(1), []any{opcode.INITSSLOT, []byte{2}}, // sslot for A, B opcode.PUSH2, opcode.STSFLD0, // store A opcode.PUSH3, opcode.LDSFLD0, opcode.PUSH1, opcode.PUSH3, opcode.PACK, opcode.PUSH1, opcode.PICKITEM, opcode.STSFLD1, // evaluate B - opcode.PUSH1, []interface{}{opcode.CALL, []byte{8}}, opcode.LDSFLD1, opcode.ADD, opcode.DROP, opcode.RET, // evalute C and drop + opcode.PUSH1, []any{opcode.CALL, []byte{8}}, opcode.LDSFLD1, opcode.ADD, opcode.DROP, opcode.RET, // evalute C and drop opcode.PUSH1, opcode.RET, // Main - []interface{}{opcode.INITSLOT, []byte{0, 1}}, opcode.LDARG0, opcode.RET) // f + []any{opcode.INITSLOT, []byte{0, 1}}, opcode.LDARG0, opcode.RET) // f }) t.Run("index expression", func(t *testing.T) { src := `package foo @@ -477,13 +477,13 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { func f(a int) int { return a }` - eval(t, src, big.NewInt(1), []interface{}{opcode.INITSSLOT, []byte{1}}, // sslot for Unused + eval(t, src, big.NewInt(1), []any{opcode.INITSSLOT, []byte{1}}, // sslot for Unused opcode.PUSH2, opcode.STSFLD0, // store Unused - opcode.PUSH1, []interface{}{opcode.CALL, []byte{14}}, // call f(1) + opcode.PUSH1, []any{opcode.CALL, []byte{14}}, // call f(1) opcode.PUSH3, opcode.PUSH2, opcode.PUSH1, opcode.PUSH3, opcode.PACK, opcode.LDSFLD0, opcode.PICKITEM, // eval index expression opcode.ADD, opcode.DROP, opcode.RET, // eval and drop A opcode.PUSH1, opcode.RET, // Main - []interface{}{opcode.INITSLOT, []byte{0, 1}}, opcode.LDARG0, opcode.RET) // f(a) + []any{opcode.INITSLOT, []byte{0, 1}}, opcode.LDARG0, opcode.RET) // f(a) }) t.Run("used via nested function calls", func(t *testing.T) { src := `package foo @@ -497,10 +497,10 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { func g() int { return A }` - eval(t, src, big.NewInt(1), []interface{}{opcode.INITSSLOT, []byte{1}}, // sslot for A + eval(t, src, big.NewInt(1), []any{opcode.INITSSLOT, []byte{1}}, // sslot for A opcode.PUSH1, opcode.STSFLD0, opcode.RET, // store A - []interface{}{opcode.CALL, []byte{3}}, opcode.RET, // Main - []interface{}{opcode.CALL, []byte{3}}, opcode.RET, // f + []any{opcode.CALL, []byte{3}}, opcode.RET, // Main + []any{opcode.CALL, []byte{3}}, opcode.RET, // f opcode.LDSFLD0, opcode.RET) // g }) t.Run("struct field name matches global var name", func(t *testing.T) { @@ -511,7 +511,7 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { func Main() int { return str.Int }` - eval(t, src, big.NewInt(2), []interface{}{opcode.INITSSLOT, []byte{1}}, // sslot for str + eval(t, src, big.NewInt(2), []any{opcode.INITSSLOT, []byte{1}}, // sslot for str opcode.PUSH2, opcode.PUSH1, opcode.PACKSTRUCT, opcode.STSFLD0, opcode.RET, // store str opcode.LDSFLD0, opcode.PUSH0, opcode.PICKITEM, opcode.RET) // Main }) @@ -929,11 +929,11 @@ func TestUnderscoreGlobalVarDontEmitCode(t *testing.T) { func f(a, b int) (int, int, int) { return 8, 9, 10 }` - eval(t, src, big.NewInt(1), []interface{}{opcode.INITSSLOT, []byte{2}}, // sslot for A, B + eval(t, src, big.NewInt(1), []any{opcode.INITSSLOT, []byte{2}}, // sslot for A, B opcode.PUSH2, opcode.STSFLD0, // store A opcode.PUSH5, opcode.STSFLD1, // store B - opcode.LDSFLD0, opcode.LDSFLD1, opcode.SWAP, []interface{}{opcode.CALL, []byte{8}}, // evaluate f(A,B) + opcode.LDSFLD0, opcode.LDSFLD1, opcode.SWAP, []any{opcode.CALL, []byte{8}}, // evaluate f(A,B) opcode.DROP, opcode.DROP, opcode.DROP, opcode.RET, // drop result of f(A,B) opcode.PUSH1, opcode.RET, // Main - []interface{}{opcode.INITSLOT, []byte{0, 2}}, opcode.PUSH10, opcode.PUSH9, opcode.PUSH8, opcode.RET) // f + []any{opcode.INITSLOT, []byte{0, 2}}, opcode.PUSH10, opcode.PUSH9, opcode.PUSH8, opcode.RET) // f } diff --git a/pkg/compiler/inline.go b/pkg/compiler/inline.go index 1653d8638..b6cd03d3b 100644 --- a/pkg/compiler/inline.go +++ b/pkg/compiler/inline.go @@ -7,6 +7,7 @@ import ( "go/types" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/binding" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" @@ -172,11 +173,21 @@ func (c *codegen) processNotify(f *funcScope, args []ast.Expr, hasEllipsis bool) return nil } - params := make([]string, 0, len(args[1:])) + params := make([]DebugParam, 0, len(args[1:])) vParams := make([]*stackitem.Type, 0, len(args[1:])) + // extMap holds the extended parameter types used for the given event call. + // It will be unified with the common extMap later during bindings config + // generation. + extMap := make(map[string]binding.ExtendedType) for _, p := range args[1:] { - st, vt, _, _ := c.scAndVMTypeFromExpr(p, nil) - params = append(params, st.String()) + st, vt, over, extT := c.scAndVMTypeFromExpr(p, extMap) + params = append(params, DebugParam{ + Name: "", // Parameter name will be filled in several lines below if the corresponding event exists in the buildinfo.options. + Type: vt.String(), + RealType: over, + ExtendedType: extT, + TypeSC: st, + }) vParams = append(vParams, &vt) } @@ -187,36 +198,43 @@ func (c *codegen) processNotify(f *funcScope, args []ast.Expr, hasEllipsis bool) return nil } var eventFound bool - if c.buildInfo.options != nil && c.buildInfo.options.ContractEvents != nil && !c.buildInfo.options.NoEventsCheck { + if c.buildInfo.options != nil && c.buildInfo.options.ContractEvents != nil { for _, e := range c.buildInfo.options.ContractEvents { if e.Name == name && len(e.Parameters) == len(vParams) { eventFound = true for i, scParam := range e.Parameters { - expectedType := scParam.Type.ConvertToStackitemType() - // No need to cast if the desired type is unknown. - if expectedType == stackitem.AnyT || - // Do not cast if desired type is Interop, the actual type is likely to be Any, leave the resolving to runtime.Notify. - expectedType == stackitem.InteropT || - // No need to cast if actual parameter type matches the desired one. - *vParams[i] == expectedType || - // expectedType doesn't contain Buffer anyway, but if actual variable type is Buffer, - // then runtime.Notify will convert it to ByteArray automatically, thus no need to emit conversion code. - (*vParams[i] == stackitem.BufferT && expectedType == stackitem.ByteArrayT) { - vParams[i] = nil - } else { - // For other cases the conversion code will be emitted using vParams... - vParams[i] = &expectedType - // ...thus, update emitted notification info in advance. - params[i] = scParam.Type.String() + params[i].Name = scParam.Name + if !c.buildInfo.options.NoEventsCheck { + expectedType := scParam.Type.ConvertToStackitemType() + // No need to cast if the desired type is unknown. + if expectedType == stackitem.AnyT || + // Do not cast if desired type is Interop, the actual type is likely to be Any, leave the resolving to runtime.Notify. + expectedType == stackitem.InteropT || + // No need to cast if actual parameter type matches the desired one. + *vParams[i] == expectedType || + // expectedType doesn't contain Buffer anyway, but if actual variable type is Buffer, + // then runtime.Notify will convert it to ByteArray automatically, thus no need to emit conversion code. + (*vParams[i] == stackitem.BufferT && expectedType == stackitem.ByteArrayT) { + vParams[i] = nil + } else { + // For other cases the conversion code will be emitted using vParams... + vParams[i] = &expectedType + // ...thus, update emitted notification info in advance. + params[i].Type = scParam.Type.String() + params[i].TypeSC = scParam.Type + } } } } } } - c.emittedEvents[name] = append(c.emittedEvents[name], params) + c.emittedEvents[name] = append(c.emittedEvents[name], EmittedEventInfo{ + ExtTypes: extMap, + Params: params, + }) // Do not enforce perfect expected/actual events match on this step, the final // check wil be performed after compilation if --no-events option is off. - if eventFound { + if eventFound && !c.buildInfo.options.NoEventsCheck { return vParams } return nil diff --git a/pkg/compiler/interop_test.go b/pkg/compiler/interop_test.go index 2ba7a5f2f..c8f77ce24 100644 --- a/pkg/compiler/interop_test.go +++ b/pkg/compiler/interop_test.go @@ -39,7 +39,7 @@ func TestTypeConstantSize(t *testing.T) { src := `package foo import "github.com/nspcc-dev/neo-go/pkg/interop" var a %T // type declaration is always ok - func Main() interface{} { + func Main() any { return %#v }` @@ -71,62 +71,13 @@ func TestTypeConstantSize(t *testing.T) { }) } -func TestFromAddress(t *testing.T) { - as1 := "NQRLhCpAru9BjGsMwk67vdMwmzKMRgsnnN" - addr1, err := address.StringToUint160(as1) - require.NoError(t, err) - - as2 := "NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8" - addr2, err := address.StringToUint160(as2) - require.NoError(t, err) - - t.Run("append 2 addresses", func(t *testing.T) { - src := ` - package foo - import "github.com/nspcc-dev/neo-go/pkg/interop/util" - func Main() []byte { - addr1 := util.FromAddress("` + as1 + `") - addr2 := util.FromAddress("` + as2 + `") - sum := append(addr1, addr2...) - return sum - } - ` - - eval(t, src, append(addr1.BytesBE(), addr2.BytesBE()...)) - }) - - t.Run("append 2 addresses inline", func(t *testing.T) { - src := ` - package foo - import "github.com/nspcc-dev/neo-go/pkg/interop/util" - func Main() []byte { - addr1 := util.FromAddress("` + as1 + `") - sum := append(addr1, util.FromAddress("` + as2 + `")...) - return sum - } - ` - - eval(t, src, append(addr1.BytesBE(), addr2.BytesBE()...)) - }) - - t.Run("AliasPackage", func(t *testing.T) { - src := ` - package foo - import uu "github.com/nspcc-dev/neo-go/pkg/interop/util" - func Main() []byte { - addr1 := uu.FromAddress("` + as1 + `") - addr2 := uu.FromAddress("` + as2 + `") - sum := append(addr1, addr2...) - return sum - }` - eval(t, src, append(addr1.BytesBE(), addr2.BytesBE()...)) - }) -} - func TestAddressToHash160BuiltinConversion(t *testing.T) { a := "NQRLhCpAru9BjGsMwk67vdMwmzKMRgsnnN" h, err := address.StringToUint160(a) require.NoError(t, err) + a2 := "NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8" + addr2, err := address.StringToUint160(a2) + require.NoError(t, err) t.Run("builtin conversion", func(t *testing.T) { src := `package foo import ( @@ -163,6 +114,18 @@ func TestAddressToHash160BuiltinConversion(t *testing.T) { // On the contrary, there should be an address string. require.True(t, strings.Contains(string(prog), a)) }) + t.Run("AliasPackage", func(t *testing.T) { + src := ` + package foo + import ad "github.com/nspcc-dev/neo-go/pkg/interop/lib/address" + func Main() []byte { + addr1 := ad.ToHash160("` + a + `") + addr2 := ad.ToHash160("` + a2 + `") + sum := append(addr1, addr2...) + return sum + }` + eval(t, src, append(h.BytesBE(), addr2.BytesBE()...)) + }) } func TestInvokeAddressToFromHash160(t *testing.T) { @@ -532,7 +495,7 @@ func TestCallWithVersion(t *testing.T) { "github.com/nspcc-dev/neo-go/pkg/interop/contract" util "github.com/nspcc-dev/neo-go/pkg/interop/lib/contract" ) - func CallWithVersion(hash interop.Hash160, version int, method string) interface{} { + func CallWithVersion(hash interop.Hash160, version int, method string) any { return util.CallWithVersion(hash, version, method, contract.All) }` ctr := neotest.CompileSource(t, e.CommitteeHash, strings.NewReader(src), &compiler.Options{Name: "Helper"}) @@ -569,7 +532,7 @@ func TestForcedNotifyArgumentsConversion(t *testing.T) { runtime.Notify("withoutEllipsis", arg0, arg1, arg2, arg3, arg4, 5, f(6)) // The fifth argument is basic literal. } func WithEllipsis() { - arg := []interface{}{0, 1, f(2), 3, 4, 5, 6} + arg := []any{0, 1, f(2), 3, 4, 5, 6} runtime.Notify("withEllipsis", arg...) } func f(i int) int { @@ -579,13 +542,13 @@ func TestForcedNotifyArgumentsConversion(t *testing.T) { if count != len(expectedVMParamTypes) { t.Fatalf("parameters count mismatch: %d vs %d", count, len(expectedVMParamTypes)) } - scParams := make([]manifest.Parameter, len(targetSCParamTypes)) + scParams := make([]compiler.HybridParameter, len(targetSCParamTypes)) vmParams := make([]stackitem.Item, len(expectedVMParamTypes)) for i := range scParams { - scParams[i] = manifest.Parameter{ + scParams[i] = compiler.HybridParameter{Parameter: manifest.Parameter{ Name: strconv.Itoa(i), Type: targetSCParamTypes[i], - } + }} defaultValue := stackitem.NewBigInteger(big.NewInt(int64(i))) var ( val stackitem.Item @@ -601,7 +564,7 @@ func TestForcedNotifyArgumentsConversion(t *testing.T) { } ctr := neotest.CompileSource(t, e.CommitteeHash, strings.NewReader(src), &compiler.Options{ Name: "Helper", - ContractEvents: []manifest.Event{ + ContractEvents: []compiler.HybridEvent{ { Name: methodWithoutEllipsis, Parameters: scParams, diff --git a/pkg/compiler/native_test.go b/pkg/compiler/native_test.go index 06db9f919..8f3b8487b 100644 --- a/pkg/compiler/native_test.go +++ b/pkg/compiler/native_test.go @@ -218,6 +218,12 @@ func TestNativeHelpersCompile(t *testing.T) { {"ripemd160", []string{"[]byte{1, 2, 3}"}}, {"murmur32", []string{"[]byte{1, 2, 3}", "123"}}, {"verifyWithECDsa", []string{"[]byte{1, 2, 3}", pub, sig, "crypto.Secp256k1"}}, + {"bls12381Serialize", []string{"crypto.Bls12381Point{}"}}, + {"bls12381Deserialize", []string{"[]byte{1, 2, 3}"}}, + {"bls12381Equal", []string{"crypto.Bls12381Point{}", "crypto.Bls12381Point{}"}}, + {"bls12381Add", []string{"crypto.Bls12381Point{}", "crypto.Bls12381Point{}"}}, + {"bls12381Mul", []string{"crypto.Bls12381Point{}", "[]byte{1, 2, 3}", "true"}}, + {"bls12381Pairing", []string{"crypto.Bls12381Point{}", "crypto.Bls12381Point{}"}}, }) runNativeTestCases(t, cs.Std.ContractMD, "std", []nativeTestCase{ {"serialize", []string{"[]byte{1, 2, 3}"}}, @@ -295,7 +301,7 @@ func addNativeTestCase(t *testing.T, srcBuilder *bytes.Buffer, ctr interop.Contr isVoid := md.MD.ReturnType == smartcontract.VoidType srcBuilder.WriteString("func F" + strconv.Itoa(i) + "() ") if !isVoid { - srcBuilder.WriteString("interface{} { return ") + srcBuilder.WriteString("any { return ") } else { srcBuilder.WriteString("{ ") } diff --git a/pkg/compiler/nilcheck_test.go b/pkg/compiler/nilcheck_test.go index 54f36e037..415a8f294 100644 --- a/pkg/compiler/nilcheck_test.go +++ b/pkg/compiler/nilcheck_test.go @@ -11,7 +11,7 @@ var nilTestCases = []testCase{ ` package foo func Main() int { - var t interface{} + var t any if t == nil { return 1 } @@ -39,7 +39,7 @@ var nilTestCases = []testCase{ ` package foo func Main() int { - var t interface{} + var t any if nil == t { return 1 } diff --git a/pkg/compiler/return_test.go b/pkg/compiler/return_test.go index ead69d2fb..437f7cd9b 100644 --- a/pkg/compiler/return_test.go +++ b/pkg/compiler/return_test.go @@ -124,7 +124,7 @@ func TestTypeAssertReturn(t *testing.T) { src := ` package main - func foo() interface{} { + func foo() any { return 5 } diff --git a/pkg/compiler/syscall_test.go b/pkg/compiler/syscall_test.go index ed0e655d6..466271343 100644 --- a/pkg/compiler/syscall_test.go +++ b/pkg/compiler/syscall_test.go @@ -44,6 +44,7 @@ func TestFindFlags(t *testing.T) { require.EqualValues(t, storage.DeserializeValues, istorage.FindDeserialize) require.EqualValues(t, storage.PickField0, istorage.FindPick0) require.EqualValues(t, storage.PickField1, istorage.FindPick1) + require.EqualValues(t, storage.Backwards, istorage.FindBackwards) } type syscallTestCase struct { @@ -121,7 +122,7 @@ func TestSyscallExecution(t *testing.T) { if tc.isVoid { tmpl = "func %s() { %s(%s) }\n" } else { - tmpl = "func %s() interface{} { return %s(%s) }\n" + tmpl = "func %s() any { return %s(%s) }\n" } srcBuilder.WriteString(fmt.Sprintf(tmpl, realName, goName, strings.Join(tc.params, ", "))) } diff --git a/pkg/compiler/testdata/util/equals.go b/pkg/compiler/testdata/util/equals.go index 391fb74db..33f4b2e7a 100644 --- a/pkg/compiler/testdata/util/equals.go +++ b/pkg/compiler/testdata/util/equals.go @@ -1,6 +1,6 @@ package util // Equals is a mirror of `Equals` builtin. -func Equals(a, b interface{}) bool { +func Equals(a, b any) bool { return true } diff --git a/pkg/compiler/vardecl_test.go b/pkg/compiler/vardecl_test.go index 3759582b3..954b7a5ed 100644 --- a/pkg/compiler/vardecl_test.go +++ b/pkg/compiler/vardecl_test.go @@ -59,18 +59,18 @@ func TestUnderscoreLocalVarDontEmitCode(t *testing.T) { func (fo Foo) GetInt() int { return fo.Int }` - eval(t, src, big.NewInt(9), []interface{}{opcode.INITSLOT, []byte{5, 0}}, // local slot for A, B, C, D, fo + eval(t, src, big.NewInt(9), []any{opcode.INITSLOT, []byte{5, 0}}, // local slot for A, B, C, D, fo opcode.PUSH2, opcode.STLOC0, // store A opcode.PUSH5, opcode.STLOC1, // store B - opcode.LDLOC0, opcode.LDLOC1, opcode.SWAP, []interface{}{opcode.CALL, []byte{27}}, // evaluate f() first time + opcode.LDLOC0, opcode.LDLOC1, opcode.SWAP, []any{opcode.CALL, []byte{27}}, // evaluate f() first time opcode.DROP, opcode.DROP, opcode.DROP, // drop all values from f - opcode.LDLOC0, opcode.LDLOC1, opcode.SWAP, []interface{}{opcode.CALL, []byte{19}}, // evaluate f() second time + opcode.LDLOC0, opcode.LDLOC1, opcode.SWAP, []any{opcode.CALL, []byte{19}}, // evaluate f() second time opcode.DROP, opcode.STLOC2, opcode.DROP, // store C opcode.PUSH7, opcode.STLOC3, // store D opcode.LDLOC3, opcode.DROP, // empty assignment opcode.PUSH3, opcode.PUSH1, opcode.PACKSTRUCT, opcode.STLOC4, // fo decl - opcode.LDLOC4, []interface{}{opcode.CALL, []byte{12}}, opcode.DROP, // fo.GetInt() + opcode.LDLOC4, []any{opcode.CALL, []byte{12}}, opcode.DROP, // fo.GetInt() opcode.LDLOC2, opcode.RET, // return C - []interface{}{opcode.INITSLOT, []byte{0, 2}}, opcode.PUSH10, opcode.PUSH9, opcode.PUSH8, opcode.RET, // f - []interface{}{opcode.INITSLOT, []byte{0, 1}}, opcode.LDARG0, opcode.PUSH0, opcode.PICKITEM, opcode.RET) // (fo Foo) GetInt() int + []any{opcode.INITSLOT, []byte{0, 2}}, opcode.PUSH10, opcode.PUSH9, opcode.PUSH8, opcode.RET, // f + []any{opcode.INITSLOT, []byte{0, 1}}, opcode.LDARG0, opcode.PUSH0, opcode.PICKITEM, opcode.RET) // (fo Foo) GetInt() int } diff --git a/pkg/compiler/vm_test.go b/pkg/compiler/vm_test.go index 58a4cda57..4ec7196a5 100644 --- a/pkg/compiler/vm_test.go +++ b/pkg/compiler/vm_test.go @@ -23,7 +23,7 @@ import ( type testCase struct { name string src string - result interface{} + result any } // testMainIdent is a method invoked in tests by default. @@ -35,7 +35,7 @@ func runTestCases(t *testing.T, tcases []testCase) { } } -func eval(t *testing.T, src string, result interface{}, expectedOps ...interface{}) []byte { +func eval(t *testing.T, src string, result any, expectedOps ...any) []byte { vm, _, script := vmAndCompileInterop(t, src) if len(expectedOps) != 0 { expected := io.NewBufBinWriter() @@ -43,7 +43,7 @@ func eval(t *testing.T, src string, result interface{}, expectedOps ...interface switch typ := op.(type) { case opcode.Opcode: emit.Opcodes(expected.BinWriter, typ) - case []interface{}: + case []any: emit.Instruction(expected.BinWriter, typ[0].(opcode.Opcode), typ[1].([]byte)) default: t.Fatalf("unexpected evaluation operation: %v", typ) @@ -64,14 +64,14 @@ func evalWithError(t *testing.T, src string, e string) []byte { return prog } -func runAndCheck(t *testing.T, v *vm.VM, result interface{}) { +func runAndCheck(t *testing.T, v *vm.VM, result any) { err := v.Run() require.NoError(t, err) assert.Equal(t, 1, v.Estack().Len(), "stack contains unexpected items") assertResult(t, v, result) } -func evalWithArgs(t *testing.T, src string, op []byte, args []stackitem.Item, result interface{}) { +func evalWithArgs(t *testing.T, src string, op []byte, args []stackitem.Item, result any) { vm := vmAndCompile(t, src) if len(args) > 0 { vm.Estack().PushVal(args) @@ -82,7 +82,7 @@ func evalWithArgs(t *testing.T, src string, op []byte, args []stackitem.Item, re runAndCheck(t, vm, result) } -func assertResult(t *testing.T, vm *vm.VM, result interface{}) { +func assertResult(t *testing.T, vm *vm.VM, result any) { assert.Equal(t, result, vm.PopResult()) assert.Nil(t, vm.Context()) } diff --git a/pkg/consensus/consensus.go b/pkg/consensus/consensus.go index 915b51629..9a29f8c2d 100644 --- a/pkg/consensus/consensus.go +++ b/pkg/consensus/consensus.go @@ -238,7 +238,7 @@ func NewPayload(m netmode.Magic, stateRootEnabled bool) *Payload { } } -func (s *service) newPayload(c *dbft.Context, t payload.MessageType, msg interface{}) payload.ConsensusPayload { +func (s *service) newPayload(c *dbft.Context, t payload.MessageType, msg any) payload.ConsensusPayload { cp := NewPayload(s.ProtocolConfiguration.Magic, s.ProtocolConfiguration.StateRootInHeader) cp.SetHeight(c.BlockIndex) cp.SetValidatorIndex(uint16(c.MyIndex)) @@ -276,7 +276,7 @@ func (s *service) Name() string { } func (s *service) Start() { - if s.started.CAS(false, true) { + if s.started.CompareAndSwap(false, true) { s.log.Info("starting consensus service") b, _ := s.Chain.GetBlock(s.Chain.CurrentBlockHash()) // Can't fail, we have some current block! s.lastTimestamp = b.Timestamp @@ -288,7 +288,7 @@ func (s *service) Start() { // Shutdown implements the Service interface. func (s *service) Shutdown() { - if s.started.CAS(true, false) { + if s.started.CompareAndSwap(true, false) { s.log.Info("stopping consensus service") close(s.quit) <-s.finished @@ -506,7 +506,7 @@ func (s *service) verifyBlock(b block.Block) bool { } var fee int64 - var pool = mempool.New(len(coreb.Transactions), 0, false) + var pool = mempool.New(len(coreb.Transactions), 0, false, nil) var mainPool = s.Chain.GetMemPool() for _, tx := range coreb.Transactions { var err error diff --git a/pkg/consensus/consensus_test.go b/pkg/consensus/consensus_test.go index 469e14767..07eeb99ed 100644 --- a/pkg/consensus/consensus_test.go +++ b/pkg/consensus/consensus_test.go @@ -1,7 +1,6 @@ package consensus import ( - "errors" "testing" "time" @@ -343,7 +342,7 @@ func TestService_PrepareRequest(t *testing.T) { require.NoError(t, err) return } - require.True(t, errors.Is(err, expectedErr), "got: %v", err) + require.ErrorIs(t, err, expectedErr) } checkRequest(t, errInvalidVersion, &prepareRequest{version: 0xFF, prevHash: prevHash}) diff --git a/pkg/consensus/payload.go b/pkg/consensus/payload.go index d9290c49f..aa100cf8c 100644 --- a/pkg/consensus/payload.go +++ b/pkg/consensus/payload.go @@ -65,12 +65,12 @@ func (p *Payload) SetType(t payload.MessageType) { } // Payload implements the payload.ConsensusPayload interface. -func (p Payload) Payload() interface{} { +func (p Payload) Payload() any { return p.payload } // SetPayload implements the payload.ConsensusPayload interface. -func (p *Payload) SetPayload(pl interface{}) { +func (p *Payload) SetPayload(pl any) { p.payload = pl.(io.Serializable) } diff --git a/pkg/core/block/block_test.go b/pkg/core/block/block_test.go index 0123e4819..f0e708f22 100644 --- a/pkg/core/block/block_test.go +++ b/pkg/core/block/block_test.go @@ -4,7 +4,6 @@ import ( "encoding/base64" "encoding/hex" "encoding/json" - "errors" "strings" "testing" @@ -19,7 +18,7 @@ import ( "github.com/stretchr/testify/require" ) -func trim0x(value interface{}) string { +func trim0x(value any) string { s := value.(string) return strings.TrimPrefix(s, "0x") } @@ -42,15 +41,15 @@ func TestDecodeBlock1(t *testing.T) { assert.Equal(t, trim0x(data["merkleroot"]), block.MerkleRoot.StringLE()) assert.Equal(t, trim0x(data["nextconsensus"]), address.Uint160ToString(block.NextConsensus)) - scripts := data["witnesses"].([]interface{}) - script := scripts[0].(map[string]interface{}) + scripts := data["witnesses"].([]any) + script := scripts[0].(map[string]any) assert.Equal(t, script["invocation"].(string), base64.StdEncoding.EncodeToString(block.Script.InvocationScript)) assert.Equal(t, script["verification"].(string), base64.StdEncoding.EncodeToString(block.Script.VerificationScript)) - tx := data["tx"].([]interface{}) - tx0 := tx[0].(map[string]interface{}) + tx := data["tx"].([]any) + tx0 := tx[0].(map[string]any) assert.Equal(t, len(tx), len(block.Transactions)) - assert.Equal(t, len(tx0["attributes"].([]interface{})), len(block.Transactions[0].Attributes)) + assert.Equal(t, len(tx0["attributes"].([]any)), len(block.Transactions[0].Attributes)) } func TestTrimmedBlock(t *testing.T) { @@ -236,7 +235,7 @@ func TestBlockEncodeDecode(t *testing.T) { data, err := testserdes.EncodeBinary(b) require.NoError(t, err) - require.True(t, errors.Is(testserdes.DecodeBinary(data, new(Block)), ErrMaxContentsPerBlock)) + require.ErrorIs(t, testserdes.DecodeBinary(data, new(Block)), ErrMaxContentsPerBlock) }) } diff --git a/pkg/core/block/helper_test.go b/pkg/core/block/helper_test.go index 85be4903b..db61d299f 100644 --- a/pkg/core/block/helper_test.go +++ b/pkg/core/block/helper_test.go @@ -24,12 +24,12 @@ func getDecodedBlock(t *testing.T, i int) *Block { return block } -func getBlockData(i int) (map[string]interface{}, error) { +func getBlockData(i int) (map[string]any, error) { b, err := os.ReadFile(fmt.Sprintf("../test_data/block_%d.json", i)) if err != nil { return nil, err } - var data map[string]interface{} + var data map[string]any if err := json.Unmarshal(b, &data); err != nil { return nil, err } diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 51a5f1d2f..b5ca036dd 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -45,7 +45,7 @@ import ( // Tuning parameters. const ( - version = "0.2.8" + version = "0.2.9" defaultInitialGAS = 52000000_00000000 defaultGCPeriod = 10000 @@ -183,8 +183,8 @@ type Blockchain struct { // Notification subsystem. events chan bcEvent - subCh chan interface{} - unsubCh chan interface{} + subCh chan any + unsubCh chan any } // StateRoot represents local state root module. @@ -309,11 +309,11 @@ func NewBlockchain(s storage.Store, cfg config.Blockchain, log *zap.Logger) (*Bl store: s, stopCh: make(chan struct{}), runToExitCh: make(chan struct{}), - memPool: mempool.New(cfg.MemPoolSize, 0, false), + memPool: mempool.New(cfg.MemPoolSize, 0, false, updateMempoolMetrics), log: log, events: make(chan bcEvent), - subCh: make(chan interface{}), - unsubCh: make(chan interface{}), + subCh: make(chan any), + unsubCh: make(chan any), contracts: *native.NewContracts(cfg.ProtocolConfiguration), } @@ -686,164 +686,279 @@ func (bc *Blockchain) resetStateInternal(height uint32, stage stateChangeStage) return fmt.Errorf("failed to retrieve stateroot for height %d: %w", height, err) } v := bc.dao.Version - cache := bc.dao // dao is MemCachedStore over DB, so use dao directly to persist cached changes right to the underlying DB + // dao is MemCachedStore over DB, we use dao directly to persist cached changes + // right to the underlying DB. + cache := bc.dao + // upperCache is a private MemCachedStore over cache. During each of the state + // sync stages we put the data inside the upperCache; in the end of each stage + // we persist changes from upperCache to cache. Changes from cache are persisted + // directly to the underlying persistent storage (boltDB, levelDB, etc.). + // upperCache/cache segregation is needed to keep the DB state clean and to + // persist data from different stages separately. + upperCache := cache.GetPrivate() - bc.log.Info("initialize state reset", zap.Uint32("target height", height)) + bc.log.Info("initializing state reset", zap.Uint32("target height", height)) start := time.Now() p := start - keys := 0 + + // Start batch persisting routine, it will be used for blocks/txs/AERs/storage items batches persist. + type postPersist func(persistedKeys int, err error) error + var ( + persistCh = make(chan postPersist) + persistToExitCh = make(chan struct{}) + ) + go func() { + for { + f, ok := <-persistCh + if !ok { + break + } + persistErr := f(cache.Persist()) + if persistErr != nil { + bc.log.Fatal("persist failed", zap.Error(persistErr)) + panic(persistErr) + } + } + close(persistToExitCh) + }() + defer func() { + close(persistCh) + <-persistToExitCh + bc.log.Info("reset finished successfully", zap.Duration("took", time.Since(start))) + }() resetStageKey := []byte{byte(storage.SYSStateChangeStage)} switch stage { case none: - cache.Store.Put(resetStageKey, []byte{stateResetBit | byte(stateJumpStarted)}) - _, err = cache.Persist() - if err != nil { - return fmt.Errorf("failed to persist state reset start marker to the DB: %w", err) + upperCache.Store.Put(resetStageKey, []byte{stateResetBit | byte(stateJumpStarted)}) + // Technically, there's no difference between Persist() and PersistSync() for the private + // MemCached storage, but we'd better use the sync version in case of some further code changes. + _, uerr := upperCache.PersistSync() + if uerr != nil { + panic(uerr) + } + upperCache = cache.GetPrivate() + persistCh <- func(persistedKeys int, err error) error { + if err != nil { + return fmt.Errorf("failed to persist state reset start marker to the DB: %w", err) + } + return nil } fallthrough case stateJumpStarted: - bc.log.Info("trying to reset blocks, transactions and AERs") + bc.log.Debug("trying to reset blocks, transactions and AERs") // Remove blocks/transactions/aers from currHeight down to height (not including height itself). // Keep headers for now, they'll be removed later. It's hard to handle the whole set of changes in // one stage, so persist periodically. const persistBatchSize = 100 * headerBatchCount // count blocks only, should be enough to avoid OOM killer even for large blocks var ( - pBlocksStart = p - blocksCnt, batchCnt, keysCnt int + pBlocksStart = p + blocksCnt, batchCnt int + keysCnt = new(int) ) for i := height + 1; i <= currHeight; i++ { - err := cache.DeleteBlock(bc.GetHeaderHash(i)) + err := upperCache.DeleteBlock(bc.GetHeaderHash(i)) if err != nil { return fmt.Errorf("error while removing block %d: %w", i, err) } blocksCnt++ if blocksCnt == persistBatchSize { - keys, err = cache.Persist() - if err != nil { - return fmt.Errorf("failed to persist intermediate batch removed blocks, transactions and AERs: %w", err) - } blocksCnt = 0 batchCnt++ - keysCnt += keys - bc.log.Info("intermediate batch of removed blocks, transactions and AERs is persisted", zap.Int("batches persisted", batchCnt), zap.Duration("took", time.Since(p)), zap.Int("keys", keys)) + bc.log.Info("intermediate batch of removed blocks, transactions and AERs is collected", + zap.Int("batch", batchCnt), + zap.Duration("took", time.Since(p))) + + persistStart := time.Now() + persistBatch := batchCnt + _, uerr := upperCache.PersistSync() + if uerr != nil { + panic(uerr) + } + upperCache = cache.GetPrivate() + persistCh <- func(persistedKeys int, err error) error { + if err != nil { + return fmt.Errorf("failed to persist intermediate batch of removed blocks, transactions and AERs: %w", err) + } + *keysCnt += persistedKeys + bc.log.Debug("intermediate batch of removed blocks, transactions and AERs is persisted", + zap.Int("batch", persistBatch), + zap.Duration("took", time.Since(persistStart)), + zap.Int("keys", persistedKeys)) + return nil + } p = time.Now() } } - cache.Store.Put(resetStageKey, []byte{stateResetBit | byte(staleBlocksRemoved)}) - keys, err = cache.Persist() - if err != nil { - return fmt.Errorf("failed to persist last batch of removed blocks, transactions ans AERs: %w", err) - } + upperCache.Store.Put(resetStageKey, []byte{stateResetBit | byte(staleBlocksRemoved)}) batchCnt++ - keysCnt += keys - bc.log.Info("last batch of removed blocks, transactions and AERs is persisted", zap.Int("batches persisted", batchCnt), zap.Duration("took", time.Since(p)), zap.Int("keys", keys)) + bc.log.Info("last batch of removed blocks, transactions and AERs is collected", + zap.Int("batch", batchCnt), + zap.Duration("took", time.Since(p))) + bc.log.Info("blocks, transactions ans AERs are reset", zap.Duration("took", time.Since(pBlocksStart))) - bc.log.Info("blocks, transactions ans AERs are reset", zap.Duration("took", time.Since(pBlocksStart)), - zap.Int("overall persisted keys", keysCnt)) + persistStart := time.Now() + persistBatch := batchCnt + _, uerr := upperCache.PersistSync() + if uerr != nil { + panic(uerr) + } + upperCache = cache.GetPrivate() + persistCh <- func(persistedKeys int, err error) error { + if err != nil { + return fmt.Errorf("failed to persist last batch of removed blocks, transactions ans AERs: %w", err) + } + *keysCnt += persistedKeys + bc.log.Debug("last batch of removed blocks, transactions and AERs is persisted", + zap.Int("batch", persistBatch), + zap.Duration("took", time.Since(persistStart)), + zap.Int("keys", persistedKeys)) + return nil + } p = time.Now() fallthrough case staleBlocksRemoved: - bc.log.Info("trying to reset contract storage items") + // Completely remove contract IDs to update them later. + bc.log.Debug("trying to reset contract storage items") pStorageStart := p + p = time.Now() var mode = mpt.ModeAll if bc.config.Ledger.RemoveUntraceableBlocks { mode |= mpt.ModeGCFlag } - trieStore := mpt.NewTrieStore(sr.Root, mode, cache.Store) + trieStore := mpt.NewTrieStore(sr.Root, mode, upperCache.Store) oldStoragePrefix := v.StoragePrefix newStoragePrefix := statesync.TemporaryPrefix(oldStoragePrefix) const persistBatchSize = 200000 - var ( - seekErr error - cnt int - storageItmsCnt int - batchCnt int - ) + var cnt, storageItmsCnt, batchCnt int trieStore.Seek(storage.SeekRange{Prefix: []byte{byte(oldStoragePrefix)}}, func(k, v []byte) bool { - if seekErr != nil { - return false - } if cnt >= persistBatchSize { cnt = 0 - keys, seekErr = cache.Persist() - if seekErr != nil { - seekErr = fmt.Errorf("failed to persist intermediate batch of contract storage items and IDs: %w", seekErr) - return false - } batchCnt++ - bc.log.Info("intermediate batch of contract storage items and IDs is persisted", zap.Int("batch", batchCnt), zap.Duration("took", time.Since(p)), zap.Int("keys", keys)) + bc.log.Info("intermediate batch of contract storage items and IDs is collected", + zap.Int("batch", batchCnt), + zap.Duration("took", time.Since(p))) + + persistStart := time.Now() + persistBatch := batchCnt + _, uerr := upperCache.PersistSync() + if uerr != nil { + panic(uerr) + } + upperCache = cache.GetPrivate() + persistCh <- func(persistedKeys int, err error) error { + if err != nil { + return fmt.Errorf("failed to persist intermediate batch of contract storage items: %w", err) + } + bc.log.Debug("intermediate batch of contract storage items is persisted", + zap.Int("batch", persistBatch), + zap.Duration("took", time.Since(persistStart)), + zap.Int("keys", persistedKeys)) + return nil + } p = time.Now() } // May safely omit KV copying. k[0] = byte(newStoragePrefix) - cache.Store.Put(k, v) + upperCache.Store.Put(k, v) cnt++ storageItmsCnt++ return true }) - if seekErr != nil { - return fmt.Errorf("failed to reset contract contract storage items and IDs: %w", seekErr) - } trieStore.Close() - cache.Store.Put(resetStageKey, []byte{stateResetBit | byte(newStorageItemsAdded)}) - keys, err = cache.Persist() - if err != nil { - return fmt.Errorf("failed to persist contract storage items and IDs changes to the DB: %w", err) - } + upperCache.Store.Put(resetStageKey, []byte{stateResetBit | byte(newStorageItemsAdded)}) batchCnt++ - bc.log.Info("last batch of contract storage items and IDs is persisted", zap.Int("batch", batchCnt), zap.Duration("took", time.Since(p)), zap.Int("keys", keys)) - bc.log.Info("contract storage items and IDs are reset", zap.Duration("took", time.Since(pStorageStart)), + persistBatch := batchCnt + bc.log.Info("last batch of contract storage items is collected", zap.Int("batch", batchCnt), zap.Duration("took", time.Since(p))) + bc.log.Info("contract storage items are reset", zap.Duration("took", time.Since(pStorageStart)), zap.Int("keys", storageItmsCnt)) + + lastStart := time.Now() + _, uerr := upperCache.PersistSync() + if uerr != nil { + panic(uerr) + } + upperCache = cache.GetPrivate() + persistCh <- func(persistedKeys int, err error) error { + if err != nil { + return fmt.Errorf("failed to persist contract storage items and IDs changes to the DB: %w", err) + } + bc.log.Debug("last batch of contract storage items and IDs is persisted", zap.Int("batch", persistBatch), zap.Duration("took", time.Since(lastStart)), zap.Int("keys", persistedKeys)) + return nil + } p = time.Now() fallthrough case newStorageItemsAdded: // Reset SYS-prefixed and IX-prefixed information. - bc.log.Info("trying to reset headers information") + bc.log.Debug("trying to reset headers information") for i := height + 1; i <= hHeight; i++ { - cache.PurgeHeader(bc.GetHeaderHash(i)) + upperCache.PurgeHeader(bc.GetHeaderHash(i)) } - cache.DeleteHeaderHashes(height+1, headerBatchCount) - cache.StoreAsCurrentBlock(b) - cache.PutCurrentHeader(b.Hash(), height) + upperCache.DeleteHeaderHashes(height+1, headerBatchCount) + upperCache.StoreAsCurrentBlock(b) + upperCache.PutCurrentHeader(b.Hash(), height) v.StoragePrefix = statesync.TemporaryPrefix(v.StoragePrefix) - cache.PutVersion(v) + upperCache.PutVersion(v) + // It's important to manually change the cache's Version at this stage, so that native cache + // can be properly initialized (with the correct contract storage data prefix) at the final + // stage of the state reset. At the same time, DB's SYSVersion-prefixed data will be persisted + // from upperCache to cache in a standard way (several lines below). + cache.Version = v bc.persistent.Version = v - cache.Store.Put(resetStageKey, []byte{stateResetBit | byte(headersReset)}) - keys, err = cache.Persist() - if err != nil { - return fmt.Errorf("failed to persist headers changes to the DB: %w", err) - } + upperCache.Store.Put(resetStageKey, []byte{stateResetBit | byte(headersReset)}) + bc.log.Info("headers information is reset", zap.Duration("took", time.Since(p))) - bc.log.Info("headers information is reset", zap.Duration("took", time.Since(p)), zap.Int("keys", keys)) + persistStart := time.Now() + _, uerr := upperCache.PersistSync() + if uerr != nil { + panic(uerr) + } + upperCache = cache.GetPrivate() + persistCh <- func(persistedKeys int, err error) error { + if err != nil { + return fmt.Errorf("failed to persist headers changes to the DB: %w", err) + } + bc.log.Debug("headers information is persisted", zap.Duration("took", time.Since(persistStart)), zap.Int("keys", persistedKeys)) + return nil + } p = time.Now() fallthrough case headersReset: // Reset MPT. - bc.log.Info("trying to reset state root information and NEP transfers") - err = bc.stateRoot.ResetState(height, cache.Store) + bc.log.Debug("trying to reset state root information and NEP transfers") + err = bc.stateRoot.ResetState(height, upperCache.Store) if err != nil { return fmt.Errorf("failed to rollback MPT state: %w", err) } // Reset transfers. - err = bc.resetTransfers(cache, height) + err = bc.resetTransfers(upperCache, height) if err != nil { return fmt.Errorf("failed to strip transfer log / transfer info: %w", err) } - cache.Store.Put(resetStageKey, []byte{stateResetBit | byte(transfersReset)}) - keys, err = cache.Persist() - if err != nil { - return fmt.Errorf("failed tpo persist contract storage items changes to the DB: %w", err) - } + upperCache.Store.Put(resetStageKey, []byte{stateResetBit | byte(transfersReset)}) + bc.log.Info("state root information and NEP transfers are reset", zap.Duration("took", time.Since(p))) - bc.log.Info("state root information and NEP transfers are reset", zap.Duration("took", time.Since(p)), zap.Int("keys", keys)) + persistStart := time.Now() + _, uerr := upperCache.PersistSync() + if uerr != nil { + panic(uerr) + } + upperCache = cache.GetPrivate() + persistCh <- func(persistedKeys int, err error) error { + if err != nil { + return fmt.Errorf("failed to persist contract storage items changes to the DB: %w", err) + } + + bc.log.Debug("state root information and NEP transfers are persisted", zap.Duration("took", time.Since(persistStart)), zap.Int("keys", persistedKeys)) + return nil + } p = time.Now() fallthrough case transfersReset: @@ -854,8 +969,8 @@ func (bc *Blockchain) resetStateInternal(height uint32, stage stateChangeStage) } // Direct (cache-less) DB operation: remove stale storage items. - bc.log.Info("trying to remove stale storage items") - keys = 0 + bc.log.Debug("trying to remove stale storage items") + keys := 0 err = bc.store.SeekGC(storage.SeekRange{ Prefix: []byte{byte(statesync.TemporaryPrefix(v.StoragePrefix))}, }, func(_, _ []byte) bool { @@ -868,22 +983,30 @@ func (bc *Blockchain) resetStateInternal(height uint32, stage stateChangeStage) bc.log.Info("stale storage items are reset", zap.Duration("took", time.Since(p)), zap.Int("keys", keys)) p = time.Now() - bc.log.Info("trying to remove state reset point") - cache.Store.Delete(resetStageKey) + bc.log.Debug("trying to remove state reset point") + upperCache.Store.Delete(resetStageKey) // Unlike the state jump, state sync point must be removed as we have complete state for this height. - cache.Store.Delete([]byte{byte(storage.SYSStateSyncPoint)}) - keys, err = cache.Persist() - if err != nil { - return fmt.Errorf("failed to persist state reset stage to DAO: %w", err) + upperCache.Store.Delete([]byte{byte(storage.SYSStateSyncPoint)}) + bc.log.Info("state reset point is removed", zap.Duration("took", time.Since(p))) + + persistStart := time.Now() + _, uerr := upperCache.PersistSync() + if uerr != nil { + panic(uerr) } - bc.log.Info("stale reset point is removed", zap.Duration("took", time.Since(p)), zap.Int("keys", keys)) + persistCh <- func(persistedKeys int, err error) error { + if err != nil { + return fmt.Errorf("failed to persist state reset stage to DAO: %w", err) + } + bc.log.Info("state reset point information is persisted", zap.Duration("took", time.Since(persistStart)), zap.Int("keys", persistedKeys)) + return nil + } + p = time.Now() err = bc.resetRAMState(height, true) if err != nil { return fmt.Errorf("failed to update in-memory blockchain data: %w", err) } - - bc.log.Info("reset finished successfully", zap.Duration("took", time.Since(start))) return nil } @@ -1317,7 +1440,7 @@ func (bc *Blockchain) AddBlock(block *block.Block) error { if !block.MerkleRoot.Equals(merkle) { return errors.New("invalid block: MerkleRoot mismatch") } - mp = mempool.New(len(block.Transactions), 0, false) + mp = mempool.New(len(block.Transactions), 0, false, nil) for _, tx := range block.Transactions { var err error // Transactions are verified before adding them @@ -2051,15 +2174,6 @@ func (bc *Blockchain) GetHeader(hash util.Uint256) (*block.Header, error) { return &block.Header, nil } -// HasTransaction returns true if the blockchain contains he given -// transaction hash. -func (bc *Blockchain) HasTransaction(hash util.Uint256) bool { - if bc.memPool.ContainsKey(hash) { - return true - } - return errors.Is(bc.dao.HasTransaction(hash), dao.ErrAlreadyExists) -} - // HasBlock returns true if the blockchain contains the given // block hash. func (bc *Blockchain) HasBlock(hash util.Uint256) bool { @@ -2099,14 +2213,6 @@ func (bc *Blockchain) GetContractState(hash util.Uint160) *state.Contract { // GetContractScriptHash returns contract script hash by its ID. func (bc *Blockchain) GetContractScriptHash(id int32) (util.Uint160, error) { - if id < 0 { - for _, n := range bc.contracts.Contracts { - nc := n.Metadata().NativeContract - if nc.ID == id { - return nc.Hash, nil - } - } - } return native.GetContractScriptHash(bc.dao, id) } @@ -2234,7 +2340,12 @@ unsubloop: // CalculateClaimable calculates the amount of GAS generated by owning specified // amount of NEO between specified blocks. func (bc *Blockchain) CalculateClaimable(acc util.Uint160, endHeight uint32) (*big.Int, error) { - return bc.contracts.NEO.CalculateBonus(bc.dao, acc, endHeight) + nextBlock, err := bc.getFakeNextBlock(bc.BlockHeight() + 1) + if err != nil { + return nil, err + } + ic := bc.newInteropContext(trigger.Application, bc.dao, nextBlock, nil) + return bc.contracts.NEO.CalculateBonus(ic, acc, endHeight) } // FeePerByte returns transaction network fee per byte. @@ -2328,7 +2439,7 @@ var ( // verifyAndPoolTx verifies whether a transaction is bonafide or not and tries // to add it to the mempool given. -func (bc *Blockchain) verifyAndPoolTx(t *transaction.Transaction, pool *mempool.Pool, feer mempool.Feer, data ...interface{}) error { +func (bc *Blockchain) verifyAndPoolTx(t *transaction.Transaction, pool *mempool.Pool, feer mempool.Feer, data ...any) error { // This code can technically be moved out of here, because it doesn't // really require a chain lock. err := vm.IsScriptCorrect(t.Script, nil) @@ -2366,7 +2477,7 @@ func (bc *Blockchain) verifyAndPoolTx(t *transaction.Transaction, pool *mempool. return fmt.Errorf("%w: net fee is %v, need %v", ErrTxSmallNetworkFee, t.NetworkFee, needNetworkFee) } // check that current tx wasn't included in the conflicts attributes of some other transaction which is already in the chain - if err := bc.dao.HasTransaction(t.Hash()); err != nil { + if err := bc.dao.HasTransaction(t.Hash(), t.Signers); err != nil { switch { case errors.Is(err, dao.ErrAlreadyExists): return fmt.Errorf("blockchain: %w", ErrAlreadyExists) @@ -2449,7 +2560,7 @@ func (bc *Blockchain) verifyTxAttributes(d *dao.Simple, tx *transaction.Transact if isPartialTx { maxNVBDelta, err := bc.GetMaxNotValidBeforeDelta() if err != nil { - return fmt.Errorf("%w: failed to retrieve MaxNotValidBeforeDelta value from native Notary contract: %v", ErrInvalidAttribute, err) + return fmt.Errorf("%w: failed to retrieve MaxNotValidBeforeDelta value from native Notary contract: %v", ErrInvalidAttribute, err) //nolint:errorlint // errorlint: non-wrapping format verb for fmt.Errorf. Use `%w` to format errors } if curHeight+maxNVBDelta < nvb { return fmt.Errorf("%w: NotValidBefore (%d) bigger than MaxNVBDelta (%d) allows at height %d", ErrInvalidAttribute, nvb, maxNVBDelta, curHeight) @@ -2467,7 +2578,9 @@ func (bc *Blockchain) verifyTxAttributes(d *dao.Simple, tx *transaction.Transact return fmt.Errorf("%w: Conflicts attribute was found, but P2PSigExtensions are disabled", ErrInvalidAttribute) } conflicts := tx.Attributes[i].Value.(*transaction.Conflicts) - if err := bc.dao.HasTransaction(conflicts.Hash); errors.Is(err, dao.ErrAlreadyExists) { + // Only fully-qualified dao.ErrAlreadyExists error bothers us here, thus, we + // can safely omit the payer argument to HasTransaction call to improve performance a bit. + if err := bc.dao.HasTransaction(conflicts.Hash, nil); errors.Is(err, dao.ErrAlreadyExists) { return fmt.Errorf("%w: conflicting transaction %s is already on chain", ErrInvalidAttribute, conflicts.Hash.StringLE()) } case transaction.NotaryAssistedT: @@ -2500,7 +2613,7 @@ func (bc *Blockchain) IsTxStillRelevant(t *transaction.Transaction, txpool *memp return false } if txpool == nil { - if bc.dao.HasTransaction(t.Hash()) != nil { + if bc.dao.HasTransaction(t.Hash(), t.Signers) != nil { return false } } else if txpool.HasConflicts(t, bc) { @@ -2525,7 +2638,7 @@ func (bc *Blockchain) IsTxStillRelevant(t *transaction.Transaction, txpool *memp // current blockchain state. Note that this verification is completely isolated // from the main node's mempool. func (bc *Blockchain) VerifyTx(t *transaction.Transaction) error { - var mp = mempool.New(1, 0, false) + var mp = mempool.New(1, 0, false, nil) bc.lock.RLock() defer bc.lock.RUnlock() return bc.verifyAndPoolTx(t, mp, bc) @@ -2549,7 +2662,7 @@ func (bc *Blockchain) PoolTx(t *transaction.Transaction, pools ...*mempool.Pool) } // PoolTxWithData verifies and tries to add given transaction with additional data into the mempool. -func (bc *Blockchain) PoolTxWithData(t *transaction.Transaction, data interface{}, mp *mempool.Pool, feer mempool.Feer, verificationFunction func(tx *transaction.Transaction, data interface{}) error) error { +func (bc *Blockchain) PoolTxWithData(t *transaction.Transaction, data any, mp *mempool.Pool, feer mempool.Feer, verificationFunction func(tx *transaction.Transaction, data any) error) error { bc.lock.RLock() defer bc.lock.RUnlock() diff --git a/pkg/core/blockchain_core_test.go b/pkg/core/blockchain_core_test.go index 5b1944c52..f9f3e0275 100644 --- a/pkg/core/blockchain_core_test.go +++ b/pkg/core/blockchain_core_test.go @@ -2,7 +2,6 @@ package core import ( "encoding/binary" - "errors" "fmt" "strings" "sync/atomic" @@ -33,16 +32,16 @@ func TestVerifyHeader(t *testing.T) { h := prev.Hash() h[0] = ^h[0] hdr := newBlock(bc.config.ProtocolConfiguration, 1, h).Header - require.True(t, errors.Is(bc.verifyHeader(&hdr, &prev), ErrHdrHashMismatch)) + require.ErrorIs(t, bc.verifyHeader(&hdr, &prev), ErrHdrHashMismatch) }) t.Run("Index", func(t *testing.T) { hdr := newBlock(bc.config.ProtocolConfiguration, 3, prev.Hash()).Header - require.True(t, errors.Is(bc.verifyHeader(&hdr, &prev), ErrHdrIndexMismatch)) + require.ErrorIs(t, bc.verifyHeader(&hdr, &prev), ErrHdrIndexMismatch) }) t.Run("Timestamp", func(t *testing.T) { hdr := newBlock(bc.config.ProtocolConfiguration, 1, prev.Hash()).Header hdr.Timestamp = 0 - require.True(t, errors.Is(bc.verifyHeader(&hdr, &prev), ErrHdrInvalidTimestamp)) + require.ErrorIs(t, bc.verifyHeader(&hdr, &prev), ErrHdrInvalidTimestamp) }) }) t.Run("Valid", func(t *testing.T) { diff --git a/pkg/core/blockchain_neotest_test.go b/pkg/core/blockchain_neotest_test.go index 9180435d0..c00f4b0e9 100644 --- a/pkg/core/blockchain_neotest_test.go +++ b/pkg/core/blockchain_neotest_test.go @@ -427,7 +427,7 @@ func TestBlockchain_AddBlockStateRoot(t *testing.T) { b.PrevStateRoot = util.Uint256{} e.SignBlock(b) err = bc.AddBlock(b) - require.True(t, errors.Is(err, core.ErrHdrStateRootSetting), "got: %v", err) + require.ErrorIs(t, err, core.ErrHdrStateRootSetting) u := sr.Root u[0] ^= 0xFF @@ -435,7 +435,7 @@ func TestBlockchain_AddBlockStateRoot(t *testing.T) { b.PrevStateRoot = u e.SignBlock(b) err = bc.AddBlock(b) - require.True(t, errors.Is(err, core.ErrHdrInvalidStateRoot), "got: %v", err) + require.ErrorIs(t, err, core.ErrHdrInvalidStateRoot) b = e.NewUnsignedBlock(t) e.SignBlock(b) @@ -455,7 +455,7 @@ func TestBlockchain_AddHeadersStateRoot(t *testing.T) { // invalid stateroot h1.PrevStateRoot[0] ^= 0xFF - require.True(t, errors.Is(bc.AddHeaders(&h1), core.ErrHdrInvalidStateRoot)) + require.ErrorIs(t, bc.AddHeaders(&h1), core.ErrHdrInvalidStateRoot) // valid stateroot h1.PrevStateRoot = r @@ -597,12 +597,12 @@ func TestBlockchain_VerifyHashAgainstScript(t *testing.T) { newH[0] = ^newH[0] w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH4)}} _, err := bc.VerifyWitness(newH, nil, w, gas) - require.True(t, errors.Is(err, core.ErrUnknownVerificationContract)) + require.ErrorIs(t, err, core.ErrUnknownVerificationContract) }) t.Run("Invalid", func(t *testing.T) { w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH4)}} _, err := bc.VerifyWitness(csInvalid.Hash, nil, w, gas) - require.True(t, errors.Is(err, core.ErrInvalidVerificationContract)) + require.ErrorIs(t, err, core.ErrInvalidVerificationContract) }) t.Run("ValidSignature", func(t *testing.T) { w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH4)}} @@ -612,7 +612,7 @@ func TestBlockchain_VerifyHashAgainstScript(t *testing.T) { t.Run("InvalidSignature", func(t *testing.T) { w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH3)}} _, err := bc.VerifyWitness(cs.Hash, nil, w, gas) - require.True(t, errors.Is(err, core.ErrVerificationFailed)) + require.ErrorIs(t, err, core.ErrVerificationFailed) }) }) t.Run("NotEnoughGas", func(t *testing.T) { @@ -622,7 +622,7 @@ func TestBlockchain_VerifyHashAgainstScript(t *testing.T) { VerificationScript: verif, } _, err := bc.VerifyWitness(hash.Hash160(verif), nil, w, 1) - require.True(t, errors.Is(err, core.ErrVerificationFailed)) + require.ErrorIs(t, err, core.ErrVerificationFailed) }) t.Run("NoResult", func(t *testing.T) { verif := []byte{byte(opcode.DROP)} @@ -631,7 +631,7 @@ func TestBlockchain_VerifyHashAgainstScript(t *testing.T) { VerificationScript: verif, } _, err := bc.VerifyWitness(hash.Hash160(verif), nil, w, gas) - require.True(t, errors.Is(err, core.ErrVerificationFailed)) + require.ErrorIs(t, err, core.ErrVerificationFailed) }) t.Run("BadResult", func(t *testing.T) { verif := make([]byte, keys.SignatureLen+2) @@ -642,7 +642,7 @@ func TestBlockchain_VerifyHashAgainstScript(t *testing.T) { VerificationScript: verif, } _, err := bc.VerifyWitness(hash.Hash160(verif), nil, w, gas) - require.True(t, errors.Is(err, core.ErrVerificationFailed)) + require.ErrorIs(t, err, core.ErrVerificationFailed) }) t.Run("TooManyResults", func(t *testing.T) { verif := []byte{byte(opcode.NOP)} @@ -651,7 +651,7 @@ func TestBlockchain_VerifyHashAgainstScript(t *testing.T) { VerificationScript: verif, } _, err := bc.VerifyWitness(hash.Hash160(verif), nil, w, gas) - require.True(t, errors.Is(err, core.ErrVerificationFailed)) + require.ErrorIs(t, err, core.ErrVerificationFailed) }) } @@ -713,10 +713,10 @@ func TestBlockchain_IsTxStillRelevant(t *testing.T) { src := fmt.Sprintf(`package verify import ( "github.com/nspcc-dev/neo-go/pkg/interop/contract" - "github.com/nspcc-dev/neo-go/pkg/interop/util" + "github.com/nspcc-dev/neo-go/pkg/interop/lib/address" ) func Verify() bool { - addr := util.FromAddress("`+address.Uint160ToString(e.NativeHash(t, nativenames.Ledger))+`") + addr := address.ToHash160("`+address.Uint160ToString(e.NativeHash(t, nativenames.Ledger))+`") currentHeight := contract.Call(addr, "currentIndex", contract.ReadStates) return currentHeight.(int) < %d }`, bc.BlockHeight()+2) // deploy + next block @@ -802,9 +802,6 @@ func TestBlockchain_GetTransaction(t *testing.T) { func TestBlockchain_GetClaimable(t *testing.T) { bc, acc := chain.NewSingle(t) - e := neotest.NewExecutor(t, bc, acc, acc) - - e.GenerateNewBlocks(t, 10) t.Run("first generation period", func(t *testing.T) { amount, err := bc.CalculateClaimable(acc.ScriptHash(), 1) @@ -1087,7 +1084,7 @@ func TestBlockchain_MPTDeleteNoKey(t *testing.T) { // native contract. func TestConfigNativeUpdateHistory(t *testing.T) { var prefixPath = filepath.Join("..", "..", "config") - check := func(t *testing.T, cfgFileSuffix interface{}) { + check := func(t *testing.T, cfgFileSuffix any) { cfgPath := filepath.Join(prefixPath, fmt.Sprintf("protocol.%s.yml", cfgFileSuffix)) cfg, err := config.LoadFile(cfgPath) require.NoError(t, err, fmt.Errorf("failed to load %s", cfgPath)) @@ -1101,7 +1098,7 @@ func TestConfigNativeUpdateHistory(t *testing.T) { "edit the test if the contract should be disabled", cfgPath, c.Metadata().Name)) } } - testCases := []interface{}{ + testCases := []any{ netmode.MainNet, netmode.PrivNet, netmode.TestNet, @@ -1168,7 +1165,7 @@ func TestBlockchain_VerifyTx(t *testing.T) { checkErr := func(t *testing.T, expectedErr error, tx *transaction.Transaction) { err := bc.VerifyTx(tx) - require.True(t, errors.Is(err, expectedErr), "expected: %v, got: %v", expectedErr, err) + require.ErrorIs(t, err, expectedErr) } testScript := []byte{byte(opcode.PUSH1)} @@ -1324,7 +1321,7 @@ func TestBlockchain_VerifyTx(t *testing.T) { tx2.NetworkFee = balance / 2 require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx2)) err := bc.PoolTx(tx2) - require.True(t, errors.Is(err, core.ErrMemPoolConflict)) + require.ErrorIs(t, err, core.ErrMemPoolConflict) }) t.Run("InvalidWitnessHash", func(t *testing.T) { tx := newTestTx(t, h, testScript) @@ -1361,10 +1358,10 @@ func TestBlockchain_VerifyTx(t *testing.T) { require.NoError(t, bc.PoolTx(tx)) err := bc.PoolTx(tx) - require.True(t, errors.Is(err, core.ErrAlreadyExists)) + require.ErrorIs(t, err, core.ErrAlreadyExists) }) t.Run("MemPoolOOM", func(t *testing.T) { - mp := mempool.New(1, 0, false) + mp := mempool.New(1, 0, false, nil) tx1 := newTestTx(t, h, testScript) tx1.NetworkFee += 10000 // Give it more priority. require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx1)) @@ -1373,7 +1370,7 @@ func TestBlockchain_VerifyTx(t *testing.T) { tx2 := newTestTx(t, h, testScript) require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx2)) err := bc.PoolTx(tx2, mp) - require.True(t, errors.Is(err, core.ErrOOM)) + require.ErrorIs(t, err, core.ErrOOM) }) t.Run("Attribute", func(t *testing.T) { t.Run("InvalidHighPriority", func(t *testing.T) { @@ -1453,7 +1450,7 @@ func TestBlockchain_VerifyTx(t *testing.T) { checkErr(t, core.ErrInvalidAttribute, tx) }) - keys := make([]interface{}, 0, len(oraclePubs)) + keys := make([]any, 0, len(oraclePubs)) for _, p := range oraclePubs { keys = append(keys, p.Bytes()) } @@ -1479,7 +1476,7 @@ func TestBlockchain_VerifyTx(t *testing.T) { emit.String(w.BinWriter, nativenames.Oracle) tx.Scripts[len(tx.Scripts)-1].VerificationScript = w.Bytes() err := bc.VerifyTx(tx) - require.True(t, errors.Is(err, core.ErrNativeContractWitness), "got: %v", err) + require.ErrorIs(t, err, core.ErrNativeContractWitness) }) t.Run("Good", func(t *testing.T) { tx.Scripts[len(tx.Scripts)-1].VerificationScript = nil @@ -1552,7 +1549,7 @@ func TestBlockchain_VerifyTx(t *testing.T) { t.Run("Enabled", func(t *testing.T) { t.Run("NotYetValid", func(t *testing.T) { tx := getNVBTx(e, bc.BlockHeight()+1) - require.True(t, errors.Is(bc.VerifyTx(tx), core.ErrInvalidAttribute)) + require.ErrorIs(t, bc.VerifyTx(tx), core.ErrInvalidAttribute) }) t.Run("positive", func(t *testing.T) { tx := getNVBTx(e, bc.BlockHeight()) @@ -1637,28 +1634,272 @@ func TestBlockchain_VerifyTx(t *testing.T) { }) t.Run("enabled", func(t *testing.T) { t.Run("dummy on-chain conflict", func(t *testing.T) { - tx := newTestTx(t, h, testScript) - require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) - conflicting := transaction.New([]byte{byte(opcode.RET)}, 1000_0000) - conflicting.ValidUntilBlock = bc.BlockHeight() + 1 - conflicting.Signers = []transaction.Signer{ - { - Account: validator.ScriptHash(), - Scopes: transaction.CalledByEntry, - }, - } - conflicting.Attributes = []transaction.Attribute{ - { - Type: transaction.ConflictsT, - Value: &transaction.Conflicts{ - Hash: tx.Hash(), + t.Run("on-chain conflict signed by malicious party", func(t *testing.T) { + tx := newTestTx(t, h, testScript) + require.NoError(t, accs[0].SignTx(netmode.UnitTestNet, tx)) + conflicting := transaction.New([]byte{byte(opcode.RET)}, 1000_0000) + conflicting.ValidUntilBlock = bc.BlockHeight() + 1 + conflicting.Signers = []transaction.Signer{ + { + Account: validator.ScriptHash(), + Scopes: transaction.CalledByEntry, }, - }, - } - conflicting.NetworkFee = 1000_0000 - require.NoError(t, validator.SignTx(netmode.UnitTestNet, conflicting)) - e.AddNewBlock(t, conflicting) - require.True(t, errors.Is(bc.VerifyTx(tx), core.ErrHasConflicts)) + } + conflicting.Attributes = []transaction.Attribute{ + { + Type: transaction.ConflictsT, + Value: &transaction.Conflicts{ + Hash: tx.Hash(), + }, + }, + } + conflicting.NetworkFee = 1000_0000 + require.NoError(t, validator.SignTx(netmode.UnitTestNet, conflicting)) + e.AddNewBlock(t, conflicting) + // We expect `tx` to pass verification, because on-chained `conflicting` doesn't have + // `tx`'s payer in the signers list, thus, `conflicting` should be considered as + // malicious conflict. + require.NoError(t, bc.VerifyTx(tx)) + }) + t.Run("multiple on-chain conflicts signed by malicious parties", func(t *testing.T) { + m1 := e.NewAccount(t) + m2 := e.NewAccount(t) + m3 := e.NewAccount(t) + good := e.NewAccount(t) + + // txGood doesn't conflict with anyone and signed by good signer. + txGood := newTestTx(t, good.ScriptHash(), testScript) + require.NoError(t, good.SignTx(netmode.UnitTestNet, txGood)) + + // txM1 conflicts with txGood and signed by two malicious signers. + txM1 := newTestTx(t, m1.ScriptHash(), testScript) + txM1.Signers = append(txM1.Signers, transaction.Signer{Account: m2.ScriptHash()}) + txM1.Attributes = []transaction.Attribute{ + { + Type: transaction.ConflictsT, + Value: &transaction.Conflicts{ + Hash: txGood.Hash(), + }, + }, + } + txM1.NetworkFee = 1_000_0000 + require.NoError(t, m1.SignTx(netmode.UnitTestNet, txM1)) + require.NoError(t, m2.SignTx(netmode.UnitTestNet, txM1)) + e.AddNewBlock(t, txM1) + + // txM2 conflicts with txGood and signed by one malicious signer. + txM2 := newTestTx(t, m3.ScriptHash(), testScript) + txM2.Attributes = []transaction.Attribute{ + { + Type: transaction.ConflictsT, + Value: &transaction.Conflicts{ + Hash: txGood.Hash(), + }, + }, + } + txM2.NetworkFee = 1_000_0000 + require.NoError(t, m3.SignTx(netmode.UnitTestNet, txM2)) + e.AddNewBlock(t, txM2) + + // We expect `tx` to pass verification, because on-chained `conflicting` doesn't have + // `tx`'s payer in the signers list, thus, `conflicting` should be considered as + // malicious conflict. + require.NoError(t, bc.VerifyTx(txGood)) + + // After that txGood can be added to the chain normally. + e.AddNewBlock(t, txGood) + + // And after that ErrAlreadyExist is expected on verification. + require.ErrorIs(t, bc.VerifyTx(txGood), core.ErrAlreadyExists) + }) + + t.Run("multiple on-chain conflicts signed by [valid+malicious] parties", func(t *testing.T) { + m1 := e.NewAccount(t) + m2 := e.NewAccount(t) + m3 := e.NewAccount(t) + good := e.NewAccount(t) + + // txGood doesn't conflict with anyone and signed by good signer. + txGood := newTestTx(t, good.ScriptHash(), testScript) + require.NoError(t, good.SignTx(netmode.UnitTestNet, txGood)) + + // txM1 conflicts with txGood and signed by one malicious and one good signers. + txM1 := newTestTx(t, m1.ScriptHash(), testScript) + txM1.Signers = append(txM1.Signers, transaction.Signer{Account: good.ScriptHash()}) + txM1.Attributes = []transaction.Attribute{ + { + Type: transaction.ConflictsT, + Value: &transaction.Conflicts{ + Hash: txGood.Hash(), + }, + }, + } + txM1.NetworkFee = 1_000_0000 + require.NoError(t, m1.SignTx(netmode.UnitTestNet, txM1)) + require.NoError(t, good.SignTx(netmode.UnitTestNet, txM1)) + e.AddNewBlock(t, txM1) + + // txM2 conflicts with txGood and signed by two malicious signers. + txM2 := newTestTx(t, m2.ScriptHash(), testScript) + txM2.Signers = append(txM2.Signers, transaction.Signer{Account: m3.ScriptHash()}) + txM2.Attributes = []transaction.Attribute{ + { + Type: transaction.ConflictsT, + Value: &transaction.Conflicts{ + Hash: txGood.Hash(), + }, + }, + } + txM2.NetworkFee = 1_000_0000 + require.NoError(t, m2.SignTx(netmode.UnitTestNet, txM2)) + require.NoError(t, m3.SignTx(netmode.UnitTestNet, txM2)) + e.AddNewBlock(t, txM2) + + // We expect `tx` to fail verification, because one of the on-chained `conflicting` + // transactions has common signers with `tx`, thus, `conflicting` should be + // considered as a valid conflict. + require.ErrorIs(t, bc.VerifyTx(txGood), core.ErrHasConflicts) + }) + + t.Run("multiple on-chain conflicts signed by [malicious+valid] parties", func(t *testing.T) { + m1 := e.NewAccount(t) + m2 := e.NewAccount(t) + m3 := e.NewAccount(t) + good := e.NewAccount(t) + + // txGood doesn't conflict with anyone and signed by good signer. + txGood := newTestTx(t, good.ScriptHash(), testScript) + require.NoError(t, good.SignTx(netmode.UnitTestNet, txGood)) + + // txM2 conflicts with txGood and signed by two malicious signers. + txM2 := newTestTx(t, m2.ScriptHash(), testScript) + txM2.Signers = append(txM2.Signers, transaction.Signer{Account: m3.ScriptHash()}) + txM2.Attributes = []transaction.Attribute{ + { + Type: transaction.ConflictsT, + Value: &transaction.Conflicts{ + Hash: txGood.Hash(), + }, + }, + } + txM2.NetworkFee = 1_000_0000 + require.NoError(t, m2.SignTx(netmode.UnitTestNet, txM2)) + require.NoError(t, m3.SignTx(netmode.UnitTestNet, txM2)) + e.AddNewBlock(t, txM2) + + // txM1 conflicts with txGood and signed by one malicious and one good signers. + txM1 := newTestTx(t, m1.ScriptHash(), testScript) + txM1.Signers = append(txM1.Signers, transaction.Signer{Account: good.ScriptHash()}) + txM1.Attributes = []transaction.Attribute{ + { + Type: transaction.ConflictsT, + Value: &transaction.Conflicts{ + Hash: txGood.Hash(), + }, + }, + } + txM1.NetworkFee = 1_000_0000 + require.NoError(t, m1.SignTx(netmode.UnitTestNet, txM1)) + require.NoError(t, good.SignTx(netmode.UnitTestNet, txM1)) + e.AddNewBlock(t, txM1) + + // We expect `tx` to fail verification, because one of the on-chained `conflicting` + // transactions has common signers with `tx`, thus, `conflicting` should be + // considered as a valid conflict. + require.ErrorIs(t, bc.VerifyTx(txGood), core.ErrHasConflicts) + }) + + t.Run("multiple on-chain conflicts signed by [valid + malicious + valid] parties", func(t *testing.T) { + m1 := e.NewAccount(t) + m2 := e.NewAccount(t) + m3 := e.NewAccount(t) + good := e.NewAccount(t) + + // txGood doesn't conflict with anyone and signed by good signer. + txGood := newTestTx(t, good.ScriptHash(), testScript) + require.NoError(t, good.SignTx(netmode.UnitTestNet, txGood)) + + // txM1 conflicts with txGood and signed by one malicious and one good signers. + txM1 := newTestTx(t, m1.ScriptHash(), testScript) + txM1.Signers = append(txM1.Signers, transaction.Signer{Account: good.ScriptHash()}) + txM1.Attributes = []transaction.Attribute{ + { + Type: transaction.ConflictsT, + Value: &transaction.Conflicts{ + Hash: txGood.Hash(), + }, + }, + } + txM1.NetworkFee = 1_000_0000 + require.NoError(t, m1.SignTx(netmode.UnitTestNet, txM1)) + require.NoError(t, good.SignTx(netmode.UnitTestNet, txM1)) + e.AddNewBlock(t, txM1) + + // txM2 conflicts with txGood and signed by two malicious signers. + txM2 := newTestTx(t, m2.ScriptHash(), testScript) + txM2.Signers = append(txM2.Signers, transaction.Signer{Account: m3.ScriptHash()}) + txM2.Attributes = []transaction.Attribute{ + { + Type: transaction.ConflictsT, + Value: &transaction.Conflicts{ + Hash: txGood.Hash(), + }, + }, + } + txM2.NetworkFee = 1_000_0000 + require.NoError(t, m2.SignTx(netmode.UnitTestNet, txM2)) + require.NoError(t, m3.SignTx(netmode.UnitTestNet, txM2)) + e.AddNewBlock(t, txM2) + + // txM3 conflicts with txGood and signed by one good and one malicious signers. + txM3 := newTestTx(t, good.ScriptHash(), testScript) + txM3.Signers = append(txM3.Signers, transaction.Signer{Account: m1.ScriptHash()}) + txM3.Attributes = []transaction.Attribute{ + { + Type: transaction.ConflictsT, + Value: &transaction.Conflicts{ + Hash: txGood.Hash(), + }, + }, + } + txM3.NetworkFee = 1_000_0000 + require.NoError(t, good.SignTx(netmode.UnitTestNet, txM3)) + require.NoError(t, m1.SignTx(netmode.UnitTestNet, txM3)) + e.AddNewBlock(t, txM3) + + // We expect `tx` to fail verification, because one of the on-chained `conflicting` + // transactions has common signers with `tx`, thus, `conflicting` should be + // considered as a valid conflict. + require.ErrorIs(t, bc.VerifyTx(txGood), core.ErrHasConflicts) + }) + + t.Run("on-chain conflict signed by single valid sender", func(t *testing.T) { + tx := newTestTx(t, h, testScript) + tx.Signers = []transaction.Signer{{Account: validator.ScriptHash()}} + require.NoError(t, validator.SignTx(netmode.UnitTestNet, tx)) + conflicting := transaction.New([]byte{byte(opcode.RET)}, 1000_0000) + conflicting.ValidUntilBlock = bc.BlockHeight() + 1 + conflicting.Signers = []transaction.Signer{ + { + Account: validator.ScriptHash(), + Scopes: transaction.CalledByEntry, + }, + } + conflicting.Attributes = []transaction.Attribute{ + { + Type: transaction.ConflictsT, + Value: &transaction.Conflicts{ + Hash: tx.Hash(), + }, + }, + } + conflicting.NetworkFee = 1000_0000 + require.NoError(t, validator.SignTx(netmode.UnitTestNet, conflicting)) + e.AddNewBlock(t, conflicting) + // We expect `tx` to fail verification, because on-chained `conflicting` has + // `tx`'s payer as a signer. + require.ErrorIs(t, bc.VerifyTx(tx), core.ErrHasConflicts) + }) }) t.Run("attribute on-chain conflict", func(t *testing.T) { tx := neoValidatorsInvoker.Invoke(t, stackitem.NewBool(true), "transfer", neoOwner, neoOwner, 1, nil) @@ -1675,7 +1916,7 @@ func TestBlockchain_VerifyTx(t *testing.T) { notary, err := wallet.NewAccount() require.NoError(t, err) designateSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", - int64(noderoles.P2PNotary), []interface{}{notary.PublicKey().Bytes()}) + int64(noderoles.P2PNotary), []any{notary.PublicKey().Bytes()}) txSetNotary := transaction.New([]byte{byte(opcode.RET)}, 0) txSetNotary.Signers = []transaction.Signer{ { @@ -1923,8 +2164,8 @@ func TestBlockchain_VerifyTx(t *testing.T) { return tx } - mp := mempool.New(10, 1, false) - verificationF := func(tx *transaction.Transaction, data interface{}) error { + mp := mempool.New(10, 1, false, nil) + verificationF := func(tx *transaction.Transaction, data any) error { if data.(int) > 5 { return errors.New("bad data") } @@ -1953,13 +2194,13 @@ func TestBlockchain_VerifyTx(t *testing.T) { maxNVB, err := bc.GetMaxNotValidBeforeDelta() require.NoError(t, err) tx := getPartiallyFilledTx(bc.BlockHeight()+maxNVB+1, bc.BlockHeight()+1) - require.True(t, errors.Is(bc.PoolTxWithData(tx, 5, mp, bc, verificationF), core.ErrInvalidAttribute)) + require.ErrorIs(t, bc.PoolTxWithData(tx, 5, mp, bc, verificationF), core.ErrInvalidAttribute) }) t.Run("bad ValidUntilBlock: too small", func(t *testing.T) { maxNVB, err := bc.GetMaxNotValidBeforeDelta() require.NoError(t, err) tx := getPartiallyFilledTx(bc.BlockHeight(), bc.BlockHeight()+maxNVB+1) - require.True(t, errors.Is(bc.PoolTxWithData(tx, 5, mp, bc, verificationF), core.ErrInvalidAttribute)) + require.ErrorIs(t, bc.PoolTxWithData(tx, 5, mp, bc, verificationF), core.ErrInvalidAttribute) }) t.Run("good", func(t *testing.T) { tx := getPartiallyFilledTx(bc.BlockHeight(), bc.BlockHeight()+1) @@ -1976,7 +2217,7 @@ func TestBlockchain_Bug1728(t *testing.T) { src := `package example import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" func init() { if true { } else { } } - func _deploy(_ interface{}, isUpdate bool) { + func _deploy(_ any, isUpdate bool) { runtime.Log("Deploy") }` c := neotest.CompileSource(t, acc.ScriptHash(), strings.NewReader(src), &compiler.Options{Name: "TestContract"}) diff --git a/pkg/core/chaindump/dump_test.go b/pkg/core/chaindump/dump_test.go index 3ae6b3bb9..06531ff31 100644 --- a/pkg/core/chaindump/dump_test.go +++ b/pkg/core/chaindump/dump_test.go @@ -85,7 +85,7 @@ func testDumpAndRestore(t *testing.T, dumpF, restoreF func(c *config.Blockchain) r = io.NewBinReaderFromBuf(buf) err := chaindump.Restore(bc2, r, 4, bc.BlockHeight()-bc2.BlockHeight(), f) - require.True(t, errors.Is(err, errStopped)) + require.ErrorIs(t, err, errStopped) require.Equal(t, bc.BlockHeight()-1, lastIndex) }) }) diff --git a/pkg/core/dao/dao.go b/pkg/core/dao/dao.go index 54a9cd85a..b6092e1fc 100644 --- a/pkg/core/dao/dao.go +++ b/pkg/core/dao/dao.go @@ -457,7 +457,8 @@ func (v *Version) FromBytes(data []byte) error { return errors.New("missing version") } i := 0 - for ; i < len(data) && data[i] != '\x00'; i++ { + for i < len(data) && data[i] != '\x00' { + i++ } if i == len(data) { @@ -683,8 +684,11 @@ func (dao *Simple) StoreHeaderHashes(hashes []util.Uint256, height uint32) error // HasTransaction returns nil if the given store does not contain the given // Transaction hash. It returns an error in case the transaction is in chain -// or in the list of conflicting transactions. -func (dao *Simple) HasTransaction(hash util.Uint256) error { +// or in the list of conflicting transactions. If non-zero signers are specified, +// then additional check against the conflicting transaction signers intersection +// is held. Do not omit signers in case if it's important to check the validity +// of a supposedly conflicting on-chain transaction. +func (dao *Simple) HasTransaction(hash util.Uint256, signers []transaction.Signer) error { key := dao.makeExecutableKey(hash) bytes, err := dao.Store.Get(key) if err != nil { @@ -694,10 +698,33 @@ func (dao *Simple) HasTransaction(hash util.Uint256) error { if len(bytes) < 6 { return nil } - if bytes[5] == transaction.DummyVersion { + if bytes[5] != transaction.DummyVersion { + return ErrAlreadyExists + } + if len(signers) == 0 { return ErrHasConflicts } - return ErrAlreadyExists + + sMap := make(map[util.Uint160]struct{}, len(signers)) + for _, s := range signers { + sMap[s.Account] = struct{}{} + } + br := io.NewBinReaderFromBuf(bytes[6:]) + for { + var u util.Uint160 + u.DecodeBinary(br) + if br.Err != nil { + if errors.Is(br.Err, iocore.EOF) { + break + } + return fmt.Errorf("failed to decode conflict record: %w", err) + } + if _, ok := sMap[u]; ok { + return ErrHasConflicts + } + } + + return nil } // StoreAsBlock stores given block as DataBlock. It can reuse given buffer for @@ -804,18 +831,50 @@ func (dao *Simple) StoreAsTransaction(tx *transaction.Transaction, index uint32, } dao.Store.Put(key, buf.Bytes()) if dao.Version.P2PSigExtensions { - var value []byte - for _, attr := range tx.GetAttributes(transaction.ConflictsT) { + var ( + valuePrefix []byte + newSigners []byte + ) + attrs := tx.GetAttributes(transaction.ConflictsT) + for _, attr := range attrs { hash := attr.Value.(*transaction.Conflicts).Hash copy(key[1:], hash.BytesBE()) - if value == nil { - buf.Reset() - buf.WriteB(storage.ExecTransaction) - buf.WriteU32LE(index) - buf.BinWriter.WriteB(transaction.DummyVersion) - value = buf.Bytes() + + old, err := dao.Store.Get(key) + if err != nil && !errors.Is(err, storage.ErrKeyNotFound) { + return fmt.Errorf("failed to retrieve previous conflict record for %s: %w", hash.StringLE(), err) + } + if err == nil { + if len(old) <= 6 { // storage.ExecTransaction + U32LE index + transaction.DummyVersion + return fmt.Errorf("invalid conflict record format of length %d", len(old)) + } + } + buf.Reset() + buf.WriteBytes(old) + if len(old) == 0 { + if len(valuePrefix) != 0 { + buf.WriteBytes(valuePrefix) + } else { + buf.WriteB(storage.ExecTransaction) + buf.WriteU32LE(index) + buf.WriteB(transaction.DummyVersion) + } + } + newSignersOffset := buf.Len() + if len(newSigners) == 0 { + for _, s := range tx.Signers { + s.Account.EncodeBinary(buf.BinWriter) + } + } else { + buf.WriteBytes(newSigners) + } + val := buf.Bytes() + dao.Store.Put(key, val) + + if len(attrs) > 1 && len(valuePrefix) == 0 { + valuePrefix = slice.Copy(val[:6]) + newSigners = slice.Copy(val[newSignersOffset:]) } - dao.Store.Put(key, value) } } return nil diff --git a/pkg/core/dao/dao_test.go b/pkg/core/dao/dao_test.go index 6583319e7..1e8b647cd 100644 --- a/pkg/core/dao/dao_test.go +++ b/pkg/core/dao/dao_test.go @@ -2,7 +2,6 @@ package dao import ( "encoding/binary" - "errors" "testing" "github.com/nspcc-dev/neo-go/internal/random" @@ -187,8 +186,8 @@ func TestStoreAsTransaction(t *testing.T) { } err := dao.StoreAsTransaction(tx, 0, aer) require.NoError(t, err) - err = dao.HasTransaction(hash) - require.NotNil(t, err) + err = dao.HasTransaction(hash, nil) + require.ErrorIs(t, err, ErrAlreadyExists) gotAppExecResult, err := dao.GetAppExecResults(hash, trigger.All) require.NoError(t, err) require.Equal(t, 1, len(gotAppExecResult)) @@ -198,34 +197,84 @@ func TestStoreAsTransaction(t *testing.T) { t.Run("P2PSigExtensions on", func(t *testing.T) { dao := NewSimple(storage.NewMemoryStore(), false, true) conflictsH := util.Uint256{1, 2, 3} - tx := transaction.New([]byte{byte(opcode.PUSH1)}, 1) - tx.Signers = append(tx.Signers, transaction.Signer{}) - tx.Scripts = append(tx.Scripts, transaction.Witness{}) - tx.Attributes = []transaction.Attribute{ + signer1 := util.Uint160{1, 2, 3} + signer2 := util.Uint160{4, 5, 6} + signer3 := util.Uint160{7, 8, 9} + signerMalicious := util.Uint160{10, 11, 12} + tx1 := transaction.New([]byte{byte(opcode.PUSH1)}, 1) + tx1.Signers = append(tx1.Signers, transaction.Signer{Account: signer1}, transaction.Signer{Account: signer2}) + tx1.Scripts = append(tx1.Scripts, transaction.Witness{}, transaction.Witness{}) + tx1.Attributes = []transaction.Attribute{ { Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: conflictsH}, }, } - hash := tx.Hash() - aer := &state.AppExecResult{ - Container: hash, + hash1 := tx1.Hash() + tx2 := transaction.New([]byte{byte(opcode.PUSH1)}, 1) + tx2.Signers = append(tx2.Signers, transaction.Signer{Account: signer3}) + tx2.Scripts = append(tx2.Scripts, transaction.Witness{}) + tx2.Attributes = []transaction.Attribute{ + { + Type: transaction.ConflictsT, + Value: &transaction.Conflicts{Hash: conflictsH}, + }, + } + hash2 := tx2.Hash() + aer1 := &state.AppExecResult{ + Container: hash1, Execution: state.Execution{ Trigger: trigger.Application, Events: []state.NotificationEvent{}, Stack: []stackitem.Item{}, }, } - err := dao.StoreAsTransaction(tx, 0, aer) + err := dao.StoreAsTransaction(tx1, 0, aer1) require.NoError(t, err) - err = dao.HasTransaction(hash) - require.True(t, errors.Is(err, ErrAlreadyExists)) - err = dao.HasTransaction(conflictsH) - require.True(t, errors.Is(err, ErrHasConflicts)) - gotAppExecResult, err := dao.GetAppExecResults(hash, trigger.All) + aer2 := &state.AppExecResult{ + Container: hash2, + Execution: state.Execution{ + Trigger: trigger.Application, + Events: []state.NotificationEvent{}, + Stack: []stackitem.Item{}, + }, + } + err = dao.StoreAsTransaction(tx2, 0, aer2) + require.NoError(t, err) + err = dao.HasTransaction(hash1, nil) + require.ErrorIs(t, err, ErrAlreadyExists) + err = dao.HasTransaction(hash2, nil) + require.ErrorIs(t, err, ErrAlreadyExists) + + // Conflicts: unimportant payer. + err = dao.HasTransaction(conflictsH, nil) + require.ErrorIs(t, err, ErrHasConflicts) + + // Conflicts: payer is important, conflict isn't malicious, test signer #1. + err = dao.HasTransaction(conflictsH, []transaction.Signer{{Account: signer1}}) + require.ErrorIs(t, err, ErrHasConflicts) + + // Conflicts: payer is important, conflict isn't malicious, test signer #2. + err = dao.HasTransaction(conflictsH, []transaction.Signer{{Account: signer2}}) + require.ErrorIs(t, err, ErrHasConflicts) + + // Conflicts: payer is important, conflict isn't malicious, test signer #3. + err = dao.HasTransaction(conflictsH, []transaction.Signer{{Account: signer3}}) + require.ErrorIs(t, err, ErrHasConflicts) + + // Conflicts: payer is important, conflict is malicious. + err = dao.HasTransaction(conflictsH, []transaction.Signer{{Account: signerMalicious}}) + require.NoError(t, err) + + gotAppExecResult, err := dao.GetAppExecResults(hash1, trigger.All) require.NoError(t, err) require.Equal(t, 1, len(gotAppExecResult)) - require.Equal(t, *aer, gotAppExecResult[0]) + require.Equal(t, *aer1, gotAppExecResult[0]) + + gotAppExecResult, err = dao.GetAppExecResults(hash2, trigger.All) + require.NoError(t, err) + require.Equal(t, 1, len(gotAppExecResult)) + require.Equal(t, *aer2, gotAppExecResult[0]) }) } diff --git a/pkg/core/interop/contract/account_test.go b/pkg/core/interop/contract/account_test.go index 2359c185e..bedb97730 100644 --- a/pkg/core/interop/contract/account_test.go +++ b/pkg/core/interop/contract/account_test.go @@ -63,7 +63,7 @@ func TestCreateMultisigAccount(t *testing.T) { e := neotest.NewExecutor(t, bc, acc, acc) w := io.NewBufBinWriter() - createScript := func(t *testing.T, pubs []interface{}, m int) []byte { + createScript := func(t *testing.T, pubs []any, m int) []byte { w.Reset() emit.Array(w.BinWriter, pubs...) emit.Int(w.BinWriter, int64(m)) @@ -74,7 +74,7 @@ func TestCreateMultisigAccount(t *testing.T) { t.Run("Good", func(t *testing.T) { m, n := 3, 5 pubs := make(keys.PublicKeys, n) - arr := make([]interface{}, n) + arr := make([]any, n) for i := range pubs { pk, err := keys.NewPrivateKey() require.NoError(t, err) @@ -94,13 +94,13 @@ func TestCreateMultisigAccount(t *testing.T) { require.Equal(t, hash.Hash160(expected), u) }) t.Run("InvalidKey", func(t *testing.T) { - script := createScript(t, []interface{}{[]byte{1, 2, 3}}, 1) + script := createScript(t, []any{[]byte{1, 2, 3}}, 1) e.InvokeScriptCheckFAULT(t, script, []neotest.Signer{acc}, "invalid prefix 1") }) t.Run("Invalid m", func(t *testing.T) { pk, err := keys.NewPrivateKey() require.NoError(t, err) - script := createScript(t, []interface{}{pk.PublicKey().Bytes()}, 2) + script := createScript(t, []any{pk.PublicKey().Bytes()}, 2) e.InvokeScriptCheckFAULT(t, script, []neotest.Signer{acc}, "length of the signatures (2) is higher then the number of public keys") }) t.Run("m overflows int32", func(t *testing.T) { @@ -131,7 +131,7 @@ func TestCreateAccount_Hardfork(t *testing.T) { pub := priv.PublicKey() w := io.NewBufBinWriter() - emit.Array(w.BinWriter, []interface{}{pub.Bytes(), pub.Bytes(), pub.Bytes()}...) + emit.Array(w.BinWriter, []any{pub.Bytes(), pub.Bytes(), pub.Bytes()}...) emit.Int(w.BinWriter, int64(2)) emit.Syscall(w.BinWriter, interopnames.SystemContractCreateMultisigAccount) require.NoError(t, w.Err) diff --git a/pkg/core/interop/contract/call_test.go b/pkg/core/interop/contract/call_test.go index 909e6a7c4..e51671562 100644 --- a/pkg/core/interop/contract/call_test.go +++ b/pkg/core/interop/contract/call_test.go @@ -90,7 +90,7 @@ func TestCall(t *testing.T) { require.Error(t, contract.Call(ic)) }) - runInvalid := func(args ...interface{}) func(t *testing.T) { + runInvalid := func(args ...any) func(t *testing.T) { return func(t *testing.T) { loadScriptWithHashAndFlags(ic, currScript, h, callflag.All, 42) for i := range args { @@ -594,7 +594,7 @@ func TestCALLL_from_VoidContext(t *testing.T) { ctrInvoker.Invoke(t, stackitem.Null{}, "callHasRet") } -func loadScript(ic *interop.Context, script []byte, args ...interface{}) { +func loadScript(ic *interop.Context, script []byte, args ...any) { ic.SpawnVM() ic.VM.LoadScriptWithFlags(script, callflag.AllowCall) for i := range args { @@ -603,7 +603,7 @@ func loadScript(ic *interop.Context, script []byte, args ...interface{}) { ic.VM.GasLimit = -1 } -func loadScriptWithHashAndFlags(ic *interop.Context, script []byte, hash util.Uint160, f callflag.CallFlag, args ...interface{}) { +func loadScriptWithHashAndFlags(ic *interop.Context, script []byte, hash util.Uint160, f callflag.CallFlag, args ...any) { ic.SpawnVM() ic.VM.LoadScriptWithHash(script, hash, f) for i := range args { diff --git a/pkg/core/interop/crypto/ecdsa_test.go b/pkg/core/interop/crypto/ecdsa_test.go index fc6f70bfd..a44c959ce 100644 --- a/pkg/core/interop/crypto/ecdsa_test.go +++ b/pkg/core/interop/crypto/ecdsa_test.go @@ -180,7 +180,7 @@ func TestCheckSig(t *testing.T) { verifyFunc := ECDSASecp256r1CheckSig d := dao.NewSimple(storage.NewMemoryStore(), false, false) ic := &interop.Context{Network: uint32(netmode.UnitTestNet), DAO: d} - runCase := func(t *testing.T, isErr bool, result interface{}, args ...interface{}) { + runCase := func(t *testing.T, isErr bool, result any, args ...any) { ic.SpawnVM() for i := range args { ic.VM.Estack().PushVal(args[i]) diff --git a/pkg/core/interop/interopnames/convert_test.go b/pkg/core/interop/interopnames/convert_test.go index 1ec2c1f21..a17592e7a 100644 --- a/pkg/core/interop/interopnames/convert_test.go +++ b/pkg/core/interop/interopnames/convert_test.go @@ -1,7 +1,6 @@ package interopnames import ( - "errors" "testing" "github.com/stretchr/testify/require" @@ -16,6 +15,6 @@ func TestFromID(t *testing.T) { }) t.Run("Invalid", func(t *testing.T) { _, err := FromID(0x42424242) - require.True(t, errors.Is(err, errNotFound)) + require.ErrorIs(t, err, errNotFound) }) } diff --git a/pkg/core/interop/runtime/engine_test.go b/pkg/core/interop/runtime/engine_test.go index 656a78dcb..646926d8a 100644 --- a/pkg/core/interop/runtime/engine_test.go +++ b/pkg/core/interop/runtime/engine_test.go @@ -21,7 +21,7 @@ import ( "go.uber.org/zap/zaptest" ) -func checkStack(t *testing.T, v *vm.VM, args ...interface{}) { +func checkStack(t *testing.T, v *vm.VM, args ...any) { require.Equal(t, len(args), v.Estack().Len()) for i := range args { require.Equal(t, stackitem.Make(args[i]), v.Estack().Pop().Item(), "%d", i) @@ -122,7 +122,7 @@ func TestLog(t *testing.T) { ls := buf.Lines() require.Equal(t, 1, len(ls)) - var logMsg map[string]interface{} + var logMsg map[string]any require.NoError(t, json.Unmarshal([]byte(ls[0]), &logMsg)) require.Equal(t, "info", logMsg["level"]) require.Equal(t, "hello", logMsg["msg"]) diff --git a/pkg/core/interop/runtime/ext_test.go b/pkg/core/interop/runtime/ext_test.go index d423ee701..64feb3ecb 100644 --- a/pkg/core/interop/runtime/ext_test.go +++ b/pkg/core/interop/runtime/ext_test.go @@ -71,7 +71,7 @@ func createVM(t testing.TB) (*vm.VM, *interop.Context, *core.Blockchain) { return v, ic, chain } -func loadScriptWithHashAndFlags(ic *interop.Context, script []byte, hash util.Uint160, f callflag.CallFlag, args ...interface{}) { +func loadScriptWithHashAndFlags(ic *interop.Context, script []byte, hash util.Uint160, f callflag.CallFlag, args ...any) { ic.SpawnVM() ic.VM.LoadScriptWithHash(script, hash, f) for i := range args { @@ -80,7 +80,7 @@ func loadScriptWithHashAndFlags(ic *interop.Context, script []byte, hash util.Ui ic.VM.GasLimit = -1 } -func wrapDynamicScript(t *testing.T, script []byte, flags callflag.CallFlag, args ...interface{}) []byte { +func wrapDynamicScript(t *testing.T, script []byte, flags callflag.CallFlag, args ...any) []byte { b := io.NewBufBinWriter() // Params. @@ -150,7 +150,7 @@ func TestCheckWitness(t *testing.T) { script := []byte{byte(opcode.RET)} scriptHash := hash.Hash160(script) - check := func(t *testing.T, ic *interop.Context, arg interface{}, shouldFail bool, expected ...bool) { + check := func(t *testing.T, ic *interop.Context, arg any, shouldFail bool, expected ...bool) { ic.VM.Estack().PushVal(arg) err := runtime.CheckWitness(ic) if shouldFail { @@ -632,7 +632,7 @@ func TestGetRandomCompatibility(t *testing.T) { func TestNotify(t *testing.T) { caller := random.Uint160() - newIC := func(name string, args interface{}) *interop.Context { + newIC := func(name string, args any) *interop.Context { _, _, bc, cs := getDeployedInternal(t) ic, err := bc.GetTestVM(trigger.Application, nil, nil) require.NoError(t, err) diff --git a/pkg/core/interop/storage/find.go b/pkg/core/interop/storage/find.go index 62a59b688..666950e34 100644 --- a/pkg/core/interop/storage/find.go +++ b/pkg/core/interop/storage/find.go @@ -19,9 +19,10 @@ const ( FindDeserialize = 1 << 3 FindPick0 = 1 << 4 FindPick1 = 1 << 5 + FindBackwards = 1 << 7 FindAll = FindDefault | FindKeysOnly | FindRemovePrefix | FindValuesOnly | - FindDeserialize | FindPick0 | FindPick1 + FindDeserialize | FindPick0 | FindPick1 | FindBackwards ) // Iterator is an iterator state representation. @@ -111,8 +112,9 @@ func Find(ic *interop.Context) error { if opts&FindDeserialize == 0 && (opts&FindPick0 != 0 || opts&FindPick1 != 0) { return fmt.Errorf("%w: PickN is specified without Deserialize", errFindInvalidOptions) } + bkwrds := opts&FindBackwards != 0 ctx, cancel := context.WithCancel(context.Background()) - seekres := ic.DAO.SeekAsync(ctx, stc.ID, storage.SeekRange{Prefix: prefix}) + seekres := ic.DAO.SeekAsync(ctx, stc.ID, storage.SeekRange{Prefix: prefix, Backwards: bkwrds}) item := NewIterator(seekres, prefix, opts) ic.VM.Estack().PushItem(stackitem.NewInterop(item)) ic.RegisterCancelFunc(func() { @@ -120,7 +122,7 @@ func Find(ic *interop.Context) error { // Underlying persistent store is likely to be a private MemCachedStore. Thus, // to avoid concurrent map iteration and map write we need to wait until internal // seek goroutine is finished, because it can access underlying persistent store. - for range seekres { + for range seekres { //nolint:revive //empty-block } }) diff --git a/pkg/core/interop/storage/interops_test.go b/pkg/core/interop/storage/interops_test.go index 74795fc21..0ae492c9d 100644 --- a/pkg/core/interop/storage/interops_test.go +++ b/pkg/core/interop/storage/interops_test.go @@ -11,7 +11,7 @@ import ( ) func TestUnexpectedNonInterops(t *testing.T) { - vals := map[string]interface{}{ + vals := map[string]any{ "int": 1, "bool": false, "string": "smth", diff --git a/pkg/core/interop/storage/storage_test.go b/pkg/core/interop/storage/storage_test.go index fec1820b2..ed2394c6d 100644 --- a/pkg/core/interop/storage/storage_test.go +++ b/pkg/core/interop/storage/storage_test.go @@ -1,7 +1,6 @@ package storage_test import ( - "errors" "math/big" "testing" @@ -42,7 +41,7 @@ func TestPut(t *testing.T) { t.Run("create, not enough gas", func(t *testing.T) { initVM(t, []byte{1}, []byte{2, 3}, 2*native.DefaultStoragePrice) err := istorage.Put(ic) - require.True(t, errors.Is(err, istorage.ErrGasLimitExceeded), "got: %v", err) + require.ErrorIs(t, err, istorage.ErrGasLimitExceeded) }) initVM(t, []byte{4}, []byte{5, 6}, 3*native.DefaultStoragePrice) @@ -52,7 +51,7 @@ func TestPut(t *testing.T) { t.Run("not enough gas", func(t *testing.T) { initVM(t, []byte{4}, []byte{5, 6, 7, 8}, native.DefaultStoragePrice) err := istorage.Put(ic) - require.True(t, errors.Is(err, istorage.ErrGasLimitExceeded), "got: %v", err) + require.ErrorIs(t, err, istorage.ErrGasLimitExceeded) }) initVM(t, []byte{4}, []byte{5, 6, 7, 8}, 3*native.DefaultStoragePrice) require.NoError(t, istorage.Put(ic)) @@ -197,7 +196,18 @@ func TestFind(t *testing.T) { }), }) }) - + t.Run("normal invocation, backwards", func(t *testing.T) { + testFind(t, []byte{0x01}, istorage.FindBackwards, []stackitem.Item{ + stackitem.NewStruct([]stackitem.Item{ + stackitem.NewByteArray(skeys[0]), + stackitem.NewByteArray(items[0]), + }), + stackitem.NewStruct([]stackitem.Item{ + stackitem.NewByteArray(skeys[2]), + stackitem.NewByteArray(items[2]), + }), + }) + }) t.Run("keys only", func(t *testing.T) { testFind(t, []byte{0x01}, istorage.FindKeysOnly, []stackitem.Item{ stackitem.NewByteArray(skeys[2]), diff --git a/pkg/core/mempool/mem_pool.go b/pkg/core/mempool/mem_pool.go index d393f6e34..2fbce047b 100644 --- a/pkg/core/mempool/mem_pool.go +++ b/pkg/core/mempool/mem_pool.go @@ -41,7 +41,7 @@ var ( type item struct { txn *transaction.Transaction blockStamp uint32 - data interface{} + data any } // items is a slice of an item. @@ -65,12 +65,13 @@ type Pool struct { // oracleResp contains the ids of oracle responses for the tx in the pool. oracleResp map[uint64]util.Uint256 - capacity int - feePerByte int64 - payerIndex int + capacity int + feePerByte int64 + payerIndex int + updateMetricsCb func(int) resendThreshold uint32 - resendFunc func(*transaction.Transaction, interface{}) + resendFunc func(*transaction.Transaction, any) // subscriptions for mempool events subscriptionsEnabled bool @@ -197,7 +198,7 @@ func checkBalance(tx *transaction.Transaction, balance utilityBalanceAndFees) (u } // Add tries to add the given transaction to the Pool. -func (mp *Pool) Add(t *transaction.Transaction, fee Feer, data ...interface{}) error { +func (mp *Pool) Add(t *transaction.Transaction, fee Feer, data ...any) error { var pItem = item{ txn: t, blockStamp: fee.BlockHeight(), @@ -286,7 +287,9 @@ func (mp *Pool) Add(t *transaction.Transaction, fee Feer, data ...interface{}) e // we already checked balance in checkTxConflicts, so don't need to check again mp.tryAddSendersFee(pItem.txn, fee, false) - updateMempoolMetrics(len(mp.verifiedTxes)) + if mp.updateMetricsCb != nil { + mp.updateMetricsCb(len(mp.verifiedTxes)) + } mp.lock.Unlock() if mp.subscriptionsOn.Load() { @@ -342,7 +345,9 @@ func (mp *Pool) removeInternal(hash util.Uint256, feer Feer) { } } } - updateMempoolMetrics(len(mp.verifiedTxes)) + if mp.updateMetricsCb != nil { + mp.updateMetricsCb(len(mp.verifiedTxes)) + } } // RemoveStale filters verified transactions through the given function keeping @@ -420,7 +425,7 @@ func (mp *Pool) checkPolicy(tx *transaction.Transaction, policyChanged bool) boo } // New returns a new Pool struct. -func New(capacity int, payerIndex int, enableSubscriptions bool) *Pool { +func New(capacity int, payerIndex int, enableSubscriptions bool, updateMetricsCb func(int)) *Pool { mp := &Pool{ verifiedMap: make(map[util.Uint256]*transaction.Transaction, capacity), verifiedTxes: make([]item, 0, capacity), @@ -434,6 +439,7 @@ func New(capacity int, payerIndex int, enableSubscriptions bool) *Pool { events: make(chan mempoolevent.Event), subCh: make(chan chan<- mempoolevent.Event), unsubCh: make(chan chan<- mempoolevent.Event), + updateMetricsCb: updateMetricsCb, } mp.subscriptionsOn.Store(false) return mp @@ -441,7 +447,7 @@ func New(capacity int, payerIndex int, enableSubscriptions bool) *Pool { // SetResendThreshold sets a threshold after which the transaction will be considered stale // and returned for retransmission by `GetStaleTransactions`. -func (mp *Pool) SetResendThreshold(h uint32, f func(*transaction.Transaction, interface{})) { +func (mp *Pool) SetResendThreshold(h uint32, f func(*transaction.Transaction, any)) { mp.lock.Lock() defer mp.lock.Unlock() mp.resendThreshold = h @@ -466,7 +472,7 @@ func (mp *Pool) TryGetValue(hash util.Uint256) (*transaction.Transaction, bool) } // TryGetData returns data associated with the specified transaction if it exists in the memory pool. -func (mp *Pool) TryGetData(hash util.Uint256) (interface{}, bool) { +func (mp *Pool) TryGetData(hash util.Uint256) (any, bool) { mp.lock.RLock() defer mp.lock.RUnlock() if tx, ok := mp.verifiedMap[hash]; ok { @@ -514,32 +520,50 @@ func (mp *Pool) checkTxConflicts(tx *transaction.Transaction, fee Feer) ([]*tran var expectedSenderFee utilityBalanceAndFees // Check Conflicts attributes. - var conflictsToBeRemoved []*transaction.Transaction + var ( + conflictsToBeRemoved []*transaction.Transaction + conflictingFee int64 + ) if fee.P2PSigExtensionsEnabled() { // Step 1: check if `tx` was in attributes of mempooled transactions. if conflictingHashes, ok := mp.conflicts[tx.Hash()]; ok { for _, hash := range conflictingHashes { existingTx := mp.verifiedMap[hash] - if existingTx.HasSigner(payer) && existingTx.NetworkFee > tx.NetworkFee { - return nil, fmt.Errorf("%w: conflicting transaction %s has bigger network fee", ErrConflictsAttribute, existingTx.Hash().StringBE()) + if existingTx.HasSigner(payer) { + conflictingFee += existingTx.NetworkFee } conflictsToBeRemoved = append(conflictsToBeRemoved, existingTx) } } // Step 2: check if mempooled transactions were in `tx`'s attributes. - for _, attr := range tx.GetAttributes(transaction.ConflictsT) { - hash := attr.Value.(*transaction.Conflicts).Hash - existingTx, ok := mp.verifiedMap[hash] - if !ok { - continue + conflictsAttrs := tx.GetAttributes(transaction.ConflictsT) + if len(conflictsAttrs) != 0 { + txSigners := make(map[util.Uint160]struct{}, len(tx.Signers)) + for _, s := range tx.Signers { + txSigners[s.Account] = struct{}{} } - if !tx.HasSigner(existingTx.Signers[mp.payerIndex].Account) { - return nil, fmt.Errorf("%w: not signed by the sender of conflicting transaction %s", ErrConflictsAttribute, existingTx.Hash().StringBE()) + for _, attr := range conflictsAttrs { + hash := attr.Value.(*transaction.Conflicts).Hash + existingTx, ok := mp.verifiedMap[hash] + if !ok { + continue + } + var signerOK bool + for _, s := range existingTx.Signers { + if _, ok := txSigners[s.Account]; ok { + signerOK = true + break + } + } + if !signerOK { + return nil, fmt.Errorf("%w: not signed by a signer of conflicting transaction %s", ErrConflictsAttribute, existingTx.Hash().StringBE()) + } + conflictingFee += existingTx.NetworkFee + conflictsToBeRemoved = append(conflictsToBeRemoved, existingTx) } - if existingTx.NetworkFee >= tx.NetworkFee { - return nil, fmt.Errorf("%w: conflicting transaction %s has bigger or equal network fee", ErrConflictsAttribute, existingTx.Hash().StringBE()) - } - conflictsToBeRemoved = append(conflictsToBeRemoved, existingTx) + } + if conflictingFee != 0 && tx.NetworkFee <= conflictingFee { + return nil, fmt.Errorf("%w: conflicting transactions have bigger or equal network fee: %d vs %d", ErrConflictsAttribute, tx.NetworkFee, conflictingFee) } // Step 3: take into account sender's conflicting transactions before balance check. expectedSenderFee = actualSenderFee diff --git a/pkg/core/mempool/mem_pool_test.go b/pkg/core/mempool/mem_pool_test.go index 20d571953..d1d247dc9 100644 --- a/pkg/core/mempool/mem_pool_test.go +++ b/pkg/core/mempool/mem_pool_test.go @@ -1,14 +1,14 @@ package mempool import ( - "errors" + "fmt" "math/big" "sort" + "strings" "testing" "time" "github.com/holiman/uint256" - "github.com/nspcc-dev/neo-go/internal/random" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/network/payload" "github.com/nspcc-dev/neo-go/pkg/util" @@ -45,7 +45,7 @@ func (fs *FeerStub) P2PSigExtensionsEnabled() bool { } func testMemPoolAddRemoveWithFeer(t *testing.T, fs Feer) { - mp := New(10, 0, false) + mp := New(10, 0, false, nil) tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0) tx.Nonce = 0 tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}} @@ -66,7 +66,7 @@ func testMemPoolAddRemoveWithFeer(t *testing.T, fs Feer) { } func TestMemPoolRemoveStale(t *testing.T) { - mp := New(5, 0, false) + mp := New(5, 0, false, nil) txs := make([]*transaction.Transaction, 5) for i := range txs { txs[i] = transaction.New([]byte{byte(opcode.PUSH1)}, 0) @@ -76,7 +76,7 @@ func TestMemPoolRemoveStale(t *testing.T) { } staleTxs := make(chan *transaction.Transaction, 5) - f := func(tx *transaction.Transaction, _ interface{}) { + f := func(tx *transaction.Transaction, _ any) { staleTxs <- tx } mp.SetResendThreshold(5, f) @@ -117,7 +117,7 @@ func TestMemPoolAddRemove(t *testing.T) { func TestOverCapacity(t *testing.T) { var fs = &FeerStub{balance: 10000000} const mempoolSize = 10 - mp := New(mempoolSize, 0, false) + mp := New(mempoolSize, 0, false, nil) for i := 0; i < mempoolSize; i++ { tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0) @@ -193,7 +193,7 @@ func TestOverCapacity(t *testing.T) { func TestGetVerified(t *testing.T) { var fs = &FeerStub{} const mempoolSize = 10 - mp := New(mempoolSize, 0, false) + mp := New(mempoolSize, 0, false, nil) txes := make([]*transaction.Transaction, 0, mempoolSize) for i := 0; i < mempoolSize; i++ { @@ -217,7 +217,7 @@ func TestGetVerified(t *testing.T) { func TestRemoveStale(t *testing.T) { var fs = &FeerStub{} const mempoolSize = 10 - mp := New(mempoolSize, 0, false) + mp := New(mempoolSize, 0, false, nil) txes1 := make([]*transaction.Transaction, 0, mempoolSize/2) txes2 := make([]*transaction.Transaction, 0, mempoolSize/2) @@ -250,7 +250,7 @@ func TestRemoveStale(t *testing.T) { } func TestMemPoolFees(t *testing.T) { - mp := New(10, 0, false) + mp := New(10, 0, false, nil) fs := &FeerStub{balance: 10000000} sender0 := util.Uint160{1, 2, 3} tx0 := transaction.New([]byte{byte(opcode.PUSH1)}, 0) @@ -355,7 +355,7 @@ func TestMempoolItemsOrder(t *testing.T) { } func TestMempoolAddRemoveOracleResponse(t *testing.T) { - mp := New(3, 0, false) + mp := New(3, 0, false, nil) nonce := uint32(0) fs := &FeerStub{balance: 10000} newTx := func(netFee int64, id uint64) *transaction.Transaction { @@ -380,7 +380,7 @@ func TestMempoolAddRemoveOracleResponse(t *testing.T) { // smaller network fee tx2 := newTx(5, 1) err := mp.Add(tx2, fs) - require.True(t, errors.Is(err, ErrOracleResponse)) + require.ErrorIs(t, err, ErrOracleResponse) // ok if old tx is removed mp.Remove(tx1.Hash(), fs) @@ -424,18 +424,23 @@ func TestMempoolAddRemoveOracleResponse(t *testing.T) { } func TestMempoolAddRemoveConflicts(t *testing.T) { - capacity := 6 - mp := New(capacity, 0, false) + var ( + capacity = 6 + mp = New(capacity, 0, false, nil) + sender = transaction.Signer{Account: util.Uint160{1, 2, 3}} + maliciousSender = transaction.Signer{Account: util.Uint160{4, 5, 6}} + ) + var ( fs = &FeerStub{p2pSigExt: true, balance: 100000} nonce uint32 = 1 ) - getConflictsTx := func(netFee int64, hashes ...util.Uint256) *transaction.Transaction { + getTx := func(netFee int64, sender transaction.Signer, hashes ...util.Uint256) *transaction.Transaction { tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0) tx.NetworkFee = netFee tx.Nonce = nonce nonce++ - tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}} + tx.Signers = []transaction.Signer{sender} tx.Attributes = make([]transaction.Attribute, len(hashes)) for i, h := range hashes { tx.Attributes[i] = transaction.Attribute{ @@ -449,6 +454,12 @@ func TestMempoolAddRemoveConflicts(t *testing.T) { require.Equal(t, false, ok) return tx } + getConflictsTx := func(netFee int64, hashes ...util.Uint256) *transaction.Transaction { + return getTx(netFee, sender, hashes...) + } + getMaliciousTx := func(netFee int64, hashes ...util.Uint256) *transaction.Transaction { + return getTx(netFee, maliciousSender, hashes...) + } // tx1 in mempool and does not conflicts with anyone smallNetFee := int64(3) @@ -457,7 +468,7 @@ func TestMempoolAddRemoveConflicts(t *testing.T) { // tx2 conflicts with tx1 and has smaller netfee (Step 2, negative) tx2 := getConflictsTx(smallNetFee-1, tx1.Hash()) - require.True(t, errors.Is(mp.Add(tx2, fs), ErrConflictsAttribute)) + require.ErrorIs(t, mp.Add(tx2, fs), ErrConflictsAttribute) // tx3 conflicts with mempooled tx1 and has larger netfee => tx1 should be replaced by tx3 (Step 2, positive) tx3 := getConflictsTx(smallNetFee+1, tx1.Hash()) @@ -468,7 +479,7 @@ func TestMempoolAddRemoveConflicts(t *testing.T) { // tx1 still does not conflicts with anyone, but tx3 is mempooled, conflicts with tx1 // and has larger netfee => tx1 shouldn't be added again (Step 1, negative) - require.True(t, errors.Is(mp.Add(tx1, fs), ErrConflictsAttribute)) + require.ErrorIs(t, mp.Add(tx1, fs), ErrConflictsAttribute) // tx2 can now safely be added because conflicting tx1 is not in mempool => we // cannot check that tx2 is signed by tx1.Sender @@ -529,19 +540,89 @@ func TestMempoolAddRemoveConflicts(t *testing.T) { assert.Equal(t, []util.Uint256{tx3.Hash(), tx2.Hash()}, mp.conflicts[tx1.Hash()]) // tx13 conflicts with tx2, but is not signed by tx2.Sender - tx13 := transaction.New([]byte{byte(opcode.PUSH1)}, 0) - tx13.NetworkFee = smallNetFee - tx13.Nonce = uint32(random.Int(0, 1e4)) - tx13.Signers = []transaction.Signer{{Account: util.Uint160{3, 2, 1}}} - tx13.Attributes = []transaction.Attribute{{ - Type: transaction.ConflictsT, - Value: &transaction.Conflicts{ - Hash: tx2.Hash(), - }, - }} + tx13 := getMaliciousTx(smallNetFee, tx2.Hash()) _, ok := mp.TryGetValue(tx13.Hash()) require.Equal(t, false, ok) - require.True(t, errors.Is(mp.Add(tx13, fs), ErrConflictsAttribute)) + require.ErrorIs(t, mp.Add(tx13, fs), ErrConflictsAttribute) + + // tx15 conflicts with tx14, but added firstly and has the same network fee => tx14 must not be added. + tx14 := getConflictsTx(smallNetFee) + tx15 := getConflictsTx(smallNetFee, tx14.Hash()) + require.NoError(t, mp.Add(tx15, fs)) + err := mp.Add(tx14, fs) + require.Error(t, err) + + require.True(t, strings.Contains(err.Error(), fmt.Sprintf("conflicting transactions have bigger or equal network fee: %d vs %d", smallNetFee, smallNetFee))) + + check := func(t *testing.T, mainFee int64, fail bool) { + // Clear mempool. + mp.RemoveStale(func(t *transaction.Transaction) bool { + return false + }, fs) + + // mempooled tx17, tx18, tx19 conflict with tx16 + tx16 := getConflictsTx(mainFee) + tx17 := getConflictsTx(smallNetFee, tx16.Hash()) + tx18 := getConflictsTx(smallNetFee, tx16.Hash()) + tx19 := getMaliciousTx(smallNetFee, tx16.Hash()) // malicious, thus, doesn't take into account during fee evaluation + require.NoError(t, mp.Add(tx17, fs)) + require.NoError(t, mp.Add(tx18, fs)) + require.NoError(t, mp.Add(tx19, fs)) + if fail { + require.Error(t, mp.Add(tx16, fs)) + _, ok = mp.TryGetValue(tx17.Hash()) + require.True(t, ok) + _, ok = mp.TryGetValue(tx18.Hash()) + require.True(t, ok) + _, ok = mp.TryGetValue(tx19.Hash()) + require.True(t, ok) + } else { + require.NoError(t, mp.Add(tx16, fs)) + _, ok = mp.TryGetValue(tx17.Hash()) + require.False(t, ok) + _, ok = mp.TryGetValue(tx18.Hash()) + require.False(t, ok) + _, ok = mp.TryGetValue(tx19.Hash()) + require.False(t, ok) + } + } + check(t, smallNetFee*2, true) + check(t, smallNetFee*2+1, false) + + check = func(t *testing.T, mainFee int64, fail bool) { + // Clear mempool. + mp.RemoveStale(func(t *transaction.Transaction) bool { + return false + }, fs) + + // mempooled tx20, tx21, tx22 don't conflict with anyone, but tx23 conflicts with them + tx20 := getConflictsTx(smallNetFee) + tx21 := getConflictsTx(smallNetFee) + tx22 := getConflictsTx(smallNetFee) + tx23 := getConflictsTx(mainFee, tx20.Hash(), tx21.Hash(), tx22.Hash()) + require.NoError(t, mp.Add(tx20, fs)) + require.NoError(t, mp.Add(tx21, fs)) + require.NoError(t, mp.Add(tx22, fs)) + if fail { + require.Error(t, mp.Add(tx23, fs)) + _, ok = mp.TryGetData(tx20.Hash()) + require.True(t, ok) + _, ok = mp.TryGetData(tx21.Hash()) + require.True(t, ok) + _, ok = mp.TryGetData(tx22.Hash()) + require.True(t, ok) + } else { + require.NoError(t, mp.Add(tx23, fs)) + _, ok = mp.TryGetData(tx20.Hash()) + require.False(t, ok) + _, ok = mp.TryGetData(tx21.Hash()) + require.False(t, ok) + _, ok = mp.TryGetData(tx22.Hash()) + require.False(t, ok) + } + } + check(t, smallNetFee*3, true) + check(t, smallNetFee*3+1, false) } func TestMempoolAddWithDataGetData(t *testing.T) { @@ -555,7 +636,7 @@ func TestMempoolAddWithDataGetData(t *testing.T) { blockHeight: 5, balance: 100, } - mp := New(10, 1, false) + mp := New(10, 1, false, nil) newTx := func(t *testing.T, netFee int64) *transaction.Transaction { tx := transaction.New([]byte{byte(opcode.RET)}, 0) tx.Signers = []transaction.Signer{{}, {}} @@ -570,7 +651,7 @@ func TestMempoolAddWithDataGetData(t *testing.T) { MainTransaction: newTx(t, 0), FallbackTransaction: newTx(t, fs.balance+1), } - require.True(t, errors.Is(mp.Add(r1.FallbackTransaction, fs, r1), ErrInsufficientFunds)) + require.ErrorIs(t, mp.Add(r1.FallbackTransaction, fs, r1), ErrInsufficientFunds) // good r2 := &payload.P2PNotaryRequest{ @@ -584,7 +665,7 @@ func TestMempoolAddWithDataGetData(t *testing.T) { require.Equal(t, r2, data) // bad, already in pool - require.True(t, errors.Is(mp.Add(r2.FallbackTransaction, fs, r2), ErrDup)) + require.ErrorIs(t, mp.Add(r2.FallbackTransaction, fs, r2), ErrDup) // good, higher priority than r2. The resulting mp.verifiedTxes: [r3, r2] r3 := &payload.P2PNotaryRequest{ diff --git a/pkg/core/mempool/prometheus.go b/pkg/core/mempool/prometheus.go deleted file mode 100644 index dbe073a0d..000000000 --- a/pkg/core/mempool/prometheus.go +++ /dev/null @@ -1,24 +0,0 @@ -package mempool - -import "github.com/prometheus/client_golang/prometheus" - -var ( - //mempoolUnsortedTx prometheus metric. - mempoolUnsortedTx = prometheus.NewGauge( - prometheus.GaugeOpts{ - Help: "Mempool Unsorted TXs", - Name: "mempool_unsorted_tx", - Namespace: "neogo", - }, - ) -) - -func init() { - prometheus.MustRegister( - mempoolUnsortedTx, - ) -} - -func updateMempoolMetrics(unsortedTxnLen int) { - mempoolUnsortedTx.Set(float64(unsortedTxnLen)) -} diff --git a/pkg/core/mempool/subscriptions_test.go b/pkg/core/mempool/subscriptions_test.go index bbe1bc2a3..d476f20bc 100644 --- a/pkg/core/mempool/subscriptions_test.go +++ b/pkg/core/mempool/subscriptions_test.go @@ -13,7 +13,7 @@ import ( func TestSubscriptions(t *testing.T) { t.Run("disabled subscriptions", func(t *testing.T) { - mp := New(5, 0, false) + mp := New(5, 0, false, nil) require.Panics(t, func() { mp.RunSubscriptions() }) @@ -24,7 +24,7 @@ func TestSubscriptions(t *testing.T) { t.Run("enabled subscriptions", func(t *testing.T) { fs := &FeerStub{balance: 100} - mp := New(2, 0, true) + mp := New(2, 0, true, nil) mp.RunSubscriptions() subChan1 := make(chan mempoolevent.Event, 3) subChan2 := make(chan mempoolevent.Event, 3) diff --git a/pkg/core/mempoolevent/event.go b/pkg/core/mempoolevent/event.go index d59c07c8e..85fc8436b 100644 --- a/pkg/core/mempoolevent/event.go +++ b/pkg/core/mempoolevent/event.go @@ -21,7 +21,7 @@ const ( type Event struct { Type Type Tx *transaction.Transaction - Data interface{} + Data any } // String is a Stringer implementation. diff --git a/pkg/core/mpt/billet_test.go b/pkg/core/mpt/billet_test.go index ff77b6436..4946926e7 100644 --- a/pkg/core/mpt/billet_test.go +++ b/pkg/core/mpt/billet_test.go @@ -2,7 +2,6 @@ package mpt import ( "encoding/binary" - "errors" "testing" "github.com/nspcc-dev/neo-go/pkg/core/storage" @@ -21,7 +20,7 @@ func TestBillet_RestoreHashNode(t *testing.T) { require.NoError(t, err) require.Equal(t, expectedRefCount, binary.LittleEndian.Uint32(expectedBytes[len(expectedBytes)-4:])) } else { - require.True(t, errors.Is(err, storage.ErrKeyNotFound)) + require.ErrorIs(t, err, storage.ErrKeyNotFound) } } @@ -49,11 +48,11 @@ func TestBillet_RestoreHashNode(t *testing.T) { check(t, tr, expected, n.Node, 1) // Same path, but wrong hash => error expected, no refcount changes - require.True(t, errors.Is(tr.RestoreHashNode(path, NewBranchNode()), ErrRestoreFailed)) + require.ErrorIs(t, tr.RestoreHashNode(path, NewBranchNode()), ErrRestoreFailed) check(t, tr, expected, n.Node, 1) // New path (changes in the MPT structure are not allowed) => error expected, no refcount changes - require.True(t, errors.Is(tr.RestoreHashNode(toNibbles([]byte{0xAB}), n.Node), ErrRestoreFailed)) + require.ErrorIs(t, tr.RestoreHashNode(toNibbles([]byte{0xAB}), n.Node), ErrRestoreFailed) check(t, tr, expected, n.Node, 1) }) @@ -75,11 +74,11 @@ func TestBillet_RestoreHashNode(t *testing.T) { check(t, tr, expected, l, 1) // Same path, but wrong hash => error expected, no refcount changes - require.True(t, errors.Is(tr.RestoreHashNode(path, NewLeafNode([]byte{0xAB, 0xEF})), ErrRestoreFailed)) + require.ErrorIs(t, tr.RestoreHashNode(path, NewLeafNode([]byte{0xAB, 0xEF})), ErrRestoreFailed) check(t, tr, expected, l, 1) // New path (changes in the MPT structure are not allowed) => error expected, no refcount changes - require.True(t, errors.Is(tr.RestoreHashNode(toNibbles([]byte{0xAB}), l), ErrRestoreFailed)) + require.ErrorIs(t, tr.RestoreHashNode(toNibbles([]byte{0xAB}), l), ErrRestoreFailed) check(t, tr, expected, l, 1) }) @@ -91,7 +90,7 @@ func TestBillet_RestoreHashNode(t *testing.T) { tr.root = e // no-op - require.True(t, errors.Is(tr.RestoreHashNode(path, h), ErrRestoreFailed)) + require.ErrorIs(t, tr.RestoreHashNode(path, h), ErrRestoreFailed) check(t, tr, e, h, 0) }) }) @@ -108,10 +107,10 @@ func TestBillet_RestoreHashNode(t *testing.T) { }) // Same path, but wrong hash => error expected, no refcount changes - require.True(t, errors.Is(tr.RestoreHashNode(path, NewLeafNode([]byte{0xAB, 0xEF})), ErrRestoreFailed)) + require.ErrorIs(t, tr.RestoreHashNode(path, NewLeafNode([]byte{0xAB, 0xEF})), ErrRestoreFailed) // Non-nil path, but MPT structure can't be changed => error expected, no refcount changes - require.True(t, errors.Is(tr.RestoreHashNode(toNibbles([]byte{0xAC}), NewLeafNode([]byte{0xAB, 0xEF})), ErrRestoreFailed)) + require.ErrorIs(t, tr.RestoreHashNode(toNibbles([]byte{0xAC}), NewLeafNode([]byte{0xAB, 0xEF})), ErrRestoreFailed) }) t.Run("parent is Branch", func(t *testing.T) { @@ -138,11 +137,11 @@ func TestBillet_RestoreHashNode(t *testing.T) { check(t, tr, b, l1, 1) // Same path, but wrong hash => error expected, no refcount changes - require.True(t, errors.Is(tr.RestoreHashNode(path, NewLeafNode([]byte{0xAD})), ErrRestoreFailed)) + require.ErrorIs(t, tr.RestoreHashNode(path, NewLeafNode([]byte{0xAD})), ErrRestoreFailed) check(t, tr, b, l1, 1) // New path pointing to the empty HashNode (changes in the MPT structure are not allowed) => error expected, no refcount changes - require.True(t, errors.Is(tr.RestoreHashNode([]byte{0x01}, l1), ErrRestoreFailed)) + require.ErrorIs(t, tr.RestoreHashNode([]byte{0x01}, l1), ErrRestoreFailed) check(t, tr, b, l1, 1) }) @@ -169,7 +168,7 @@ func TestBillet_RestoreHashNode(t *testing.T) { check(t, tr, b, l2, 1) // Same path, but wrong hash => error expected, no refcount changes - require.True(t, errors.Is(tr.RestoreHashNode(path, NewLeafNode([]byte{0xAD})), ErrRestoreFailed)) + require.ErrorIs(t, tr.RestoreHashNode(path, NewLeafNode([]byte{0xAD})), ErrRestoreFailed) check(t, tr, b, l2, 1) }) diff --git a/pkg/core/mpt/empty.go b/pkg/core/mpt/empty.go index b3ceec873..7a25b0f7d 100644 --- a/pkg/core/mpt/empty.go +++ b/pkg/core/mpt/empty.go @@ -29,7 +29,7 @@ func (e EmptyNode) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements Node interface. func (e EmptyNode) UnmarshalJSON(bytes []byte) error { - var m map[string]interface{} + var m map[string]any err := json.Unmarshal(bytes, &m) if err != nil { return err diff --git a/pkg/core/mpt/extension.go b/pkg/core/mpt/extension.go index 49af8558b..c752e8aff 100644 --- a/pkg/core/mpt/extension.go +++ b/pkg/core/mpt/extension.go @@ -80,7 +80,7 @@ func (e *ExtensionNode) Size() int { // MarshalJSON implements the json.Marshaler. func (e *ExtensionNode) MarshalJSON() ([]byte, error) { - m := map[string]interface{}{ + m := map[string]any{ "key": hex.EncodeToString(e.key), "next": e.next, } diff --git a/pkg/core/native/crypto.go b/pkg/core/native/crypto.go index 0fef3d73a..b4da10fa2 100644 --- a/pkg/core/native/crypto.go +++ b/pkg/core/native/crypto.go @@ -5,7 +5,9 @@ import ( "encoding/binary" "errors" "fmt" + "math/big" + "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/decred/dcrd/dcrec/secp256k1/v4" "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/interop" @@ -62,6 +64,42 @@ func newCrypto() *Crypto { manifest.NewParameter("curve", smartcontract.IntegerType)) md = newMethodAndPrice(c.verifyWithECDsa, 1<<15, callflag.NoneFlag) c.AddMethod(md, desc) + + desc = newDescriptor("bls12381Serialize", smartcontract.ByteArrayType, + manifest.NewParameter("g", smartcontract.InteropInterfaceType)) + md = newMethodAndPrice(c.bls12381Serialize, 1<<19, callflag.NoneFlag) + c.AddMethod(md, desc) + + desc = newDescriptor("bls12381Deserialize", smartcontract.InteropInterfaceType, + manifest.NewParameter("data", smartcontract.ByteArrayType)) + md = newMethodAndPrice(c.bls12381Deserialize, 1<<19, callflag.NoneFlag) + c.AddMethod(md, desc) + + desc = newDescriptor("bls12381Equal", smartcontract.BoolType, + manifest.NewParameter("x", smartcontract.InteropInterfaceType), + manifest.NewParameter("y", smartcontract.InteropInterfaceType)) + md = newMethodAndPrice(c.bls12381Equal, 1<<5, callflag.NoneFlag) + c.AddMethod(md, desc) + + desc = newDescriptor("bls12381Add", smartcontract.InteropInterfaceType, + manifest.NewParameter("x", smartcontract.InteropInterfaceType), + manifest.NewParameter("y", smartcontract.InteropInterfaceType)) + md = newMethodAndPrice(c.bls12381Add, 1<<19, callflag.NoneFlag) + c.AddMethod(md, desc) + + desc = newDescriptor("bls12381Mul", smartcontract.InteropInterfaceType, + manifest.NewParameter("x", smartcontract.InteropInterfaceType), + manifest.NewParameter("mul", smartcontract.ByteArrayType), + manifest.NewParameter("neg", smartcontract.BoolType)) + md = newMethodAndPrice(c.bls12381Mul, 1<<21, callflag.NoneFlag) + c.AddMethod(md, desc) + + desc = newDescriptor("bls12381Pairing", smartcontract.InteropInterfaceType, + manifest.NewParameter("g1", smartcontract.InteropInterfaceType), + manifest.NewParameter("g2", smartcontract.InteropInterfaceType)) + md = newMethodAndPrice(c.bls12381Pairing, 1<<23, callflag.NoneFlag) + c.AddMethod(md, desc) + return c } @@ -138,6 +176,113 @@ func curveFromStackitem(si stackitem.Item) (elliptic.Curve, error) { } } +func (c *Crypto) bls12381Serialize(_ *interop.Context, args []stackitem.Item) stackitem.Item { + val, ok := args[0].(*stackitem.Interop).Value().(blsPoint) + if !ok { + panic(errors.New("not a bls12381 point")) + } + return stackitem.NewByteArray(val.Bytes()) +} + +func (c *Crypto) bls12381Deserialize(_ *interop.Context, args []stackitem.Item) stackitem.Item { + buf, err := args[0].TryBytes() + if err != nil { + panic(fmt.Errorf("invalid serialized bls12381 point: %w", err)) + } + p := new(blsPoint) + err = p.FromBytes(buf) + if err != nil { + panic(err) + } + return stackitem.NewInterop(*p) +} + +func (c *Crypto) bls12381Equal(_ *interop.Context, args []stackitem.Item) stackitem.Item { + a, okA := args[0].(*stackitem.Interop).Value().(blsPoint) + b, okB := args[1].(*stackitem.Interop).Value().(blsPoint) + if !(okA && okB) { + panic("some of the arguments are not a bls12381 point") + } + res, err := a.EqualsCheckType(b) + if err != nil { + panic(err) + } + return stackitem.NewBool(res) +} + +func (c *Crypto) bls12381Add(_ *interop.Context, args []stackitem.Item) stackitem.Item { + a, okA := args[0].(*stackitem.Interop).Value().(blsPoint) + b, okB := args[1].(*stackitem.Interop).Value().(blsPoint) + if !(okA && okB) { + panic("some of the arguments are not a bls12381 point") + } + + p, err := blsPointAdd(a, b) + if err != nil { + panic(err) + } + return stackitem.NewInterop(p) +} + +func scalarFromBytes(bytes []byte, neg bool) (*fr.Element, error) { + alpha := new(fr.Element) + if len(bytes) != fr.Bytes { + return nil, fmt.Errorf("invalid multiplier: 32-bytes scalar is expected, got %d", len(bytes)) + } + // The input bytes are in the LE form, so we can't use fr.Element.SetBytesCanonical as far + // as it accepts BE. + v, err := fr.LittleEndian.Element((*[fr.Bytes]byte)(bytes)) + if err != nil { + return nil, fmt.Errorf("invalid multiplier: failed to decode scalar: %w", err) + } + *alpha = v + if neg { + alpha.Neg(alpha) + } + return alpha, nil +} + +func (c *Crypto) bls12381Mul(_ *interop.Context, args []stackitem.Item) stackitem.Item { + a, okA := args[0].(*stackitem.Interop).Value().(blsPoint) + if !okA { + panic("multiplier is not a bls12381 point") + } + mulBytes, err := args[1].TryBytes() + if err != nil { + panic(fmt.Errorf("invalid multiplier: %w", err)) + } + neg, err := args[2].TryBool() + if err != nil { + panic(fmt.Errorf("invalid negative argument: %w", err)) + } + alpha, err := scalarFromBytes(mulBytes, neg) + if err != nil { + panic(err) + } + alphaBi := new(big.Int) + alpha.BigInt(alphaBi) + + p, err := blsPointMul(a, alphaBi) + if err != nil { + panic(err) + } + return stackitem.NewInterop(p) +} + +func (c *Crypto) bls12381Pairing(_ *interop.Context, args []stackitem.Item) stackitem.Item { + a, okA := args[0].(*stackitem.Interop).Value().(blsPoint) + b, okB := args[1].(*stackitem.Interop).Value().(blsPoint) + if !(okA && okB) { + panic("some of the arguments are not a bls12381 point") + } + + p, err := blsPointPairing(a, b) + if err != nil { + panic(err) + } + return stackitem.NewInterop(p) +} + // Metadata implements the Contract interface. func (c *Crypto) Metadata() *interop.ContractMD { return &c.ContractMD diff --git a/pkg/core/native/crypto_blspoints.go b/pkg/core/native/crypto_blspoints.go new file mode 100644 index 000000000..317d9b795 --- /dev/null +++ b/pkg/core/native/crypto_blspoints.go @@ -0,0 +1,293 @@ +package native + +import ( + "errors" + "fmt" + "math/big" + + bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" +) + +// blsPoint is a wrapper around bls12381 point types that must be used as +// stackitem.Interop values and implement stackitem.Equatable interface. +type blsPoint struct { + point any +} + +var _ = stackitem.Equatable(blsPoint{}) + +// Equals implements stackitem.Equatable interface. +func (p blsPoint) Equals(other stackitem.Equatable) bool { + res, err := p.EqualsCheckType(other) + return err == nil && res +} + +// EqualsCheckType checks whether other is of the same type as p and returns an error if not. +// It also returns whether other and p are equal. +func (p blsPoint) EqualsCheckType(other stackitem.Equatable) (bool, error) { + b, ok := other.(blsPoint) + if !ok { + return false, errors.New("not a bls12-381 point") + } + var ( + res bool + err error + ) + switch x := p.point.(type) { + case *bls12381.G1Affine: + y, ok := b.point.(*bls12381.G1Affine) + if !ok { + err = fmt.Errorf("equal: unexpected y bls12381 point type: %T vs G1Affine", y) + break + } + res = x.Equal(y) + case *bls12381.G1Jac: + y, ok := b.point.(*bls12381.G1Jac) + if !ok { + err = fmt.Errorf("equal: unexpected y bls12381 point type: %T vs G1Jac", y) + break + } + res = x.Equal(y) + case *bls12381.G2Affine: + y, ok := b.point.(*bls12381.G2Affine) + if !ok { + err = fmt.Errorf("equal: unexpected y bls12381 point type: %T vs G2Affine", y) + break + } + res = x.Equal(y) + case *bls12381.G2Jac: + y, ok := b.point.(*bls12381.G2Jac) + if !ok { + err = fmt.Errorf("equal: unexpected y bls12381 point type: %T vs G2Jac", y) + break + } + res = x.Equal(y) + case *bls12381.GT: + y, ok := b.point.(*bls12381.GT) + if !ok { + err = fmt.Errorf("equal: unexpected y bls12381 point type: %T vs GT", y) + break + } + res = x.Equal(y) + default: + err = fmt.Errorf("equal: unexpected x bls12381 point type: %T", x) + } + + return res, err +} + +// Bytes returns serialized representation of the provided point in compressed form. +func (p blsPoint) Bytes() []byte { + switch p := p.point.(type) { + case *bls12381.G1Affine: + compressed := p.Bytes() + return compressed[:] + case *bls12381.G1Jac: + g1Affine := new(bls12381.G1Affine) + g1Affine.FromJacobian(p) + compressed := g1Affine.Bytes() + return compressed[:] + case *bls12381.G2Affine: + compressed := p.Bytes() + return compressed[:] + case *bls12381.G2Jac: + g2Affine := new(bls12381.G2Affine) + g2Affine.FromJacobian(p) + compressed := g2Affine.Bytes() + return compressed[:] + case *bls12381.GT: + compressed := p.Bytes() + return compressed[:] + default: + panic(errors.New("unknown bls12381 point type")) + } +} + +// FromBytes deserializes BLS12-381 point from the given byte slice in compressed form. +func (p *blsPoint) FromBytes(buf []byte) error { + switch l := len(buf); l { + case bls12381.SizeOfG1AffineCompressed: + g1Affine := new(bls12381.G1Affine) + _, err := g1Affine.SetBytes(buf) + if err != nil { + return fmt.Errorf("failed to decode bls12381 G1Affine point: %w", err) + } + p.point = g1Affine + case bls12381.SizeOfG2AffineCompressed: + g2Affine := new(bls12381.G2Affine) + _, err := g2Affine.SetBytes(buf) + if err != nil { + return fmt.Errorf("failed to decode bls12381 G2Affine point: %w", err) + } + p.point = g2Affine + case bls12381.SizeOfGT: + gt := new(bls12381.GT) + err := gt.SetBytes(buf) + if err != nil { + return fmt.Errorf("failed to decode GT point: %w", err) + } + p.point = gt + } + + return nil +} + +// blsPointAdd performs addition of two BLS12-381 points. +func blsPointAdd(a, b blsPoint) (blsPoint, error) { + var ( + res any + err error + ) + switch x := a.point.(type) { + case *bls12381.G1Affine: + switch y := b.point.(type) { + case *bls12381.G1Affine: + xJac := new(bls12381.G1Jac) + xJac.FromAffine(x) + xJac.AddMixed(y) + res = xJac + case *bls12381.G1Jac: + yJac := new(bls12381.G1Jac) + yJac.Set(y) + yJac.AddMixed(x) + res = yJac + default: + err = fmt.Errorf("add: inconsistent bls12381 point types: %T and %T", x, y) + } + case *bls12381.G1Jac: + resJac := new(bls12381.G1Jac) + resJac.Set(x) + switch y := b.point.(type) { + case *bls12381.G1Affine: + resJac.AddMixed(y) + case *bls12381.G1Jac: + resJac.AddAssign(y) + default: + err = fmt.Errorf("add: inconsistent bls12381 point types: %T and %T", x, y) + } + res = resJac + case *bls12381.G2Affine: + switch y := b.point.(type) { + case *bls12381.G2Affine: + xJac := new(bls12381.G2Jac) + xJac.FromAffine(x) + xJac.AddMixed(y) + res = xJac + case *bls12381.G2Jac: + yJac := new(bls12381.G2Jac) + yJac.Set(y) + yJac.AddMixed(x) + res = yJac + default: + err = fmt.Errorf("add: inconsistent bls12381 point types: %T and %T", x, y) + } + case *bls12381.G2Jac: + resJac := new(bls12381.G2Jac) + resJac.Set(x) + switch y := b.point.(type) { + case *bls12381.G2Affine: + resJac.AddMixed(y) + case *bls12381.G2Jac: + resJac.AddAssign(y) + default: + err = fmt.Errorf("add: inconsistent bls12381 point types: %T and %T", x, y) + } + res = resJac + case *bls12381.GT: + resGT := new(bls12381.GT) + resGT.Set(x) + switch y := b.point.(type) { + case *bls12381.GT: + // It's multiplication, see https://github.com/neo-project/Neo.Cryptography.BLS12_381/issues/4. + resGT.Mul(x, y) + default: + err = fmt.Errorf("add: inconsistent bls12381 point types: %T and %T", x, y) + } + res = resGT + default: + err = fmt.Errorf("add: unexpected bls12381 point type: %T", x) + } + + return blsPoint{point: res}, err +} + +// blsPointAdd performs scalar multiplication of two BLS12-381 points. +func blsPointMul(a blsPoint, alphaBi *big.Int) (blsPoint, error) { + var ( + res any + err error + ) + switch x := a.point.(type) { + case *bls12381.G1Affine: + // The result is in Jacobian form in the reference implementation. + g1Jac := new(bls12381.G1Jac) + g1Jac.FromAffine(x) + g1Jac.ScalarMultiplication(g1Jac, alphaBi) + res = g1Jac + case *bls12381.G1Jac: + g1Jac := new(bls12381.G1Jac) + g1Jac.ScalarMultiplication(x, alphaBi) + res = g1Jac + case *bls12381.G2Affine: + // The result is in Jacobian form in the reference implementation. + g2Jac := new(bls12381.G2Jac) + g2Jac.FromAffine(x) + g2Jac.ScalarMultiplication(g2Jac, alphaBi) + res = g2Jac + case *bls12381.G2Jac: + g2Jac := new(bls12381.G2Jac) + g2Jac.ScalarMultiplication(x, alphaBi) + res = g2Jac + case *bls12381.GT: + gt := new(bls12381.GT) + + // C# implementation differs a bit from go's. They use double-and-add algorithm, see + // https://github.com/neo-project/Neo.Cryptography.BLS12_381/blob/844bc3a4f7d8ba2c545ace90ca124f8ada4c8d29/src/Neo.Cryptography.BLS12_381/Gt.cs#L102 + // and https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Double-and-add, + // Pay attention that C#'s Gt.Double() squares (not doubles!) the initial GT point. + // Thus.C#'s scalar multiplication operation over Gt and Scalar is effectively an exponent. + // Go's exponent algorithm differs a bit from the C#'s double-and-add in that go's one + // uses 2-bits windowed method for multiplication. However, the resulting GT point is + // absolutely the same between two implementations. + gt.Exp(*x, alphaBi) + + res = gt + default: + err = fmt.Errorf("mul: unexpected bls12381 point type: %T", x) + } + + return blsPoint{point: res}, err +} + +func blsPointPairing(a, b blsPoint) (blsPoint, error) { + var ( + x *bls12381.G1Affine + y *bls12381.G2Affine + ) + switch p := a.point.(type) { + case *bls12381.G1Affine: + x = p + case *bls12381.G1Jac: + x = new(bls12381.G1Affine) + x.FromJacobian(p) + default: + return blsPoint{}, fmt.Errorf("pairing: unexpected bls12381 point type (g1): %T", x) + } + switch p := b.point.(type) { + case *bls12381.G2Affine: + y = p + case *bls12381.G2Jac: + y = new(bls12381.G2Affine) + y.FromJacobian(p) + default: + return blsPoint{}, fmt.Errorf("pairing: unexpected bls12381 point type (g2): %T", x) + } + + gt, err := bls12381.Pair([]bls12381.G1Affine{*x}, []bls12381.G2Affine{*y}) + if err != nil { + return blsPoint{}, fmt.Errorf("failed to perform pairing operation: %w", err) + } + + return blsPoint{>}, nil +} diff --git a/pkg/core/native/crypto_test.go b/pkg/core/native/crypto_test.go index 727c160a9..47688e2c2 100644 --- a/pkg/core/native/crypto_test.go +++ b/pkg/core/native/crypto_test.go @@ -7,6 +7,7 @@ import ( "math/big" "testing" + "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/vm" @@ -91,7 +92,7 @@ func testECDSAVerify(t *testing.T, curve NamedCurve) { } require.NoError(t, err) - runCase := func(t *testing.T, isErr bool, result interface{}, args ...interface{}) { + runCase := func(t *testing.T, isErr bool, result any, args ...any) { argsArr := make([]stackitem.Item, len(args)) for i := range args { argsArr[i] = stackitem.Make(args[i]) @@ -141,3 +142,63 @@ func testECDSAVerify(t *testing.T, curve NamedCurve) { runCase(t, false, true, msg, priv.PublicKey().Bytes(), sign, int64(curve)) }) } + +func TestCryptolib_ScalarFromBytes_Compat(t *testing.T) { + r2Ref := &fr.Element{ + 0xc999_e990_f3f2_9c6d, + 0x2b6c_edcb_8792_5c23, + 0x05d3_1496_7254_398f, + 0x0748_d9d9_9f59_ff11, + } // R2 Scalar representation taken from the https://github.com/neo-project/Neo.Cryptography.BLS12_381/blob/844bc3a4f7d8ba2c545ace90ca124f8ada4c8d29/src/Neo.Cryptography.BLS12_381/ScalarConstants.cs#L55 + + tcs := map[string]struct { + bytes []byte + expected *fr.Element + shouldFail bool + }{ + "zero": { + bytes: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + expected: new(fr.Element).SetZero(), + }, + "one": { + bytes: []byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + expected: new(fr.Element).SetOne(), + }, + "R2": { + bytes: []byte{254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24}, + expected: r2Ref, + }, + "negative": { + bytes: []byte{0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115}, + }, + "modulus": { + bytes: []byte{1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115}, + shouldFail: true, + }, + "larger than modulus": { + bytes: []byte{2, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115}, + shouldFail: true, + }, + "larger than modulus 2": { + bytes: []byte{1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 58, 51, 72, 125, 157, 41, 83, 167, 237, 115}, + shouldFail: true, + }, + "larger than modulus 3": { + bytes: []byte{1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 116}, + shouldFail: true, + }, + } + for name, tc := range tcs { + t.Run(name, func(t *testing.T) { + actual, err := scalarFromBytes(tc.bytes, false) + if tc.shouldFail { + require.Error(t, err) + } else { + require.NoError(t, err) + if tc.expected != nil { + require.Equal(t, tc.expected, actual) + } + } + }) + } +} diff --git a/pkg/core/native/management.go b/pkg/core/native/management.go index f7148f8b5..24f88e076 100644 --- a/pkg/core/native/management.go +++ b/pkg/core/native/management.go @@ -280,7 +280,7 @@ func (m *Management) getContractHashes(ic *interop.Context, _ []stackitem.Item) item := istorage.NewIterator(filteredRes, prefix, int64(opts)) ic.RegisterCancelFunc(func() { cancel() - for range seekres { + for range seekres { //nolint:revive //empty-block } }) return stackitem.NewInterop(item) @@ -686,10 +686,8 @@ func putContractState(d *dao.Simple, cs *state.Contract, updateCache bool) error if cs.UpdateCounter != 0 { // Update. return nil } - if cs.ID > 0 { - key = putHashKey(key, cs.ID) - d.PutStorageItem(ManagementContractID, key, cs.Hash.BytesBE()) - } + key = putHashKey(key, cs.ID) + d.PutStorageItem(ManagementContractID, key, cs.Hash.BytesBE()) return nil } diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index 9014047f5..6c8742dbb 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -410,7 +410,7 @@ func (n *NEO) PostPersist(ic *interop.Context) error { var ( cs = cache.committee isCacheRW bool - key = make([]byte, 38) + key = make([]byte, 34) ) for i := range cs { if cs[i].Votes.Sign() > 0 { @@ -424,17 +424,9 @@ func (n *NEO) PostPersist(ic *interop.Context) error { tmp.Div(tmp, cs[i].Votes) key = makeVoterKey([]byte(cs[i].Key), key) + r := n.getLatestGASPerVote(ic.DAO, key) + tmp.Add(tmp, &r) - var r *big.Int - if g, ok := cache.gasPerVoteCache[cs[i].Key]; ok { - r = &g - } else { - reward := n.getGASPerVote(ic.DAO, key[:34], []uint32{ic.Block.Index + 1}) - r = &reward[0] - } - tmp.Add(tmp, r) - - binary.BigEndian.PutUint32(key[34:], ic.Block.Index+1) if !isCacheRW { cache = ic.DAO.GetRWCache(n.ID).(*NeoCache) isCacheRW = true @@ -448,33 +440,19 @@ func (n *NEO) PostPersist(ic *interop.Context) error { return nil } -func (n *NEO) getGASPerVote(d *dao.Simple, key []byte, indexes []uint32) []big.Int { - sort.Slice(indexes, func(i, j int) bool { - return indexes[i] < indexes[j] - }) - start := make([]byte, 4) - binary.BigEndian.PutUint32(start, indexes[len(indexes)-1]) - - need := len(indexes) - var reward = make([]big.Int, need) - collected := 0 - d.Seek(n.ID, storage.SeekRange{ - Prefix: key, - Start: start, - Backwards: true, - }, func(k, v []byte) bool { - if len(k) == 4 { - num := binary.BigEndian.Uint32(k) - for i, ind := range indexes { - if reward[i].Sign() == 0 && num <= ind { - reward[i] = *bigint.FromBytes(v) - collected++ - } - } - } - return collected < need - }) - return reward +func (n *NEO) getLatestGASPerVote(d *dao.Simple, key []byte) big.Int { + var g big.Int + cache := d.GetROCache(n.ID).(*NeoCache) + if g, ok := cache.gasPerVoteCache[string(key[1:])]; ok { + return g + } + item := d.GetStorageItem(n.ID, key) + if item == nil { + g = *big.NewInt(0) + } else { + g = *bigint.FromBytes(item) + } + return g } func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.StorageItem, amount *big.Int, checkBal *big.Int) (func(), error) { @@ -528,11 +506,15 @@ func (n *NEO) distributeGas(ic *interop.Context, acc *state.NEOBalance) (*big.In if ic.Block == nil || ic.Block.Index == 0 || ic.Block.Index == acc.BalanceHeight { return nil, nil } - gen, err := n.calculateBonus(ic.DAO, acc.VoteTo, &acc.Balance, acc.BalanceHeight, ic.Block.Index) + gen, err := n.calculateBonus(ic.DAO, acc, ic.Block.Index) if err != nil { return nil, err } acc.BalanceHeight = ic.Block.Index + if acc.VoteTo != nil { + latestGasPerVote := n.getLatestGASPerVote(ic.DAO, makeVoterKey(acc.VoteTo.Bytes())) + acc.LastGasPerVote = latestGasPerVote + } return gen, nil } @@ -540,7 +522,7 @@ func (n *NEO) distributeGas(ic *interop.Context, acc *state.NEOBalance) (*big.In func (n *NEO) unclaimedGas(ic *interop.Context, args []stackitem.Item) stackitem.Item { u := toUint160(args[0]) end := uint32(toBigInt(args[1]).Int64()) - gen, err := n.CalculateBonus(ic.DAO, u, end) + gen, err := n.CalculateBonus(ic, u, end) if err != nil { panic(err) } @@ -648,10 +630,7 @@ func (n *NEO) dropCandidateIfZero(d *dao.Simple, cache *NeoCache, pub *keys.Publ d.DeleteStorageItem(n.ID, makeValidatorKey(pub)) voterKey := makeVoterKey(pub.Bytes()) - d.Seek(n.ID, storage.SeekRange{Prefix: voterKey}, func(k, v []byte) bool { - d.DeleteStorageItem(n.ID, append(voterKey, k...)) // d.Seek cuts prefix, thus need to append it again. - return true - }) + d.DeleteStorageItem(n.ID, voterKey) delete(cache.gasPerVoteCache, string(voterKey)) return true @@ -662,7 +641,7 @@ func makeVoterKey(pub []byte, prealloc ...[]byte) []byte { if len(prealloc) != 0 { key = prealloc[0] } else { - key = make([]byte, 34, 38) + key = make([]byte, 34) } key[0] = prefixVoterRewardPerCommittee copy(key[1:], pub) @@ -671,9 +650,12 @@ func makeVoterKey(pub []byte, prealloc ...[]byte) []byte { // CalculateBonus calculates amount of gas generated for holding value NEO from start to end block // and having voted for active committee member. -func (n *NEO) CalculateBonus(d *dao.Simple, acc util.Uint160, end uint32) (*big.Int, error) { +func (n *NEO) CalculateBonus(ic *interop.Context, acc util.Uint160, end uint32) (*big.Int, error) { + if ic.Block == nil || end != ic.Block.Index { + return nil, errors.New("can't calculate bonus of height unequal (BlockHeight + 1)") + } key := makeAccountKey(acc) - si := d.GetStorageItem(n.ID, key) + si := ic.DAO.GetStorageItem(n.ID, key) if si == nil { return nil, storage.ErrKeyNotFound } @@ -681,19 +663,19 @@ func (n *NEO) CalculateBonus(d *dao.Simple, acc util.Uint160, end uint32) (*big. if err != nil { return nil, err } - return n.calculateBonus(d, st.VoteTo, &st.Balance, st.BalanceHeight, end) + return n.calculateBonus(ic.DAO, st, end) } -func (n *NEO) calculateBonus(d *dao.Simple, vote *keys.PublicKey, value *big.Int, start, end uint32) (*big.Int, error) { - r, err := n.CalculateNEOHolderReward(d, value, start, end) - if err != nil || vote == nil { +func (n *NEO) calculateBonus(d *dao.Simple, acc *state.NEOBalance, end uint32) (*big.Int, error) { + r, err := n.CalculateNEOHolderReward(d, &acc.Balance, acc.BalanceHeight, end) + if err != nil || acc.VoteTo == nil { return r, err } - var key = makeVoterKey(vote.Bytes()) - var reward = n.getGASPerVote(d, key, []uint32{start, end}) - var tmp = (&reward[1]).Sub(&reward[1], &reward[0]) - tmp.Mul(tmp, value) + var key = makeVoterKey(acc.VoteTo.Bytes()) + var reward = n.getLatestGASPerVote(d, key) + var tmp = big.NewInt(0).Sub(&reward, &acc.LastGasPerVote) + tmp.Mul(tmp, &acc.Balance) tmp.Div(tmp, bigVoterRewardFactor) tmp.Add(tmp, r) return tmp, nil @@ -870,6 +852,9 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pub *keys.Public if err := n.ModifyAccountVotes(acc, ic.DAO, new(big.Int).Neg(&acc.Balance), false); err != nil { return err } + if pub != nil && pub != acc.VoteTo { + acc.LastGasPerVote = n.getLatestGASPerVote(ic.DAO, makeVoterKey(pub.Bytes())) + } oldVote := acc.VoteTo acc.VoteTo = pub if err := n.ModifyAccountVotes(acc, ic.DAO, &acc.Balance, true); err != nil { @@ -1023,7 +1008,7 @@ func (n *NEO) getAllCandidatesCall(ic *interop.Context, _ []stackitem.Item) stac item := istorage.NewIterator(filteredRes, prefix, int64(opts)) ic.RegisterCancelFunc(func() { cancel() - for range seekres { + for range seekres { //nolint:revive //empty-block } }) return stackitem.NewInterop(item) diff --git a/pkg/core/native/native_test/common_test.go b/pkg/core/native/native_test/common_test.go index 53a927a52..4957291f6 100644 --- a/pkg/core/native/native_test/common_test.go +++ b/pkg/core/native/native_test/common_test.go @@ -113,7 +113,7 @@ func testGetSetCache(t *testing.T, c *neotest.ContractInvoker, name string, defa } func setNodesByRole(t *testing.T, designateInvoker *neotest.ContractInvoker, ok bool, r noderoles.Role, nodes keys.PublicKeys) { - pubs := make([]interface{}, len(nodes)) + pubs := make([]any, len(nodes)) for i := range nodes { pubs[i] = nodes[i].Bytes() } diff --git a/pkg/core/native/native_test/cryptolib_test.go b/pkg/core/native/native_test/cryptolib_test.go new file mode 100644 index 000000000..69bed9325 --- /dev/null +++ b/pkg/core/native/native_test/cryptolib_test.go @@ -0,0 +1,422 @@ +package native_test + +import ( + "encoding/hex" + "strconv" + "strings" + "testing" + + "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" + "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/neotest" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" + "github.com/nspcc-dev/neo-go/pkg/vm/emit" + "github.com/nspcc-dev/neo-go/pkg/vm/opcode" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/stretchr/testify/require" +) + +// A set of points taken from the reference implementation for compatibility tests. +var ( + g1 []byte + g2 []byte + gt []byte + notG1, notG2 []byte +) + +func init() { + g1, _ = hex.DecodeString("97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb") + g2, _ = hex.DecodeString("93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8") + gt, _ = hex.DecodeString("0f41e58663bf08cf068672cbd01a7ec73baca4d72ca93544deff686bfd6df543d48eaa24afe47e1efde449383b67663104c581234d086a9902249b64728ffd21a189e87935a954051c7cdba7b3872629a4fafc05066245cb9108f0242d0fe3ef03350f55a7aefcd3c31b4fcb6ce5771cc6a0e9786ab5973320c806ad360829107ba810c5a09ffdd9be2291a0c25a99a211b8b424cd48bf38fcef68083b0b0ec5c81a93b330ee1a677d0d15ff7b984e8978ef48881e32fac91b93b47333e2ba5706fba23eb7c5af0d9f80940ca771b6ffd5857baaf222eb95a7d2809d61bfe02e1bfd1b68ff02f0b8102ae1c2d5d5ab1a19f26337d205fb469cd6bd15c3d5a04dc88784fbb3d0b2dbdea54d43b2b73f2cbb12d58386a8703e0f948226e47ee89d018107154f25a764bd3c79937a45b84546da634b8f6be14a8061e55cceba478b23f7dacaa35c8ca78beae9624045b4b601b2f522473d171391125ba84dc4007cfbf2f8da752f7c74185203fcca589ac719c34dffbbaad8431dad1c1fb597aaa5193502b86edb8857c273fa075a50512937e0794e1e65a7617c90d8bd66065b1fffe51d7a579973b1315021ec3c19934f1368bb445c7c2d209703f239689ce34c0378a68e72a6b3b216da0e22a5031b54ddff57309396b38c881c4c849ec23e87089a1c5b46e5110b86750ec6a532348868a84045483c92b7af5af689452eafabf1a8943e50439f1d59882a98eaa0170f1250ebd871fc0a92a7b2d83168d0d727272d441befa15c503dd8e90ce98db3e7b6d194f60839c508a84305aaca1789b6") + notG1, _ = hex.DecodeString("8123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef") + notG2, _ = hex.DecodeString("8123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef") +} + +func newCryptolibClient(t *testing.T) *neotest.ContractInvoker { + return newNativeClient(t, nativenames.CryptoLib) +} + +type serializable interface { + Bytes() []byte +} + +func TestCryptolib_TestG1_Compat(t *testing.T) { + c := newCryptolibClient(t) + cryptoInvoker := c.WithSigners(c.Committee) + + // Use testinvoke because the return value of this method is InteropInterface. + stack, err := cryptoInvoker.TestInvoke(t, "bls12381Deserialize", g1) + require.NoError(t, err) + require.Equal(t, 1, stack.Len()) + itm := stack.Pop().Item() + require.Equal(t, stackitem.InteropT, itm.Type()) + actual, ok := itm.(*stackitem.Interop).Value().(serializable) + require.True(t, ok) + arr := actual.Bytes() // the G1Affine result in compressed form. + // Expected value is taken from the reference test. + require.Equal(t, "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + hex.EncodeToString(arr[:])) +} + +func TestCryptolib_TestG2_Compat(t *testing.T) { + c := newCryptolibClient(t) + cryptoInvoker := c.WithSigners(c.Committee) + + // Use testinvoke because the return value of this method is InteropInterface. + stack, err := cryptoInvoker.TestInvoke(t, "bls12381Deserialize", g2) + require.NoError(t, err) + require.Equal(t, 1, stack.Len()) + itm := stack.Pop().Item() + require.Equal(t, stackitem.InteropT, itm.Type()) + actual, ok := itm.(*stackitem.Interop).Value().(serializable) + require.True(t, ok) + arr := actual.Bytes() // the result G2Affine in compressed form. + // Expected value is taken from the reference test. + require.Equal(t, "93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8", + hex.EncodeToString(arr[:])) +} + +func TestCryptolib_TestNotG1_Compat(t *testing.T) { + c := newCryptolibClient(t) + cryptoInvoker := c.WithSigners(c.Committee) + + cryptoInvoker.InvokeFail(t, "failed to decode bls12381 G1Affine point: invalid point: subgroup check failed", "bls12381Deserialize", notG1) +} + +func TestCryptolib_TestNotG2_Compat(t *testing.T) { + c := newCryptolibClient(t) + cryptoInvoker := c.WithSigners(c.Committee) + + cryptoInvoker.InvokeFail(t, "failed to decode bls12381 G2Affine point: invalid point: subgroup check failed", "bls12381Deserialize", notG2) +} + +func TestCryptolib_TestBls12381Add_Compat(t *testing.T) { + c := newCryptolibClient(t) + + script := io.NewBufBinWriter() + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, gt) + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, gt) + emit.Opcodes(script.BinWriter, opcode.PUSH2, opcode.PACK) + emit.AppCallNoArgs(script.BinWriter, c.Hash, "bls12381Add", callflag.All) + + stack, err := c.TestInvokeScript(t, script.Bytes(), c.Signers) + require.NoError(t, err) + require.Equal(t, 1, stack.Len()) + itm := stack.Pop().Item() + require.Equal(t, stackitem.InteropT, itm.Type()) + actual, ok := itm.(*stackitem.Interop).Value().(serializable) + require.True(t, ok) + arr := actual.Bytes() + // Expected GT value is taken from the reference test. + require.Equal(t, strings.ToLower("079AB7B345EB23C944C957A36A6B74C37537163D4CBF73BAD9751DE1DD9C68EF72CB21447E259880F72A871C3EDA1B0C017F1C95CF79B22B459599EA57E613E00CB75E35DE1F837814A93B443C54241015AC9761F8FB20A44512FF5CFC04AC7F0F6B8B52B2B5D0661CBF232820A257B8C5594309C01C2A45E64C6A7142301E4FB36E6E16B5A85BD2E437599D103C3ACE06D8046C6B3424C4CD2D72CE98D279F2290A28A87E8664CB0040580D0C485F34DF45267F8C215DCBCD862787AB555C7E113286DEE21C9C63A458898BEB35914DC8DAAAC453441E7114B21AF7B5F47D559879D477CF2A9CBD5B40C86BECD071280900410BB2751D0A6AF0FE175DCF9D864ECAAC463C6218745B543F9E06289922434EE446030923A3E4C4473B4E3B1914081ABD33A78D31EB8D4C1BB3BAAB0529BB7BAF1103D848B4CEAD1A8E0AA7A7B260FBE79C67DBE41CA4D65BA8A54A72B61692A61CE5F4D7A093B2C46AA4BCA6C4A66CF873D405EBC9C35D8AA639763720177B23BEFFAF522D5E41D3C5310EA3331409CEBEF9EF393AA00F2AC64673675521E8FC8FDDAF90976E607E62A740AC59C3DDDF95A6DE4FBA15BEB30C43D4E3F803A3734DBEB064BF4BC4A03F945A4921E49D04AB8D45FD753A28B8FA082616B4B17BBCB685E455FF3BF8F60C3BD32A0C185EF728CF41A1B7B700B7E445F0B372BC29E370BC227D443C70AE9DBCF73FEE8ACEDBD317A286A53266562D817269C004FB0F149DD925D2C590A960936763E519C2B62E14C7759F96672CD852194325904197B0B19C6B528AB33566946AF39B"), + hex.EncodeToString(arr[:])) +} + +func TestCryptolib_TestBls12381Mul_Compat(t *testing.T) { + c := newCryptolibClient(t) + + check := func(t *testing.T, neg bool, expected string) { + data := make([]byte, fr.Bytes) + data[0] = 0x03 + script := io.NewBufBinWriter() + if neg { + emit.Opcodes(script.BinWriter, opcode.PUSHT) + } else { + emit.Opcodes(script.BinWriter, opcode.PUSHF) + } + emit.Bytes(script.BinWriter, data) + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, gt) + emit.Opcodes(script.BinWriter, opcode.PUSH3, opcode.PACK) + emit.AppCallNoArgs(script.BinWriter, c.Hash, "bls12381Mul", callflag.All) + + stack, err := c.TestInvokeScript(t, script.Bytes(), c.Signers) + require.NoError(t, err) + require.Equal(t, 1, stack.Len()) + itm := stack.Pop().Item() + require.Equal(t, stackitem.InteropT, itm.Type()) + actual, ok := itm.(*stackitem.Interop).Value().(serializable) + require.True(t, ok) + arr := actual.Bytes() + // Expected GT value is taken from the reference test. + require.Equal(t, strings.ToLower(expected), hex.EncodeToString(arr[:])) + } + check(t, false, "18B2DB6B3286BAEA116CCAD8F5554D170A69B329A6DE5B24C50B8834965242001A1C58089FD872B211ACD3263897FA660B117248D69D8AC745283A3E6A4CCEC607F6CF7CEDEE919575D4B7C8AE14C36001F76BE5FCA50ADC296EF8DF4926FA7F0B55A75F255FE61FC2DA7CFFE56ADC8775AAAB54C50D0C4952AD919D90FB0EB221C41ABB9F2352A11BE2D7F176ABE41E0E30AFB34FC2CE16136DE66900D92068F30011E9882C0A56E7E7B30F08442BE9E58D093E1888151136259D059FB539210D635BC491D5244A16CA28FDCF10546EC0F7104D3A419DDC081BA30ECB0CD2289010C2D385946229B7A9735ADC82736914FE61AD26C6C38B787775DE3B939105DE055F8D7004358272A0823F6F1787A7ABB6C3C59C8C9CBD1674AC900512632818CDD273F0D38833C07467EAF77743B70C924D43975D3821D47110A358757F926FCF970660FBDD74EF15D93B81E3AA290C78F59CBC6ED0C1E0DCBADFD11A73EB7137850D29EFEB6FA321330D0CF70F5C7F6B004BCF86AC99125F8FECF83157930BEC2AF89F8B378C6D7F63B0A07B3651F5207A84F62CEE929D574DA154EBE795D519B661086F069C9F061BA3B53DC4910EA1614C87B114E2F9EF328AC94E93D00440B412D5AE5A3C396D52D26C0CDF2156EBD3D3F60EA500C42120A7CE1F7EF80F15323118956B17C09E80E96ED4E1572461D604CDE2533330C684F86680406B1D3EE830CBAFE6D29C9A0A2F41E03E26095B713EB7E782144DB1EC6B53047FCB606B7B665B3DD1F52E95FCF2AE59C4AB159C3F98468C0A43C36C022B548189B6") + check(t, true, "014E367F06F92BB039AEDCDD4DF65FC05A0D985B4CA6B79AA2254A6C605EB424048FA7F6117B8D4DA8522CD9C767B0450EEF9FA162E25BD305F36D77D8FEDE115C807C0805968129F15C1AD8489C32C41CB49418B4AEF52390900720B6D8B02C0EAB6A8B1420007A88412AB65DE0D04FEECCA0302E7806761483410365B5E771FCE7E5431230AD5E9E1C280E8953C68D0BD06236E9BD188437ADC14D42728C6E7177399B6B5908687F491F91EE6CCA3A391EF6C098CBEAEE83D962FA604A718A0C9DB625A7AAC25034517EB8743B5868A3803B37B94374E35F152F922BA423FB8E9B3D2B2BBF9DD602558CA5237D37420502B03D12B9230ED2A431D807B81BD18671EBF78380DD3CF490506187996E7C72F53C3914C76342A38A536FFAED478318CDD273F0D38833C07467EAF77743B70C924D43975D3821D47110A358757F926FCF970660FBDD74EF15D93B81E3AA290C78F59CBC6ED0C1E0DCBADFD11A73EB7137850D29EFEB6FA321330D0CF70F5C7F6B004BCF86AC99125F8FECF83157930BEC2AF89F8B378C6D7F63B0A07B3651F5207A84F62CEE929D574DA154EBE795D519B661086F069C9F061BA3B53DC4910EA1614C87B114E2F9EF328AC94E93D00440B412D5AE5A3C396D52D26C0CDF2156EBD3D3F60EA500C42120A7CE1F7EF80F15323118956B17C09E80E96ED4E1572461D604CDE2533330C684F86680406B1D3EE830CBAFE6D29C9A0A2F41E03E26095B713EB7E782144DB1EC6B53047FCB606B7B665B3DD1F52E95FCF2AE59C4AB159C3F98468C0A43C36C022B548189B6") +} + +func TestCryptolib_TestBls12381Pairing_Compat(t *testing.T) { + c := newCryptolibClient(t) + + script := io.NewBufBinWriter() + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, g2) + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, g1) + emit.Opcodes(script.BinWriter, opcode.PUSH2, opcode.PACK) + emit.AppCallNoArgs(script.BinWriter, c.Hash, "bls12381Pairing", callflag.All) + + stack, err := c.TestInvokeScript(t, script.Bytes(), c.Signers) + require.NoError(t, err) + require.Equal(t, 1, stack.Len()) + itm := stack.Pop().Item() + require.Equal(t, stackitem.InteropT, itm.Type()) + actual, ok := itm.(*stackitem.Interop).Value().(serializable) + require.True(t, ok) + arr := actual.Bytes() + // Expected GT value is taken from the reference test. + require.Equal(t, strings.ToLower("0F41E58663BF08CF068672CBD01A7EC73BACA4D72CA93544DEFF686BFD6DF543D48EAA24AFE47E1EFDE449383B67663104C581234D086A9902249B64728FFD21A189E87935A954051C7CDBA7B3872629A4FAFC05066245CB9108F0242D0FE3EF03350F55A7AEFCD3C31B4FCB6CE5771CC6A0E9786AB5973320C806AD360829107BA810C5A09FFDD9BE2291A0C25A99A211B8B424CD48BF38FCEF68083B0B0EC5C81A93B330EE1A677D0D15FF7B984E8978EF48881E32FAC91B93B47333E2BA5706FBA23EB7C5AF0D9F80940CA771B6FFD5857BAAF222EB95A7D2809D61BFE02E1BFD1B68FF02F0B8102AE1C2D5D5AB1A19F26337D205FB469CD6BD15C3D5A04DC88784FBB3D0B2DBDEA54D43B2B73F2CBB12D58386A8703E0F948226E47EE89D018107154F25A764BD3C79937A45B84546DA634B8F6BE14A8061E55CCEBA478B23F7DACAA35C8CA78BEAE9624045B4B601B2F522473D171391125BA84DC4007CFBF2F8DA752F7C74185203FCCA589AC719C34DFFBBAAD8431DAD1C1FB597AAA5193502B86EDB8857C273FA075A50512937E0794E1E65A7617C90D8BD66065B1FFFE51D7A579973B1315021EC3C19934F1368BB445C7C2D209703F239689CE34C0378A68E72A6B3B216DA0E22A5031B54DDFF57309396B38C881C4C849EC23E87089A1C5B46E5110B86750EC6A532348868A84045483C92B7AF5AF689452EAFABF1A8943E50439F1D59882A98EAA0170F1250EBD871FC0A92A7B2D83168D0D727272D441BEFA15C503DD8E90CE98DB3E7B6D194F60839C508A84305AACA1789B6"), + hex.EncodeToString(arr[:])) +} + +func TestCryptolib_TestBls12381Equal_Compat(t *testing.T) { + c := newCryptolibClient(t) + + script := io.NewBufBinWriter() + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, g1) + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, g1) + emit.Opcodes(script.BinWriter, opcode.PUSH2, opcode.PACK) + emit.AppCallNoArgs(script.BinWriter, c.Hash, "bls12381Equal", callflag.All) + + stack, err := c.TestInvokeScript(t, script.Bytes(), c.Signers) + require.NoError(t, err) + require.Equal(t, 1, stack.Len()) + itm := stack.Pop().Item() + require.Equal(t, stackitem.BooleanT, itm.Type()) + require.True(t, itm.Value().(bool)) +} + +// pointType represents one of the BLS12-381 point types and is used for tests only. +type pointType byte + +const ( + g1JacP pointType = iota + g2JacP + gtP +) + +// Data used for this test are generated by C# library and taken from the reference +// tests. +func TestCryptolib_TestBls12381Mul_CompatCustom(t *testing.T) { + tcs := []struct { + name string + point string // serialized hex-encoded point representation. + mul string // serialized hex-encoded Scalar multiplier representation. + neg bool // whether Scalar must be negative. + expectedTyp pointType // point type of the expected result. + expected string // serialized hex-encoded result of (point * mul) multiplication taken from the reference implementation. + }{ + { + name: "GT mul by positive scalar", + point: "14fd52fe9bfd08bbe23fcdf1d3bc5390c62e75a8786a72f8a343123a30a7c5f8d18508a21a2bf902f4db2c068913bc1c130e7ce13260d601c89ee717acfd3d4e1d80f409dd2a5c38b176f0b64d3d0a224c502717270dfecf2b825ac24608215c0d7fcfdf3c1552ada42b7e0521bc2e7389436660c352ecbf2eedf30b77b6b501df302399e6240473af47abe56fc974780c214542fcc0cf10e3001fa5e82d398f6ba1ddd1ccdf133bfd75e033eae50aec66bd5e884b8c74d4c1c6ac7c01278ac5164a54600cb2e24fec168f82542fbf98234dbb9ddf06503dc3c497da88b73db584ba19e685b1b398b51f40160e6c8f0917b4a68dedcc04674e5f5739cf0d845ba801263f712ed4ddda59c1d9909148e3f28124ae770682c9b19233bf0bcfa00d05bfe708d381b066b83a883ba8251ce2ea6772cbde51e1322d82b2c8a026a2153f4822e20cb69b8b05003ee74e09cb481728d688caa8a671f90b55488e272f48c7c5ae32526d3635a5343eb02640358d9ac445c76a5d8f52f653bbaee04ba5ce03c68b88c25be6fd3611cc21c9968e4f87e541beeccc5170b8696a439bb666ad8a6608ab30ebc7dfe56eaf0dd9ab8439171a6e4e0d608e6e6c8ac5ddcf8d6d2a950d06051e6b6c4d3feb6dc8dac2acadd345cadfb890454a2101a112f7471f0e001701f60f3d4352c4d388c0f198854908c0e939719709c1b3f82d2a25cc7156a3838bc141e041c259849326fbd0839f15cea6a78b89349dcd1c03695a74e72d3657af4ee2cf267337bc96363ef4a1c5d5d7a673cc3a3c1a1350043f99537d62", + mul: "8463159bd9a1d1e1fd815172177ec24c0c291353ed88b3d1838fd9d63b1efd0b", + neg: false, + expectedTyp: gtP, + expected: "03dc980ce0c037634816f9fc1edb2e1807e38a51f838e3a684f195d6c52c41d6a8a5b64d57d3fda507bebe3bd4b661af0e4f7c46754b373c955982b4d64a24838cbc010d04b6ceb499bf411d114dab77eaf70f96ab66c2868dcd63706b602b07010c487fc16c90b61e1c2ad33c31c8f3fc86d114a59b127ac584640f149f3597102c55dd1ed8a305a10c052c0a724e570fc079e410123735a6144ccd88d9e4e91d7b889f80b18a1741eacd6f244fce3cf57795e619b6648b9238053b4b8e4ed6115c905fbcb61525370667ff43144e12b700662a7344ac1af97f11d09779ca6865973f95ff318b42ff00df7c6eb958160947a0ab6cb25534af51ce1f0b076907c6eb5ce0760bd7670cab8814cc3308766eb6e52b5427dbf85d6424990fd3354515ab880358bc55075a08f36b855694c02ee0bd63adefe235ba4ee41dc600a1cae950c1dc760bf7b1edd8712e9e90eebb19de705e29f4feb870129441bd4b9e91c3d37e60c12fa79a5b1e4132ba9498044e6fbf2de37e4dd88b4e9095b46f122019e73a561ba3967b32813c3ec74b8e1b6ab619eeab698e6638114cb29ca9c3d353192db3d392fee2b4dfdfd36b13db440534dd754417cffcd470f4d4cfdcb6d7896181c27b8b30622d7a4ca0a05a7ea67ca011cab07738235b115bbd330239691487d2de5d679a8cad2fe5c7fff16b0b0f3f929619c8005289c3d7ffe5bcd5ea19651bfc9366682a2790cab45ee9a98815bb7e58dc666e2209cd9d700546cf181ceb43fe719243930984b696b0d18d4cd1f5d960e149a2b753b1396e4f8f3b16", + }, + { + name: "GT mul by positive scalar", + point: "0e0c651ff4a57adebab1fa41aa8d1e53d1cf6a6cc554282a24bb460ea0dc169d3ede8b5a93a331698f3926d273a729aa18788543413f43ada55a6a7505e3514f0db7e14d58311c3211962a350bcf908b3af90fbae31ff536fe542328ad25cd3e044a796200c8a8ead7edbc3a8a37209c5d37433ca7d8b0e644d7aac9726b524c41fef1cf0d546c252d795dffc445ddee07041f57c4c9a673bd314294e280ab61390731c09ad904bdd7b8c087d0ce857ea86e78f2d98e75d9b5e377e5751d67cf1717cbce31bc7ea6df95132549bf6d284a68005c53228127671afa54ecfd4c5c4debc437c4c6d9b9aeeee8b4159a5691128c6dc68b309fd822b14f3ce8ff390bd6834d30147e8ab2edc59d0d7b14cc13c79e6eed5fd6cae1795ba3760345d59c0c585f79c900902515e3e95938d9929ad8310e71fc7fd54be9c7529f244af40dadaca0b3bd8afd911f24b261079de48b161dd8f340d42bd84e717275193a0375d9e10fbe048bbea30abd64d3fe085c15b9be192f7baaa0b3a9658bcbb4292a0c0149beb30e54b065a75df45e5da77583f4471e3454cea90a00b5a9a224c15e2ebe01f0ab8aa86591c1012c618d41fdce07ecfcaddc8dc408b7176b79d8711a4161a56f41a5be6714cbcaa70e53387ab049826ac9e636640bc6da919e52f86f3209572b62d9bfd48bd2b5ef217932237b90a70d40167623d0f25a73b753e3214310bc5b6e017aebc1a9ca0c8067a97da6162c70cc754f1b2ac3b05ba834712758c8de4641ef09237edf588989182ab3047ee42da2b840fd3633fa0f34d46ad961", + mul: "06c93a0ebbc8b5cd3af798b8f72442a67aa885b395452a08e48ec80b4e9f1b3f", + neg: false, + expectedTyp: gtP, + expected: "0d6d91f120ab61e14a3163601ce584f053f1de9dc0a548b6fbf37a776ec7b6ce6b866e8c8b0fc0ac8d32a9a9747c98bf0e6aee5bddd058313958bfc3ac1ed75284628f92bb9b99fee101e1bee9d74bad7812287ea76bdbe07f20ff9998d6e9f016689be1cfc4337433644a679945d5c34a6d4dd984c56d6c28428438268b385cb1d86f69b0377b18f9b084e1d0b6596213233d559a1b5caaba38be853f667fc3b1f9f2c4c9020584502ff5f370b0aba7768a1a4ca4328bc3c7be2bc9c3949f5e16fd3bfc16b11da41b7393e56e777640b000db15b6e6192e5c59dfece90c6fc0b6071fdeef7061974b5e967c5b88b1db09f7c92077c16f56aff9e9627f5e09928e965daee17d05ef3fdc0c502b649db473b5b2bba867d829b04d32cfeab7387614190b265382378f75e4e085a5537d4f200fe56b74b7c52c5546b30d51862e1ac1f60eba157880090a42ea9b0295529f134c1fc90f19a4c20dc0be105b07e0c67218b2f5619a66d8d770d539658eb74c255743e5847bc437fef3077d0a6c4f17198d63cf17e6957f2ad9449269af009635697e92254a3f67be9b8760fd9f974826a1829fedb4cf66968b7c63b0c88c510da12e6d52255256757afa03ad29b5c1624292ef7eb463eb4bc81ac7426f36db3fe1513bdd31bc138bfe903bbb0c5207001335f708c16cea15ef6b77c3215326a779e927b8c2081b15adffe71ba75164e376665533c5bb59373b27dbe93a0a0e1796d821a1b9ff01846446c5ad53064cb9b941f97aa870285395e1a44c9f6e5144ea5a0cf57b9fdd962a5ec3ff1f72fe", + }, + { + name: "GT mul by positive scalar", + point: "0e0c651ff4a57adebab1fa41aa8d1e53d1cf6a6cc554282a24bb460ea0dc169d3ede8b5a93a331698f3926d273a729aa18788543413f43ada55a6a7505e3514f0db7e14d58311c3211962a350bcf908b3af90fbae31ff536fe542328ad25cd3e044a796200c8a8ead7edbc3a8a37209c5d37433ca7d8b0e644d7aac9726b524c41fef1cf0d546c252d795dffc445ddee07041f57c4c9a673bd314294e280ab61390731c09ad904bdd7b8c087d0ce857ea86e78f2d98e75d9b5e377e5751d67cf1717cbce31bc7ea6df95132549bf6d284a68005c53228127671afa54ecfd4c5c4debc437c4c6d9b9aeeee8b4159a5691128c6dc68b309fd822b14f3ce8ff390bd6834d30147e8ab2edc59d0d7b14cc13c79e6eed5fd6cae1795ba3760345d59c0c585f79c900902515e3e95938d9929ad8310e71fc7fd54be9c7529f244af40dadaca0b3bd8afd911f24b261079de48b161dd8f340d42bd84e717275193a0375d9e10fbe048bbea30abd64d3fe085c15b9be192f7baaa0b3a9658bcbb4292a0c0149beb30e54b065a75df45e5da77583f4471e3454cea90a00b5a9a224c15e2ebe01f0ab8aa86591c1012c618d41fdce07ecfcaddc8dc408b7176b79d8711a4161a56f41a5be6714cbcaa70e53387ab049826ac9e636640bc6da919e52f86f3209572b62d9bfd48bd2b5ef217932237b90a70d40167623d0f25a73b753e3214310bc5b6e017aebc1a9ca0c8067a97da6162c70cc754f1b2ac3b05ba834712758c8de4641ef09237edf588989182ab3047ee42da2b840fd3633fa0f34d46ad961", + mul: "b0010000000000005e0000000000000071f30400000000006d9189c813000000", + neg: false, + expectedTyp: gtP, + expected: "0919ad29cdbe0b6bbd636fbe3c8930a1b959e5aa37294a6cc7d018e2776580768bb98bf91ce1bc97f2e6fa647e7dad7b15db564645d2e4868129ed414b7e369e831b8ff93997a22b6ca0e2ba288783f535aed4b44cf3e952897db1536da18a120a70da2b9dd901bd12a5a7047d3b6346ba1aea53b642b7355a91f957687fccd840ef24af100d0ada6b49e35183456ec30b505098526b975477b6ca0273d3a841c85e4a8319b950e76ec217a4f939844baa6b875a4046a30c618636fe9b25c620030f31044f883789945c2bcb75d7d4099b2bc97665e75c1bee27bc3864e7e5e2ccb57a9da0b57be1a6aca217a6cfda090c4fd222f7b8cfdc32969da4fe8828a59ee1314546efdf99ef7ede1a42df6e7a126fe83b4c41b5e70a56bd9ab499f7e80e27a08884be05f1d2a527417fc6e30448333c0724463bf92d722ef5fd6f06949e294e6f941976d24c856038b55a2ec200d14d958a688f23b572993bd0f18cbbc20defe88e423b262c552dcc4d9f63ad78e85efbcea9449f81f39e1a887eb79b07056bb5a672444e240660617ba7a40985a622c687c1d05c12cee7b086abfc5f39a83a5ad7638ee559f710013b772d4207924687cb30100bcd4e8c83c9fa19dce7785bf3ae7681a0968fd9661c990e2dace05902dceeed65aacf51a04e72f0fd04858ea70fb72f2a3807dc1839a385d85b536abfd3ec76d4931b3bc5ec4d90e2ebc0342567c9507abdfafa602fc6983f13f20eb26b4169dc3908109fe3c1887db4be8f30edad989dc8caa234f9818ac488b110ad30a30f769277168650b6910e", + }, + { + name: "GT mul by negative scalar", + point: "0bdbfc3b68e7067630a1908de2ce15e1890d57b855ffc2ee0fe765293581c304d0507254fd9921d8ff4bff3185b1e8ae017091a6b9e243c3108b4302f30e2f4cb452c4574d23d06942cf915fb0b64c3546aa0bfbba5182dc42b63ebd09cd950f06ebf85ff360032e63d5422fed5969b80ed4abaf58d29317d9cf8e5a55744993ffc0ccc586a187c63f9c47d4b41870aa0fd73e13a4f7d3b072407a3bfa6539f8d56856542b17326ab77833df274e61a41c237a6dbf20a333698a675fded6ab1a114891795eabbedcb81590ff9bfb4b23b66c8b8376a69cf58511c80f3ac83d52c0c950be8c30d01108479f232d8e4e8919d869dc85db0b9d6ccf40eb8f8ab08e43a910c341737a55e751fa4a097ee82c5ac83d38c543d957bd9850af16039d1a00c96575d2ee24e9990b3401153446aa6593d3afb6ce7ca57d6432b8dda31aaa1a08834ad38deae5a807d11663adc5c20ae7227a2cbb7917d1489175b89ed1ba415e4fc55b7d0a286caf2f5f40b0dd39cdd8fc8c271d8a7ae952fe6ece5f7c1019bfab0167af86314a73bfa37fd16bc6edff6d9ee75610a4eec1818c668ef9f509b1cdd54542e73dc0e343a4fd6e3bb618540c1d060b60b63b645a895105425eb813b08b6ac91be3145da04040f2a45ffcf06e96b685519fca93b0f15238dc0e030c2199127ba82fa8a193f5f01ae24270e9669923653db38cae711d68169aa25df51a8915f3f8219892f4f5e67d550b00910011685017dcc1777a9d48689ce590d57c1fc942d49cfad0ed7efc0169a95d7e7378af26bafb90d1619bcdab64cd", + mul: "688e58217305c1fd2fe0637cbd8e7414d4d0a2113314eb05592f97930d23b34d", + neg: true, + expectedTyp: gtP, + expected: "056fdc84f044148950c0b7c4c0613f5710fcaeb1b023b9d8f814dc39d48702db70ce41aa276566960e37237f22b086b017b9ed0e264e2b7872c8a7affb8b9f847a528d092a038dab4ac58d3a33d30e2e5078b5e39ebb7441c56ae7556b63ecd6139ed9be1c5eb9f987cc704c913c1e23d44d2e04377347f6c471edc40cdb2cd4e32c396194363cd21ceff9bedbd164a41050e701012f0456383210f8054e76c0906e3f37e10d4a3d6342e79e39d566ea785b385bb692cddbd6c16456dfabf19f0f84c27ec4bce096af0369ac070747cd89d97bc287afe5ed5e495ed2d743adbd8eec47df6c3a69628e803e23d824845800e44a8d874756a7541128892e55e9df1d1fe0583ef967db6740617a9ff50766866c0fa631aed8639cd0c13d3d6f6f210b340ee315caec4cc31c916d651db5e002e259fca081fb605258ccf692d786bd5bb45a054c4d8498ac2a7fa241870df60ba0fd8a2b063740af11e7530db1e758a8e2858a443104b8337e18c083035768a0e93126f116bb9c50c8cebe30e0ceaa0c0b53eb2b6a1f96b34b6cc36f3417edda184e19ae1790d255337f14315323e1d2d7382b344bdc0b6b2cfab5837c24c916640ca351539d5459389a9c7f9b0d79e04e4a8392e0c2495dcecf7d48b10c7043825b7c6709108d81856ebf98385f0d099e6521714c48b8eb5d2e97665375175f47c57d427d35a9dc44064a99d1c079028e36d34540baba947333ab3c8976b801ea48578159f041e740ea5bf73c1de3c1043a6e03311d0f2463b72694249ccc5d603e4a93cfd8a6713fb0470383c23f", + }, + { + name: "GT mul by zero scalar", + point: "176ec726aa447f1791e69fc70a71103c84b17385094ef06a9a0235ac7241f6635377f55ad486c216c8701d61ea2ace3e05ca1605f238dc8f29f868b795e45645c6f7ff8d9d8ffd77b5e149b0325c2a8f24dde40e80a3381ae72a9a1104ef02d70af7cf8f2fe6ff38961b352b0fde6f8536424fc9aa5805b8e12313bdfc01d5c1db1c0a37654c307fbd252c265dcbfc040ee5605ffd6ac20aab15b0343e47831f4157a20ecedd7350d2cf070c0c7d423786fd97aa7236b99f4462fb23e173528815bf2cf3ccbfc38303fa8154d70ee5e1e3158cbb14d5c87a773cbe948a5cfec2763c5e7129940906920aed344453b0f801760fd3eac8e254ce8e0ae4edd30c914bea9e2935acd4a6a9d42d185a9a6e786c8e462b769b2112423f6591b093347718897438ba918b9e4525888194b20ee17709f7dea319cfd053bb1c222783340326953fd3763eb6feaaa4d1458ee6ca001818ad88222a97e43a71dca8d2abaef70657b9ff7b94ca422d0c50ddb4265fa35514ed534217ce2f0219c6985ec2827a0ee1dc17940926551072d693d89e36e6d14162f414b52587e5612ed4a562c9ac15df9d5fa68ccf61d52fea64b2f5d7a600e0a8fa735105bc9a2ecb69b6d9161e55a4ccdc2285164c6846fa5bdc106d1e0693ebd5fe86432e5e88c55f0159ec3217332c8492332dfbd93970f002a6a05f23484e081f38815785e766779c843765d58b2444295a87939ad7f8fa4c11e8530a62426063c9a57cf3481a00372e443dc014fd6ef4723dd4636105d7ce7b96c4b2b3b641c3a2b6e0fa9be6187e5bfaf9", + mul: "0000000000000000000000000000000000000000000000000000000000000000", + neg: false, + expectedTyp: gtP, + expected: "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + }, + { + name: "G1Affine mul by positive scalar", + point: "a1f9855f7670a63e4c80d64dfe6ddedc2ed2bfaebae27e4da82d71ba474987a39808e8921d3df97df6e5d4b979234de8", + mul: "8463159bd9a1d1e1fd815172177ec24c0c291353ed88b3d1838fd9d63b1efd0b", + neg: false, + expectedTyp: g1JacP, + expected: "ae85e3e2d677c9e3424ed79b5a7554262c3d6849202b84d2e7024e4b1f2e9dd3f7cf20b807a9f2a67d87e47e9e94d361", + }, + { + name: "G1Affine mul by negative scalar", + point: "a1f9855f7670a63e4c80d64dfe6ddedc2ed2bfaebae27e4da82d71ba474987a39808e8921d3df97df6e5d4b979234de8", + mul: "8463159bd9a1d1e1fd815172177ec24c0c291353ed88b3d1838fd9d63b1efd0b", + neg: true, + expectedTyp: g1JacP, + expected: "8e85e3e2d677c9e3424ed79b5a7554262c3d6849202b84d2e7024e4b1f2e9dd3f7cf20b807a9f2a67d87e47e9e94d361", + }, + { + name: "G1Affine mul by zero scalar", + point: "a1f9855f7670a63e4c80d64dfe6ddedc2ed2bfaebae27e4da82d71ba474987a39808e8921d3df97df6e5d4b979234de8", + mul: "0000000000000000000000000000000000000000000000000000000000000000", + neg: false, + expectedTyp: g1JacP, + expected: "c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + }, + { + name: "G2Affine mul by positive scalar", + point: "a41e586fdd58d39616fea921a855e65417a5732809afc35e28466e3acaeed3d53dd4b97ca398b2f29bf6bbcaca026a6609a42bdeaaeef42813ae225e35c23c61c293e6ecb6759048fb76ac648ba3bc49f0fcf62f73fca38cdc5e7fa5bf511365", + mul: "cbfffe3e37e53e31306addde1a1725641fbe88cd047ee7477966c44a3f764b47", + neg: false, + expectedTyp: g2JacP, + expected: "88ae9bba988e854877c66dfb7ff84aa5e107861aa51d1a2a8dac2414d716a7e219bc4b0239e4b12d2182f57b5eea82830639f2e6713098ae8d4b4c3942f366614bac35c91c83ecb57fa90fe03094aca1ecd3555a7a6fdfa2417b5bb06917732e", + }, + { + name: "G2Affine mul by negative scalar", + point: "a41e586fdd58d39616fea921a855e65417a5732809afc35e28466e3acaeed3d53dd4b97ca398b2f29bf6bbcaca026a6609a42bdeaaeef42813ae225e35c23c61c293e6ecb6759048fb76ac648ba3bc49f0fcf62f73fca38cdc5e7fa5bf511365", + mul: "cbfffe3e37e53e31306addde1a1725641fbe88cd047ee7477966c44a3f764b47", + neg: true, + expectedTyp: g2JacP, + expected: "a8ae9bba988e854877c66dfb7ff84aa5e107861aa51d1a2a8dac2414d716a7e219bc4b0239e4b12d2182f57b5eea82830639f2e6713098ae8d4b4c3942f366614bac35c91c83ecb57fa90fe03094aca1ecd3555a7a6fdfa2417b5bb06917732e", + }, + { + name: "G2Affine mul by zero scalar", + point: "a41e586fdd58d39616fea921a855e65417a5732809afc35e28466e3acaeed3d53dd4b97ca398b2f29bf6bbcaca026a6609a42bdeaaeef42813ae225e35c23c61c293e6ecb6759048fb76ac648ba3bc49f0fcf62f73fca38cdc5e7fa5bf511365", + mul: "0000000000000000000000000000000000000000000000000000000000000000", + neg: false, + expectedTyp: g2JacP, + expected: "c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + }, + } + c := newCryptolibClient(t) + + check := func(t *testing.T, resTyp pointType, gt string, mul string, neg bool, expected string) { + gtBytes, err := hex.DecodeString(gt) + require.NoError(t, err) + mulBytes, err := hex.DecodeString(mul) + require.NoError(t, err) + script := io.NewBufBinWriter() + if neg { + emit.Opcodes(script.BinWriter, opcode.PUSHT) + } else { + emit.Opcodes(script.BinWriter, opcode.PUSHF) + } + emit.Bytes(script.BinWriter, mulBytes) + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, gtBytes) + emit.Opcodes(script.BinWriter, opcode.PUSH3, opcode.PACK) + emit.AppCallNoArgs(script.BinWriter, c.Hash, "bls12381Mul", callflag.All) + + stack, err := c.TestInvokeScript(t, script.Bytes(), c.Signers) + require.NoError(t, err) + require.Equal(t, 1, stack.Len()) + itm := stack.Pop().Item() + require.Equal(t, stackitem.InteropT, itm.Type()) + p, ok := itm.(*stackitem.Interop).Value().(serializable) + require.True(t, ok) + arr := p.Bytes() + actual := arr[:] + require.Equal(t, strings.ToLower(expected), hex.EncodeToString(actual)) + } + + for i, tc := range tcs { + t.Run(strconv.Itoa(i), func(t *testing.T) { + check(t, tc.expectedTyp, + tc.point, + tc.mul, + tc.neg, + tc.expected) + }) + } +} + +func TestCryptolib_Bls12381PointsEQUAL_DUP(t *testing.T) { + c := newCryptolibClient(t) + + check := func(t *testing.T, point string) { + bytes, err := hex.DecodeString(point) + require.NoError(t, err) + script := io.NewBufBinWriter() + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, bytes) + emit.Opcodes(script.BinWriter, opcode.DUP, opcode.EQUAL) + + c.InvokeScriptCheckHALT(t, script.Bytes(), c.Signers, stackitem.NewBool(true)) + } + t.Run("GT", func(t *testing.T) { + check(t, "14fd52fe9bfd08bbe23fcdf1d3bc5390c62e75a8786a72f8a343123a30a7c5f8d18508a21a2bf902f4db2c068913bc1c130e7ce13260d601c89ee717acfd3d4e1d80f409dd2a5c38b176f0b64d3d0a224c502717270dfecf2b825ac24608215c0d7fcfdf3c1552ada42b7e0521bc2e7389436660c352ecbf2eedf30b77b6b501df302399e6240473af47abe56fc974780c214542fcc0cf10e3001fa5e82d398f6ba1ddd1ccdf133bfd75e033eae50aec66bd5e884b8c74d4c1c6ac7c01278ac5164a54600cb2e24fec168f82542fbf98234dbb9ddf06503dc3c497da88b73db584ba19e685b1b398b51f40160e6c8f0917b4a68dedcc04674e5f5739cf0d845ba801263f712ed4ddda59c1d9909148e3f28124ae770682c9b19233bf0bcfa00d05bfe708d381b066b83a883ba8251ce2ea6772cbde51e1322d82b2c8a026a2153f4822e20cb69b8b05003ee74e09cb481728d688caa8a671f90b55488e272f48c7c5ae32526d3635a5343eb02640358d9ac445c76a5d8f52f653bbaee04ba5ce03c68b88c25be6fd3611cc21c9968e4f87e541beeccc5170b8696a439bb666ad8a6608ab30ebc7dfe56eaf0dd9ab8439171a6e4e0d608e6e6c8ac5ddcf8d6d2a950d06051e6b6c4d3feb6dc8dac2acadd345cadfb890454a2101a112f7471f0e001701f60f3d4352c4d388c0f198854908c0e939719709c1b3f82d2a25cc7156a3838bc141e041c259849326fbd0839f15cea6a78b89349dcd1c03695a74e72d3657af4ee2cf267337bc96363ef4a1c5d5d7a673cc3a3c1a1350043f99537d62") + }) + t.Run("G1", func(t *testing.T) { + check(t, "a1f9855f7670a63e4c80d64dfe6ddedc2ed2bfaebae27e4da82d71ba474987a39808e8921d3df97df6e5d4b979234de8") + }) + t.Run("G2", func(t *testing.T) { + check(t, "a41e586fdd58d39616fea921a855e65417a5732809afc35e28466e3acaeed3d53dd4b97ca398b2f29bf6bbcaca026a6609a42bdeaaeef42813ae225e35c23c61c293e6ecb6759048fb76ac648ba3bc49f0fcf62f73fca38cdc5e7fa5bf511365") + }) +} + +func TestCryptolib_Bls12381PointsEQUAL(t *testing.T) { + c := newCryptolibClient(t) + + check := func(t *testing.T, point1, point2 string, expected bool) { + bytes1, err := hex.DecodeString(point1) + require.NoError(t, err) + bytes2, err := hex.DecodeString(point2) + require.NoError(t, err) + script := io.NewBufBinWriter() + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, bytes1) + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, bytes2) + emit.Opcodes(script.BinWriter, opcode.EQUAL) + + c.InvokeScriptCheckHALT(t, script.Bytes(), c.Signers, stackitem.NewBool(expected)) + } + t.Run("GT true", func(t *testing.T) { + check(t, "14fd52fe9bfd08bbe23fcdf1d3bc5390c62e75a8786a72f8a343123a30a7c5f8d18508a21a2bf902f4db2c068913bc1c130e7ce13260d601c89ee717acfd3d4e1d80f409dd2a5c38b176f0b64d3d0a224c502717270dfecf2b825ac24608215c0d7fcfdf3c1552ada42b7e0521bc2e7389436660c352ecbf2eedf30b77b6b501df302399e6240473af47abe56fc974780c214542fcc0cf10e3001fa5e82d398f6ba1ddd1ccdf133bfd75e033eae50aec66bd5e884b8c74d4c1c6ac7c01278ac5164a54600cb2e24fec168f82542fbf98234dbb9ddf06503dc3c497da88b73db584ba19e685b1b398b51f40160e6c8f0917b4a68dedcc04674e5f5739cf0d845ba801263f712ed4ddda59c1d9909148e3f28124ae770682c9b19233bf0bcfa00d05bfe708d381b066b83a883ba8251ce2ea6772cbde51e1322d82b2c8a026a2153f4822e20cb69b8b05003ee74e09cb481728d688caa8a671f90b55488e272f48c7c5ae32526d3635a5343eb02640358d9ac445c76a5d8f52f653bbaee04ba5ce03c68b88c25be6fd3611cc21c9968e4f87e541beeccc5170b8696a439bb666ad8a6608ab30ebc7dfe56eaf0dd9ab8439171a6e4e0d608e6e6c8ac5ddcf8d6d2a950d06051e6b6c4d3feb6dc8dac2acadd345cadfb890454a2101a112f7471f0e001701f60f3d4352c4d388c0f198854908c0e939719709c1b3f82d2a25cc7156a3838bc141e041c259849326fbd0839f15cea6a78b89349dcd1c03695a74e72d3657af4ee2cf267337bc96363ef4a1c5d5d7a673cc3a3c1a1350043f99537d62", + "14fd52fe9bfd08bbe23fcdf1d3bc5390c62e75a8786a72f8a343123a30a7c5f8d18508a21a2bf902f4db2c068913bc1c130e7ce13260d601c89ee717acfd3d4e1d80f409dd2a5c38b176f0b64d3d0a224c502717270dfecf2b825ac24608215c0d7fcfdf3c1552ada42b7e0521bc2e7389436660c352ecbf2eedf30b77b6b501df302399e6240473af47abe56fc974780c214542fcc0cf10e3001fa5e82d398f6ba1ddd1ccdf133bfd75e033eae50aec66bd5e884b8c74d4c1c6ac7c01278ac5164a54600cb2e24fec168f82542fbf98234dbb9ddf06503dc3c497da88b73db584ba19e685b1b398b51f40160e6c8f0917b4a68dedcc04674e5f5739cf0d845ba801263f712ed4ddda59c1d9909148e3f28124ae770682c9b19233bf0bcfa00d05bfe708d381b066b83a883ba8251ce2ea6772cbde51e1322d82b2c8a026a2153f4822e20cb69b8b05003ee74e09cb481728d688caa8a671f90b55488e272f48c7c5ae32526d3635a5343eb02640358d9ac445c76a5d8f52f653bbaee04ba5ce03c68b88c25be6fd3611cc21c9968e4f87e541beeccc5170b8696a439bb666ad8a6608ab30ebc7dfe56eaf0dd9ab8439171a6e4e0d608e6e6c8ac5ddcf8d6d2a950d06051e6b6c4d3feb6dc8dac2acadd345cadfb890454a2101a112f7471f0e001701f60f3d4352c4d388c0f198854908c0e939719709c1b3f82d2a25cc7156a3838bc141e041c259849326fbd0839f15cea6a78b89349dcd1c03695a74e72d3657af4ee2cf267337bc96363ef4a1c5d5d7a673cc3a3c1a1350043f99537d62", + true) + }) + t.Run("GT false", func(t *testing.T) { + check(t, "14fd52fe9bfd08bbe23fcdf1d3bc5390c62e75a8786a72f8a343123a30a7c5f8d18508a21a2bf902f4db2c068913bc1c130e7ce13260d601c89ee717acfd3d4e1d80f409dd2a5c38b176f0b64d3d0a224c502717270dfecf2b825ac24608215c0d7fcfdf3c1552ada42b7e0521bc2e7389436660c352ecbf2eedf30b77b6b501df302399e6240473af47abe56fc974780c214542fcc0cf10e3001fa5e82d398f6ba1ddd1ccdf133bfd75e033eae50aec66bd5e884b8c74d4c1c6ac7c01278ac5164a54600cb2e24fec168f82542fbf98234dbb9ddf06503dc3c497da88b73db584ba19e685b1b398b51f40160e6c8f0917b4a68dedcc04674e5f5739cf0d845ba801263f712ed4ddda59c1d9909148e3f28124ae770682c9b19233bf0bcfa00d05bfe708d381b066b83a883ba8251ce2ea6772cbde51e1322d82b2c8a026a2153f4822e20cb69b8b05003ee74e09cb481728d688caa8a671f90b55488e272f48c7c5ae32526d3635a5343eb02640358d9ac445c76a5d8f52f653bbaee04ba5ce03c68b88c25be6fd3611cc21c9968e4f87e541beeccc5170b8696a439bb666ad8a6608ab30ebc7dfe56eaf0dd9ab8439171a6e4e0d608e6e6c8ac5ddcf8d6d2a950d06051e6b6c4d3feb6dc8dac2acadd345cadfb890454a2101a112f7471f0e001701f60f3d4352c4d388c0f198854908c0e939719709c1b3f82d2a25cc7156a3838bc141e041c259849326fbd0839f15cea6a78b89349dcd1c03695a74e72d3657af4ee2cf267337bc96363ef4a1c5d5d7a673cc3a3c1a1350043f99537d62", + hex.EncodeToString(gt), + false) + }) + t.Run("G1 true", func(t *testing.T) { + check(t, "a1f9855f7670a63e4c80d64dfe6ddedc2ed2bfaebae27e4da82d71ba474987a39808e8921d3df97df6e5d4b979234de8", + "a1f9855f7670a63e4c80d64dfe6ddedc2ed2bfaebae27e4da82d71ba474987a39808e8921d3df97df6e5d4b979234de8", + true) + }) + t.Run("G1 false", func(t *testing.T) { + check(t, "a1f9855f7670a63e4c80d64dfe6ddedc2ed2bfaebae27e4da82d71ba474987a39808e8921d3df97df6e5d4b979234de8", + hex.EncodeToString(g1), + false) + }) + t.Run("G2 true", func(t *testing.T) { + check(t, "a41e586fdd58d39616fea921a855e65417a5732809afc35e28466e3acaeed3d53dd4b97ca398b2f29bf6bbcaca026a6609a42bdeaaeef42813ae225e35c23c61c293e6ecb6759048fb76ac648ba3bc49f0fcf62f73fca38cdc5e7fa5bf511365", + "a41e586fdd58d39616fea921a855e65417a5732809afc35e28466e3acaeed3d53dd4b97ca398b2f29bf6bbcaca026a6609a42bdeaaeef42813ae225e35c23c61c293e6ecb6759048fb76ac648ba3bc49f0fcf62f73fca38cdc5e7fa5bf511365", + true) + }) + t.Run("G2 false", func(t *testing.T) { + check(t, "a41e586fdd58d39616fea921a855e65417a5732809afc35e28466e3acaeed3d53dd4b97ca398b2f29bf6bbcaca026a6609a42bdeaaeef42813ae225e35c23c61c293e6ecb6759048fb76ac648ba3bc49f0fcf62f73fca38cdc5e7fa5bf511365", + hex.EncodeToString(g2), + false) + }) +} + +func TestCryptolib_Bls12381Equal_GT(t *testing.T) { + c := newCryptolibClient(t) + + script := io.NewBufBinWriter() + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, gt) + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, gt) + emit.Opcodes(script.BinWriter, opcode.PUSH2, opcode.PACK) + emit.AppCallNoArgs(script.BinWriter, c.Hash, "bls12381Equal", callflag.All) + + stack, err := c.TestInvokeScript(t, script.Bytes(), c.Signers) + require.NoError(t, err) + require.Equal(t, 1, stack.Len()) + itm := stack.Pop().Item() + require.Equal(t, stackitem.BooleanT, itm.Type()) + require.True(t, itm.Value().(bool)) +} diff --git a/pkg/core/native/native_test/designate_test.go b/pkg/core/native/native_test/designate_test.go index 98d3c362b..5bb7e47d0 100644 --- a/pkg/core/native/native_test/designate_test.go +++ b/pkg/core/native/native_test/designate_test.go @@ -106,11 +106,11 @@ func TestDesignate_Cache(t *testing.T) { } privGood, err := keys.NewPrivateKey() require.NoError(t, err) - pubsGood := []interface{}{privGood.PublicKey().Bytes()} + pubsGood := []any{privGood.PublicKey().Bytes()} privBad, err := keys.NewPrivateKey() require.NoError(t, err) - pubsBad := []interface{}{privBad.PublicKey().Bytes()} + pubsBad := []any{privBad.PublicKey().Bytes()} // Firstly, designate good Oracle node and check that OracleService callback was called during PostPersist. e.Chain.SetOracle(oracleServ) diff --git a/pkg/core/native/native_test/gas_test.go b/pkg/core/native/native_test/gas_test.go index 3c08fbf30..e92f9097b 100644 --- a/pkg/core/native/native_test/gas_test.go +++ b/pkg/core/native/native_test/gas_test.go @@ -78,7 +78,7 @@ func TestGAS_RewardWithP2PSigExtensionsEnabled(t *testing.T) { // set Notary nodes and check their balance notaryNodes := make([]*keys.PrivateKey, nNotaries) - notaryNodesPublicKeys := make([]interface{}, nNotaries) + notaryNodesPublicKeys := make([]any, nNotaries) var err error for i := range notaryNodes { notaryNodes[i], err = keys.NewPrivateKey() @@ -92,7 +92,7 @@ func TestGAS_RewardWithP2PSigExtensionsEnabled(t *testing.T) { // deposit GAS for `signer` with lock until the next block depositAmount := 100_0000 + (2+int64(nKeys))*notaryServiceFeePerKey // sysfee + netfee of the next transaction - gasCommitteeInvoker.Invoke(t, true, "transfer", e.CommitteeHash, notaryHash, depositAmount, []interface{}{e.CommitteeHash, e.Chain.BlockHeight() + 1}) + gasCommitteeInvoker.Invoke(t, true, "transfer", e.CommitteeHash, notaryHash, depositAmount, []any{e.CommitteeHash, e.Chain.BlockHeight() + 1}) // save initial GAS total supply getGASTS := func(t *testing.T) int64 { diff --git a/pkg/core/native/native_test/management_test.go b/pkg/core/native/native_test/management_test.go index f5af18246..04ff15c86 100644 --- a/pkg/core/native/native_test/management_test.go +++ b/pkg/core/native/native_test/management_test.go @@ -98,7 +98,7 @@ func TestManagement_ContractDeploy(t *testing.T) { managementInvoker.InvokeFail(t, "invalid NEF file", "deploy", []byte{}, manifestBytes) }) t.Run("array for NEF", func(t *testing.T) { - managementInvoker.InvokeFail(t, "invalid NEF file", "deploy", []interface{}{int64(1)}, manifestBytes) + managementInvoker.InvokeFail(t, "invalid NEF file", "deploy", []any{int64(1)}, manifestBytes) }) t.Run("bad script in NEF", func(t *testing.T) { nf, err := nef.FileFromBytes(nefBytes) // make a full copy @@ -116,7 +116,7 @@ func TestManagement_ContractDeploy(t *testing.T) { managementInvoker.InvokeFail(t, "invalid manifest", "deploy", nefBytes, []byte{}) }) t.Run("array for manifest", func(t *testing.T) { - managementInvoker.InvokeFail(t, "invalid manifest", "deploy", nefBytes, []interface{}{int64(1)}) + managementInvoker.InvokeFail(t, "invalid manifest", "deploy", nefBytes, []any{int64(1)}) }) t.Run("non-utf8 manifest", func(t *testing.T) { manifestBad := bytes.Replace(manifestBytes, []byte("TestMain"), []byte("\xff\xfe\xfd"), 1) // Replace name. @@ -538,7 +538,7 @@ func TestManagement_GetContract(t *testing.T) { managementInvoker.Invoke(t, si, "deploy", nefBytes, manifestBytes) t.Run("bad parameter type", func(t *testing.T) { - managementInvoker.InvokeFail(t, "invalid conversion: Array/ByteString", "getContract", []interface{}{int64(1)}) + managementInvoker.InvokeFail(t, "invalid conversion: Array/ByteString", "getContract", []any{int64(1)}) }) t.Run("not a hash", func(t *testing.T) { managementInvoker.InvokeFail(t, "expected byte size of 20 got 3", "getContract", []byte{1, 2, 3}) @@ -547,7 +547,7 @@ func TestManagement_GetContract(t *testing.T) { managementInvoker.Invoke(t, si, "getContract", cs1.Hash.BytesBE()) }) t.Run("by ID, bad parameter type", func(t *testing.T) { - managementInvoker.InvokeFail(t, "invalid conversion: Array/Integer", "getContractById", []interface{}{int64(1)}) + managementInvoker.InvokeFail(t, "invalid conversion: Array/Integer", "getContractById", []any{int64(1)}) }) t.Run("by ID, bad num", func(t *testing.T) { managementInvoker.InvokeFail(t, "id is not a correct int32", "getContractById", []byte{1, 2, 3, 4, 5}) @@ -555,7 +555,6 @@ func TestManagement_GetContract(t *testing.T) { t.Run("by ID, positive", func(t *testing.T) { managementInvoker.Invoke(t, si, "getContractById", cs1.ID) }) - /* C# compatibility t.Run("by ID, native", func(t *testing.T) { csm := managementInvoker.Executor.Chain.GetContractState(managementInvoker.Hash) require.NotNil(t, csm) @@ -563,7 +562,6 @@ func TestManagement_GetContract(t *testing.T) { require.NoError(t, err) managementInvoker.Invoke(t, sim, "getContractById", -1) }) - */ t.Run("by ID, empty", func(t *testing.T) { managementInvoker.Invoke(t, stackitem.Null{}, "getContractById", -100) }) diff --git a/pkg/core/native/native_test/neo_test.go b/pkg/core/native/native_test/neo_test.go index 011636ca9..dae6e1eb5 100644 --- a/pkg/core/native/native_test/neo_test.go +++ b/pkg/core/native/native_test/neo_test.go @@ -3,13 +3,16 @@ package native_test import ( "bytes" "encoding/json" + "fmt" "math" "math/big" "sort" + "strings" "testing" "github.com/nspcc-dev/neo-go/internal/contracts" "github.com/nspcc-dev/neo-go/internal/random" + "github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" @@ -305,6 +308,15 @@ func TestNEO_GetAccountState(t *testing.T) { neoValidatorInvoker := newNeoValidatorsClient(t) e := neoValidatorInvoker.Executor + cfg := e.Chain.GetConfig() + committeeSize := cfg.GetCommitteeSize(0) + validatorSize := cfg.GetNumOfCNs(0) + advanceChain := func(t *testing.T) { + for i := 0; i < committeeSize; i++ { + neoValidatorInvoker.AddNewBlock(t) + } + } + t.Run("empty", func(t *testing.T) { neoValidatorInvoker.Invoke(t, stackitem.Null{}, "getAccountState", util.Uint160{}) }) @@ -318,8 +330,100 @@ func TestNEO_GetAccountState(t *testing.T) { stackitem.Make(amount), stackitem.Make(lub), stackitem.Null{}, + stackitem.Make(0), }), "getAccountState", acc.ScriptHash()) }) + + t.Run("lastGasPerVote", func(t *testing.T) { + const ( + GasPerBlock = 5 + VoterRewardRatio = 80 + ) + getAccountState := func(t *testing.T, account util.Uint160) *state.NEOBalance { + stack, err := neoValidatorInvoker.TestInvoke(t, "getAccountState", account) + require.NoError(t, err) + as := new(state.NEOBalance) + err = as.FromStackItem(stack.Pop().Item()) + require.NoError(t, err) + return as + } + + amount := int64(1000) + acc := e.NewAccount(t) + neoValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), acc.ScriptHash(), amount, nil) + as := getAccountState(t, acc.ScriptHash()) + require.Equal(t, uint64(amount), as.Balance.Uint64()) + require.Equal(t, e.Chain.BlockHeight(), as.BalanceHeight) + require.Equal(t, uint64(0), as.LastGasPerVote.Uint64()) + committee, _ := e.Chain.GetCommittee() + neoValidatorInvoker.WithSigners(e.Validator, e.Validator.(neotest.MultiSigner).Single(0)).Invoke(t, true, "registerCandidate", committee[0].Bytes()) + neoValidatorInvoker.WithSigners(acc).Invoke(t, true, "vote", acc.ScriptHash(), committee[0].Bytes()) + as = getAccountState(t, acc.ScriptHash()) + require.Equal(t, uint64(0), as.LastGasPerVote.Uint64()) + advanceChain(t) + neoValidatorInvoker.WithSigners(acc).Invoke(t, true, "transfer", acc.ScriptHash(), acc.ScriptHash(), amount, nil) + as = getAccountState(t, acc.ScriptHash()) + expect := GasPerBlock * native.GASFactor * VoterRewardRatio / 100 * (uint64(e.Chain.BlockHeight()) / uint64(committeeSize)) + expect = expect * uint64(committeeSize) / uint64(validatorSize+committeeSize) * native.NEOTotalSupply / as.Balance.Uint64() + require.Equal(t, e.Chain.BlockHeight(), as.BalanceHeight) + require.Equal(t, expect, as.LastGasPerVote.Uint64()) + }) +} + +func TestNEO_GetAccountStateInteropAPI(t *testing.T) { + neoValidatorInvoker := newNeoValidatorsClient(t) + e := neoValidatorInvoker.Executor + + cfg := e.Chain.GetConfig() + committeeSize := cfg.GetCommitteeSize(0) + validatorSize := cfg.GetNumOfCNs(0) + advanceChain := func(t *testing.T) { + for i := 0; i < committeeSize; i++ { + neoValidatorInvoker.AddNewBlock(t) + } + } + + amount := int64(1000) + acc := e.NewAccount(t) + neoValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), acc.ScriptHash(), amount, nil) + committee, _ := e.Chain.GetCommittee() + neoValidatorInvoker.WithSigners(e.Validator, e.Validator.(neotest.MultiSigner).Single(0)).Invoke(t, true, "registerCandidate", committee[0].Bytes()) + neoValidatorInvoker.WithSigners(acc).Invoke(t, true, "vote", acc.ScriptHash(), committee[0].Bytes()) + advanceChain(t) + neoValidatorInvoker.WithSigners(acc).Invoke(t, true, "transfer", acc.ScriptHash(), acc.ScriptHash(), amount, nil) + + var hashAStr string + for i := 0; i < util.Uint160Size; i++ { + hashAStr += fmt.Sprintf("%#x", acc.ScriptHash()[i]) + if i != util.Uint160Size-1 { + hashAStr += ", " + } + } + src := `package testaccountstate + import ( + "github.com/nspcc-dev/neo-go/pkg/interop/native/neo" + "github.com/nspcc-dev/neo-go/pkg/interop" + ) + func GetLastGasPerVote() int { + accState := neo.GetAccountState(interop.Hash160{` + hashAStr + `}) + if accState == nil { + panic("nil state") + } + return accState.LastGasPerVote + }` + ctr := neotest.CompileSource(t, e.Validator.ScriptHash(), strings.NewReader(src), &compiler.Options{ + Name: "testaccountstate_contract", + }) + e.DeployContract(t, ctr, nil) + + const ( + GasPerBlock = 5 + VoterRewardRatio = 80 + ) + expect := GasPerBlock * native.GASFactor * VoterRewardRatio / 100 * (uint64(e.Chain.BlockHeight()) / uint64(committeeSize)) + expect = expect * uint64(committeeSize) / uint64(validatorSize+committeeSize) * native.NEOTotalSupply / uint64(amount) + ctrInvoker := e.NewInvoker(ctr.Hash, e.Committee) + ctrInvoker.Invoke(t, stackitem.Make(expect), "getLastGasPerVote") } func TestNEO_CommitteeBountyOnPersist(t *testing.T) { diff --git a/pkg/core/native/native_test/notary_test.go b/pkg/core/native/native_test/notary_test.go index 85e3914fb..1f846cd3e 100644 --- a/pkg/core/native/native_test/notary_test.go +++ b/pkg/core/native/native_test/notary_test.go @@ -12,6 +12,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/neotest" "github.com/nspcc-dev/neo-go/pkg/neotest/chain" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/notary" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -75,19 +76,19 @@ func TestNotary_Pipeline(t *testing.T) { notaryCommitteeInvoker.Invoke(t, false, "lockDepositUntil", multisigHash, int64(depositLock+1)) // `onPayment`: bad token - neoCommitteeInvoker.InvokeFail(t, "only GAS can be accepted for deposit", "transfer", multisigHash, notaryHash, int64(1), []interface{}{nil, int64(depositLock)}) + neoCommitteeInvoker.InvokeFail(t, "only GAS can be accepted for deposit", "transfer", multisigHash, notaryHash, int64(1), ¬ary.OnNEP17PaymentData{Till: uint32(depositLock)}) // `onPayment`: insufficient first deposit - gasCommitteeInvoker.InvokeFail(t, "first deposit can not be less then", "transfer", multisigHash, notaryHash, int64(2*feePerKey-1), []interface{}{nil, int64(depositLock)}) + gasCommitteeInvoker.InvokeFail(t, "first deposit can not be less then", "transfer", multisigHash, notaryHash, int64(2*feePerKey-1), ¬ary.OnNEP17PaymentData{Till: uint32(depositLock)}) // `onPayment`: invalid `data` (missing `till` parameter) - gasCommitteeInvoker.InvokeFail(t, "`data` parameter should be an array of 2 elements", "transfer", multisigHash, notaryHash, 2*feePerKey, []interface{}{nil}) + gasCommitteeInvoker.InvokeFail(t, "`data` parameter should be an array of 2 elements", "transfer", multisigHash, notaryHash, 2*feePerKey, []any{nil}) // `onPayment`: invalid `data` (outdated `till` parameter) - gasCommitteeInvoker.InvokeFail(t, "`till` shouldn't be less then the chain's height", "transfer", multisigHash, notaryHash, 2*feePerKey, []interface{}{nil, int64(0)}) + gasCommitteeInvoker.InvokeFail(t, "`till` shouldn't be less then the chain's height", "transfer", multisigHash, notaryHash, 2*feePerKey, ¬ary.OnNEP17PaymentData{}) // `onPayment`: good - gasCommitteeInvoker.Invoke(t, true, "transfer", multisigHash, notaryHash, 2*feePerKey, []interface{}{nil, int64(depositLock)}) + gasCommitteeInvoker.Invoke(t, true, "transfer", multisigHash, notaryHash, 2*feePerKey, ¬ary.OnNEP17PaymentData{Till: uint32(depositLock)}) checkBalanceOf(t, notaryHash, 2*feePerKey) // `expirationOf`: check `till` was set @@ -97,7 +98,7 @@ func TestNotary_Pipeline(t *testing.T) { notaryCommitteeInvoker.Invoke(t, 2*feePerKey, "balanceOf", multisigHash) // `onPayment`: good second deposit and explicit `to` paramenter - gasCommitteeInvoker.Invoke(t, true, "transfer", multisigHash, notaryHash, feePerKey, []interface{}{multisigHash, int64(depositLock + 1)}) + gasCommitteeInvoker.Invoke(t, true, "transfer", multisigHash, notaryHash, feePerKey, ¬ary.OnNEP17PaymentData{Account: &multisigHash, Till: uint32(depositLock + 1)}) checkBalanceOf(t, notaryHash, 3*feePerKey) // `balanceOf`: check deposited amount for the multisig account @@ -107,17 +108,17 @@ func TestNotary_Pipeline(t *testing.T) { notaryCommitteeInvoker.Invoke(t, depositLock+1, "expirationOf", multisigHash) // `onPayment`: empty payment, should fail because `till` less then the previous one - gasCommitteeInvoker.InvokeFail(t, "`till` shouldn't be less then the previous value", "transfer", multisigHash, notaryHash, int64(0), []interface{}{multisigHash, int64(depositLock)}) + gasCommitteeInvoker.InvokeFail(t, "`till` shouldn't be less then the previous value", "transfer", multisigHash, notaryHash, int64(0), ¬ary.OnNEP17PaymentData{Account: &multisigHash, Till: uint32(depositLock)}) checkBalanceOf(t, notaryHash, 3*feePerKey) notaryCommitteeInvoker.Invoke(t, depositLock+1, "expirationOf", multisigHash) // `onPayment`: empty payment, should fail because `till` less then the chain height - gasCommitteeInvoker.InvokeFail(t, "`till` shouldn't be less then the chain's height", "transfer", multisigHash, notaryHash, int64(0), []interface{}{multisigHash, int64(1)}) + gasCommitteeInvoker.InvokeFail(t, "`till` shouldn't be less then the chain's height", "transfer", multisigHash, notaryHash, int64(0), ¬ary.OnNEP17PaymentData{Account: &multisigHash, Till: uint32(1)}) checkBalanceOf(t, notaryHash, 3*feePerKey) notaryCommitteeInvoker.Invoke(t, depositLock+1, "expirationOf", multisigHash) // `onPayment`: empty payment, should successfully update `till` - gasCommitteeInvoker.Invoke(t, true, "transfer", multisigHash, notaryHash, int64(0), []interface{}{multisigHash, int64(depositLock + 2)}) + gasCommitteeInvoker.Invoke(t, true, "transfer", multisigHash, notaryHash, int64(0), ¬ary.OnNEP17PaymentData{Account: &multisigHash, Till: uint32(depositLock + 2)}) checkBalanceOf(t, notaryHash, 3*feePerKey) notaryCommitteeInvoker.Invoke(t, depositLock+2, "expirationOf", multisigHash) @@ -159,12 +160,12 @@ func TestNotary_Pipeline(t *testing.T) { notaryCommitteeInvoker.Invoke(t, false, "withdraw", multisigHash, accHash) // `onPayment`: good first deposit to other account, should set default `till` even if other `till` value is provided - gasCommitteeInvoker.Invoke(t, true, "transfer", multisigHash, notaryHash, 2*feePerKey, []interface{}{accHash, int64(math.MaxUint32 - 1)}) + gasCommitteeInvoker.Invoke(t, true, "transfer", multisigHash, notaryHash, 2*feePerKey, ¬ary.OnNEP17PaymentData{Account: &accHash, Till: uint32(math.MaxUint32 - 1)}) checkBalanceOf(t, notaryHash, 2*feePerKey) notaryCommitteeInvoker.Invoke(t, 5760+e.Chain.BlockHeight()-1, "expirationOf", accHash) // `onPayment`: good second deposit to other account, shouldn't update `till` even if other `till` value is provided - gasCommitteeInvoker.Invoke(t, true, "transfer", multisigHash, notaryHash, feePerKey, []interface{}{accHash, int64(math.MaxUint32 - 1)}) + gasCommitteeInvoker.Invoke(t, true, "transfer", multisigHash, notaryHash, feePerKey, ¬ary.OnNEP17PaymentData{Account: &accHash, Till: uint32(math.MaxUint32 - 1)}) checkBalanceOf(t, notaryHash, 3*feePerKey) notaryCommitteeInvoker.Invoke(t, 5760+e.Chain.BlockHeight()-3, "expirationOf", accHash) } @@ -184,7 +185,7 @@ func TestNotary_NotaryNodesReward(t *testing.T) { // set Notary nodes and check their balance notaryNodes := make([]*keys.PrivateKey, nNotaryNodes) - notaryNodesPublicKeys := make([]interface{}, nNotaryNodes) + notaryNodesPublicKeys := make([]any, nNotaryNodes) for i := range notaryNodes { notaryNodes[i], err = keys.NewPrivateKey() require.NoError(t, err) @@ -201,7 +202,7 @@ func TestNotary_NotaryNodesReward(t *testing.T) { if !spendFullDeposit { depositAmount += 1_0000 } - gasCommitteeInvoker.Invoke(t, true, "transfer", multisigHash, notaryHash, depositAmount, []interface{}{multisigHash, e.Chain.BlockHeight() + 1}) + gasCommitteeInvoker.Invoke(t, true, "transfer", multisigHash, notaryHash, depositAmount, ¬ary.OnNEP17PaymentData{Account: &multisigHash, Till: e.Chain.BlockHeight() + 1}) // send transaction with Notary contract as a sender tx := transaction.New([]byte{byte(opcode.PUSH1)}, 1_000_000) diff --git a/pkg/core/native/native_test/oracle_test.go b/pkg/core/native/native_test/oracle_test.go index f7ce224f3..54ad6569c 100644 --- a/pkg/core/native/native_test/oracle_test.go +++ b/pkg/core/native/native_test/oracle_test.go @@ -36,7 +36,7 @@ func TestOracle_GetSetPriceCache(t *testing.T) { func putOracleRequest(t *testing.T, oracleInvoker *neotest.ContractInvoker, url string, filter *string, cb string, userData []byte, gas int64, errStr ...string) { - var filtItem interface{} + var filtItem any if filter != nil { filtItem = *filter } @@ -71,7 +71,7 @@ func TestOracle_Request(t *testing.T) { // Designate single Oracle node. oracleNode := e.NewAccount(t) - designationCommitteeInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", int(noderoles.Oracle), []interface{}{oracleNode.(neotest.SingleSigner).Account().PublicKey().Bytes()}) + designationCommitteeInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", int(noderoles.Oracle), []any{oracleNode.(neotest.SingleSigner).Account().PublicKey().Bytes()}) err = oracleNode.(neotest.SingleSigner).Account().ConvertMultisig(1, []*keys.PublicKey{oracleNode.(neotest.SingleSigner).Account().PublicKey()}) require.NoError(t, err) oracleNodeMulti := neotest.NewMultiSigner(oracleNode.(neotest.SingleSigner).Account()) diff --git a/pkg/core/native/oracle_types_test.go b/pkg/core/native/oracle_types_test.go index cba0e20b1..ac640151d 100644 --- a/pkg/core/native/oracle_types_test.go +++ b/pkg/core/native/oracle_types_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/require" ) -func getInvalidTestFunc(actual stackitem.Convertible, value interface{}) func(t *testing.T) { +func getInvalidTestFunc(actual stackitem.Convertible, value any) func(t *testing.T) { return func(t *testing.T) { it := stackitem.Make(value) require.Error(t, actual.FromStackItem(it)) diff --git a/pkg/core/native/std_test.go b/pkg/core/native/std_test.go index 48b81b526..dc1461328 100644 --- a/pkg/core/native/std_test.go +++ b/pkg/core/native/std_test.go @@ -306,7 +306,7 @@ func TestStdLibSerializeDeserialize(t *testing.T) { ic := &interop.Context{VM: vm.New(), DAO: &dao.Simple{}} var actual stackitem.Item - checkSerializeDeserialize := func(t *testing.T, value interface{}, expected stackitem.Item) { + checkSerializeDeserialize := func(t *testing.T, value any, expected stackitem.Item) { require.NotPanics(t, func() { actual = s.serialize(ic, []stackitem.Item{stackitem.Make(value)}) }) @@ -427,7 +427,7 @@ func TestMemorySearch(t *testing.T) { s := newStd() ic := &interop.Context{VM: vm.New()} - check := func(t *testing.T, result int64, args ...interface{}) { + check := func(t *testing.T, result int64, args ...any) { items := make([]stackitem.Item, len(args)) for i := range args { items[i] = stackitem.Make(args[i]) @@ -519,7 +519,7 @@ func TestStringSplit(t *testing.T) { s := newStd() ic := &interop.Context{VM: vm.New()} - check := func(t *testing.T, result []string, str, sep string, remove interface{}) { + check := func(t *testing.T, result []string, str, sep string, remove any) { args := []stackitem.Item{stackitem.Make(str), stackitem.Make(sep)} var actual stackitem.Item if remove == nil { diff --git a/pkg/core/native_designate_test.go b/pkg/core/native_designate_test.go index 533127859..27685b671 100644 --- a/pkg/core/native_designate_test.go +++ b/pkg/core/native_designate_test.go @@ -1,7 +1,6 @@ package core import ( - "errors" "testing" "github.com/nspcc-dev/neo-go/internal/testchain" @@ -30,7 +29,7 @@ func TestDesignate_DesignateAsRole(t *testing.T) { ic.VM.LoadScript([]byte{byte(opcode.RET)}) _, _, err := des.GetDesignatedByRole(bc.dao, 0xFF, 255) - require.True(t, errors.Is(err, native.ErrInvalidRole), "got: %v", err) + require.ErrorIs(t, err, native.ErrInvalidRole) pubs, index, err := des.GetDesignatedByRole(bc.dao, noderoles.Oracle, 255) require.NoError(t, err) @@ -38,20 +37,20 @@ func TestDesignate_DesignateAsRole(t *testing.T) { require.Equal(t, uint32(0), index) err = des.DesignateAsRole(ic, noderoles.Oracle, keys.PublicKeys{}) - require.True(t, errors.Is(err, native.ErrEmptyNodeList), "got: %v", err) + require.ErrorIs(t, err, native.ErrEmptyNodeList) err = des.DesignateAsRole(ic, noderoles.Oracle, make(keys.PublicKeys, 32+1)) - require.True(t, errors.Is(err, native.ErrLargeNodeList), "got: %v", err) + require.ErrorIs(t, err, native.ErrLargeNodeList) priv, err := keys.NewPrivateKey() require.NoError(t, err) pub := priv.PublicKey() err = des.DesignateAsRole(ic, 0xFF, keys.PublicKeys{pub}) - require.True(t, errors.Is(err, native.ErrInvalidRole), "got: %v", err) + require.ErrorIs(t, err, native.ErrInvalidRole) err = des.DesignateAsRole(ic, noderoles.Oracle, keys.PublicKeys{pub}) - require.True(t, errors.Is(err, native.ErrInvalidWitness), "got: %v", err) + require.ErrorIs(t, err, native.ErrInvalidWitness) setSigner(tx, testchain.CommitteeScriptHash()) err = des.DesignateAsRole(ic, noderoles.Oracle, keys.PublicKeys{pub}) diff --git a/pkg/core/prometheus.go b/pkg/core/prometheus.go index f47b60e20..8e429d044 100644 --- a/pkg/core/prometheus.go +++ b/pkg/core/prometheus.go @@ -6,7 +6,7 @@ import ( // Metrics for monitoring service. var ( - //blockHeight prometheus metric. + // blockHeight prometheus metric. blockHeight = prometheus.NewGauge( prometheus.GaugeOpts{ Help: "Current index of processed block", @@ -14,7 +14,7 @@ var ( Namespace: "neogo", }, ) - //persistedHeight prometheus metric. + // persistedHeight prometheus metric. persistedHeight = prometheus.NewGauge( prometheus.GaugeOpts{ Help: "Current persisted block count", @@ -22,7 +22,7 @@ var ( Namespace: "neogo", }, ) - //headerHeight prometheus metric. + // headerHeight prometheus metric. headerHeight = prometheus.NewGauge( prometheus.GaugeOpts{ Help: "Current header height", @@ -30,6 +30,14 @@ var ( Namespace: "neogo", }, ) + // mempoolUnsortedTx prometheus metric. + mempoolUnsortedTx = prometheus.NewGauge( + prometheus.GaugeOpts{ + Help: "Mempool unsorted transactions", + Name: "mempool_unsorted_tx", + Namespace: "neogo", + }, + ) ) func init() { @@ -37,6 +45,7 @@ func init() { blockHeight, persistedHeight, headerHeight, + mempoolUnsortedTx, ) } @@ -51,3 +60,8 @@ func updateHeaderHeightMetric(hHeight uint32) { func updateBlockHeightMetric(bHeight uint32) { blockHeight.Set(float64(bHeight)) } + +// updateMempoolMetrics updates metric of the number of unsorted txs inside the mempool. +func updateMempoolMetrics(unsortedTxnLen int) { + mempoolUnsortedTx.Set(float64(unsortedTxnLen)) +} diff --git a/pkg/core/state/native_state.go b/pkg/core/state/native_state.go index 48eef1a0f..5e4d7c4c3 100644 --- a/pkg/core/state/native_state.go +++ b/pkg/core/state/native_state.go @@ -19,8 +19,9 @@ type NEP17Balance struct { // NEOBalance represents the balance state of a NEO-token. type NEOBalance struct { NEP17Balance - BalanceHeight uint32 - VoteTo *keys.PublicKey + BalanceHeight uint32 + VoteTo *keys.PublicKey + LastGasPerVote big.Int } // NEP17BalanceFromBytes converts the serialized NEP17Balance to a structure. @@ -125,6 +126,7 @@ func (s *NEOBalance) ToStackItem() (stackitem.Item, error) { stackitem.NewBigInteger(&s.Balance), stackitem.NewBigInteger(big.NewInt(int64(s.BalanceHeight))), voteItem, + stackitem.NewBigInteger(&s.LastGasPerVote), }), nil } @@ -157,5 +159,12 @@ func (s *NEOBalance) FromStackItem(item stackitem.Item) error { return fmt.Errorf("invalid public key bytes: %w", err) } s.VoteTo = pub + if len(structItem) >= 4 { + lastGasPerVote, err := structItem[3].TryInteger() + if err != nil { + return fmt.Errorf("invalid last vote reward per neo stackitem: %w", err) + } + s.LastGasPerVote = *lastGasPerVote + } return nil } diff --git a/pkg/core/transaction/attribute.go b/pkg/core/transaction/attribute.go index 91f8b66e5..0044f4b5f 100644 --- a/pkg/core/transaction/attribute.go +++ b/pkg/core/transaction/attribute.go @@ -17,7 +17,7 @@ type Attribute struct { // Anonymous interface fields are not considered anonymous by // json lib and marshaling Value together with type makes code // harder to follow. - toJSONMap(map[string]interface{}) + toJSONMap(map[string]any) } } @@ -70,7 +70,7 @@ func (attr *Attribute) EncodeBinary(bw *io.BinWriter) { // MarshalJSON implements the json Marshaller interface. func (attr *Attribute) MarshalJSON() ([]byte, error) { - m := map[string]interface{}{"type": attr.Type.String()} + m := map[string]any{"type": attr.Type.String()} if attr.Value != nil { attr.Value.toJSONMap(m) } diff --git a/pkg/core/transaction/conflicts.go b/pkg/core/transaction/conflicts.go index 01b62e441..50005fb21 100644 --- a/pkg/core/transaction/conflicts.go +++ b/pkg/core/transaction/conflicts.go @@ -20,6 +20,6 @@ func (c *Conflicts) EncodeBinary(w *io.BinWriter) { c.Hash.EncodeBinary(w) } -func (c *Conflicts) toJSONMap(m map[string]interface{}) { +func (c *Conflicts) toJSONMap(m map[string]any) { m["hash"] = c.Hash } diff --git a/pkg/core/transaction/not_valid_before.go b/pkg/core/transaction/not_valid_before.go index 232009f3d..6fe9b7b56 100644 --- a/pkg/core/transaction/not_valid_before.go +++ b/pkg/core/transaction/not_valid_before.go @@ -19,6 +19,6 @@ func (n *NotValidBefore) EncodeBinary(w *io.BinWriter) { w.WriteU32LE(n.Height) } -func (n *NotValidBefore) toJSONMap(m map[string]interface{}) { +func (n *NotValidBefore) toJSONMap(m map[string]any) { m["height"] = n.Height } diff --git a/pkg/core/transaction/notary_assisted.go b/pkg/core/transaction/notary_assisted.go index 5a7de29a4..747711165 100644 --- a/pkg/core/transaction/notary_assisted.go +++ b/pkg/core/transaction/notary_assisted.go @@ -19,6 +19,6 @@ func (n *NotaryAssisted) EncodeBinary(w *io.BinWriter) { w.WriteB(n.NKeys) } -func (n *NotaryAssisted) toJSONMap(m map[string]interface{}) { +func (n *NotaryAssisted) toJSONMap(m map[string]any) { m["nkeys"] = n.NKeys } diff --git a/pkg/core/transaction/oracle.go b/pkg/core/transaction/oracle.go index 2d396d2f7..68459ef56 100644 --- a/pkg/core/transaction/oracle.go +++ b/pkg/core/transaction/oracle.go @@ -111,7 +111,7 @@ func (r *OracleResponse) EncodeBinary(w *io.BinWriter) { w.WriteVarBytes(r.Result) } -func (r *OracleResponse) toJSONMap(m map[string]interface{}) { +func (r *OracleResponse) toJSONMap(m map[string]any) { m["id"] = r.ID m["code"] = r.Code m["result"] = r.Result diff --git a/pkg/core/transaction/oracle_test.go b/pkg/core/transaction/oracle_test.go index f0ec24a09..89c4d392e 100644 --- a/pkg/core/transaction/oracle_test.go +++ b/pkg/core/transaction/oracle_test.go @@ -2,7 +2,6 @@ package transaction import ( "encoding/json" - "errors" "math/rand" "testing" @@ -41,7 +40,7 @@ func TestOracleResponse_EncodeBinary(t *testing.T) { require.NoError(t, err) err = testserdes.DecodeBinary(bs, new(OracleResponse)) - require.True(t, errors.Is(err, ErrInvalidResponseCode), "got: %v", err) + require.ErrorIs(t, err, ErrInvalidResponseCode) }) t.Run("InvalidResult", func(t *testing.T) { r := &OracleResponse{ @@ -53,7 +52,7 @@ func TestOracleResponse_EncodeBinary(t *testing.T) { require.NoError(t, err) err = testserdes.DecodeBinary(bs, new(OracleResponse)) - require.True(t, errors.Is(err, ErrInvalidResult), "got: %v", err) + require.ErrorIs(t, err, ErrInvalidResult) }) }) } @@ -68,7 +67,7 @@ func TestOracleResponse_toJSONMap(t *testing.T) { b1, err := json.Marshal(r) require.NoError(t, err) - m := map[string]interface{}{} + m := map[string]any{} r.toJSONMap(m) b2, err := json.Marshal(m) require.NoError(t, err) diff --git a/pkg/core/transaction/reserved.go b/pkg/core/transaction/reserved.go index cc622c708..677458aa3 100644 --- a/pkg/core/transaction/reserved.go +++ b/pkg/core/transaction/reserved.go @@ -19,6 +19,6 @@ func (e *Reserved) EncodeBinary(w *io.BinWriter) { w.WriteVarBytes(e.Value) } -func (e *Reserved) toJSONMap(m map[string]interface{}) { +func (e *Reserved) toJSONMap(m map[string]any) { m["value"] = e.Value } diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index 3c201d042..eba0c237d 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -193,7 +193,7 @@ func (t *Transaction) decodeBinaryNoSize(br *io.BinReader, buf []byte) { br.Err = errors.New("too many witnesses") return } else if int(nscripts) != len(t.Signers) { - br.Err = fmt.Errorf("%w: %d vs %d", ErrInvalidWitnessNum, len(t.Signers), len(t.Scripts)) + br.Err = fmt.Errorf("%w: %d vs %d", ErrInvalidWitnessNum, len(t.Signers), nscripts) return } t.Scripts = make([]Witness, nscripts) diff --git a/pkg/core/transaction/transaction_test.go b/pkg/core/transaction/transaction_test.go index 23c274bfc..9f8340bc4 100644 --- a/pkg/core/transaction/transaction_test.go +++ b/pkg/core/transaction/transaction_test.go @@ -4,7 +4,6 @@ import ( "encoding/base64" "encoding/hex" "encoding/json" - "errors" "math" "testing" @@ -125,7 +124,7 @@ func TestDecodingTxWithInvalidWitnessesNumber(t *testing.T) { tx.Scripts = []Witness{{InvocationScript: []byte{}, VerificationScript: []byte{}}, {InvocationScript: []byte{}, VerificationScript: []byte{}}} data, err := testserdes.EncodeBinary(tx) require.NoError(t, err) - require.True(t, errors.Is(testserdes.DecodeBinary(data, new(Transaction)), ErrInvalidWitnessNum)) + require.ErrorIs(t, testserdes.DecodeBinary(data, new(Transaction)), ErrInvalidWitnessNum) } func TestUnmarshalNeoFSTX(t *testing.T) { @@ -216,32 +215,32 @@ func TestTransaction_isValid(t *testing.T) { t.Run("InvalidVersion", func(t *testing.T) { tx := newTx() tx.Version = 1 - require.True(t, errors.Is(tx.isValid(), ErrInvalidVersion)) + require.ErrorIs(t, tx.isValid(), ErrInvalidVersion) }) t.Run("NegativeSystemFee", func(t *testing.T) { tx := newTx() tx.SystemFee = -1 - require.True(t, errors.Is(tx.isValid(), ErrNegativeSystemFee)) + require.ErrorIs(t, tx.isValid(), ErrNegativeSystemFee) }) t.Run("NegativeNetworkFee", func(t *testing.T) { tx := newTx() tx.NetworkFee = -1 - require.True(t, errors.Is(tx.isValid(), ErrNegativeNetworkFee)) + require.ErrorIs(t, tx.isValid(), ErrNegativeNetworkFee) }) t.Run("TooBigFees", func(t *testing.T) { tx := newTx() tx.SystemFee = math.MaxInt64 - tx.NetworkFee + 1 - require.True(t, errors.Is(tx.isValid(), ErrTooBigFees)) + require.ErrorIs(t, tx.isValid(), ErrTooBigFees) }) t.Run("EmptySigners", func(t *testing.T) { tx := newTx() tx.Signers = tx.Signers[:0] - require.True(t, errors.Is(tx.isValid(), ErrEmptySigners)) + require.ErrorIs(t, tx.isValid(), ErrEmptySigners) }) t.Run("NonUniqueSigners", func(t *testing.T) { tx := newTx() tx.Signers[1].Account = tx.Signers[0].Account - require.True(t, errors.Is(tx.isValid(), ErrNonUniqueSigners)) + require.ErrorIs(t, tx.isValid(), ErrNonUniqueSigners) }) t.Run("MultipleHighPriority", func(t *testing.T) { tx := newTx() @@ -249,7 +248,7 @@ func TestTransaction_isValid(t *testing.T) { {Type: HighPriority}, {Type: HighPriority}, } - require.True(t, errors.Is(tx.isValid(), ErrInvalidAttribute)) + require.ErrorIs(t, tx.isValid(), ErrInvalidAttribute) }) t.Run("MultipleOracle", func(t *testing.T) { tx := newTx() @@ -257,12 +256,12 @@ func TestTransaction_isValid(t *testing.T) { {Type: OracleResponseT}, {Type: OracleResponseT}, } - require.True(t, errors.Is(tx.isValid(), ErrInvalidAttribute)) + require.ErrorIs(t, tx.isValid(), ErrInvalidAttribute) }) t.Run("NoScript", func(t *testing.T) { tx := newTx() tx.Script = []byte{} - require.True(t, errors.Is(tx.isValid(), ErrEmptyScript)) + require.ErrorIs(t, tx.isValid(), ErrEmptyScript) }) } diff --git a/pkg/core/transaction/witness_condition.go b/pkg/core/transaction/witness_condition.go index 65a73b09e..b34a8d389 100644 --- a/pkg/core/transaction/witness_condition.go +++ b/pkg/core/transaction/witness_condition.go @@ -631,7 +631,7 @@ func unmarshalConditionJSON(data []byte, maxDepth int) (WitnessCondition, error) return res, nil } -func condToStackItem(typ WitnessConditionType, c interface{}) stackitem.Item { +func condToStackItem(typ WitnessConditionType, c any) stackitem.Item { res := make([]stackitem.Item, 0, 2) res = append(res, stackitem.NewBigInteger(big.NewInt(int64(typ)))) switch typ { diff --git a/pkg/crypto/keys/publickey.go b/pkg/crypto/keys/publickey.go index 674ca8e76..c1bf5c3b0 100644 --- a/pkg/crypto/keys/publickey.go +++ b/pkg/crypto/keys/publickey.go @@ -181,7 +181,7 @@ func (p *PublicKey) UncompressedBytes() []byte { func NewPublicKeyFromASN1(data []byte) (*PublicKey, error) { var ( err error - pubkey interface{} + pubkey any ) if pubkey, err = x509.ParsePKIXPublicKey(data); err != nil { return nil, err diff --git a/pkg/encoding/fixedn/fixed8.go b/pkg/encoding/fixedn/fixed8.go index 3bea85b3a..ad28d5133 100644 --- a/pkg/encoding/fixedn/fixed8.go +++ b/pkg/encoding/fixedn/fixed8.go @@ -85,7 +85,7 @@ func (f *Fixed8) UnmarshalJSON(data []byte) error { } // UnmarshalYAML implements the yaml unmarshaler interface. -func (f *Fixed8) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (f *Fixed8) UnmarshalYAML(unmarshal func(any) error) error { var s string err := unmarshal(&s) if err != nil { @@ -109,7 +109,7 @@ func (f Fixed8) MarshalJSON() ([]byte, error) { } // MarshalYAML implements the yaml marshaller interface. -func (f Fixed8) MarshalYAML() (interface{}, error) { +func (f Fixed8) MarshalYAML() (any, error) { return f.String(), nil } diff --git a/pkg/interop/contract/contract.go b/pkg/interop/contract/contract.go index 2f3843ee3..0b4ad6b58 100644 --- a/pkg/interop/contract/contract.go +++ b/pkg/interop/contract/contract.go @@ -47,6 +47,6 @@ func GetCallFlags() CallFlag { // (20 bytes in BE form) using the provided arguments and call flags. // It returns whatever this contract returns. This function uses // `System.Contract.Call` syscall. -func Call(scriptHash interop.Hash160, method string, f CallFlag, args ...interface{}) interface{} { +func Call(scriptHash interop.Hash160, method string, f CallFlag, args ...any) any { return neogointernal.Syscall4("System.Contract.Call", scriptHash, method, f, args) } diff --git a/pkg/interop/convert/convert.go b/pkg/interop/convert/convert.go index 653f55ca1..0f4eb8dc0 100644 --- a/pkg/interop/convert/convert.go +++ b/pkg/interop/convert/convert.go @@ -2,21 +2,21 @@ package convert // ToInteger converts it's argument to an Integer. -func ToInteger(v interface{}) int { +func ToInteger(v any) int { return v.(int) } // ToBytes converts it's argument to a Buffer VM type. -func ToBytes(v interface{}) []byte { +func ToBytes(v any) []byte { return v.([]byte) } // ToString converts it's argument to a ByteString VM type. -func ToString(v interface{}) string { +func ToString(v any) string { return v.(string) } // ToBool converts it's argument to a Boolean. -func ToBool(v interface{}) bool { +func ToBool(v any) bool { return v.(bool) } diff --git a/pkg/interop/doc.go b/pkg/interop/doc.go index a78d25b72..b9d083500 100644 --- a/pkg/interop/doc.go +++ b/pkg/interop/doc.go @@ -14,6 +14,7 @@ correspond to smartcontract and VM types: []byte - ByteArray (Buffer in VM) string - String (ByteString in VM) (interface{})(nil) - Any + (any)(nil) - Any non-byte slice - Array map[K]V - map diff --git a/pkg/interop/go.mod b/pkg/interop/go.mod index 2e0512988..d4822fa7e 100644 --- a/pkg/interop/go.mod +++ b/pkg/interop/go.mod @@ -1,3 +1,3 @@ module github.com/nspcc-dev/neo-go/pkg/interop -go 1.17 \ No newline at end of file +go 1.18 diff --git a/pkg/interop/iterator/iterator.go b/pkg/interop/iterator/iterator.go index 391e64cf3..243d02851 100644 --- a/pkg/interop/iterator/iterator.go +++ b/pkg/interop/iterator/iterator.go @@ -24,6 +24,6 @@ func Next(it Iterator) bool { // For slices, the result is just value. // For maps, the result can be cast to a slice of 2 elements: a key and a value. // For storage iterators, refer to `storage.FindFlags` documentation. -func Value(it Iterator) interface{} { +func Value(it Iterator) any { return neogointernal.Syscall1("System.Iterator.Value", it) } diff --git a/pkg/interop/lib/contract/contract.go b/pkg/interop/lib/contract/contract.go index 735db00ba..8011ae6f9 100644 --- a/pkg/interop/lib/contract/contract.go +++ b/pkg/interop/lib/contract/contract.go @@ -11,7 +11,7 @@ import ( // (20 bytes in BE form) using the provided arguments and call flags. It fails // if the contract has version mismatch. It returns whatever this contract // returns. This function uses `System.Contract.Call` syscall. -func CallWithVersion(scriptHash interop.Hash160, version int, method string, f contract.CallFlag, args ...interface{}) interface{} { +func CallWithVersion(scriptHash interop.Hash160, version int, method string, f contract.CallFlag, args ...any) any { cs := management.GetContract(scriptHash) if cs == nil { panic("unknown contract") diff --git a/pkg/interop/native/crypto/crypto.go b/pkg/interop/native/crypto/crypto.go index 8055a6b07..2d82357c0 100644 --- a/pkg/interop/native/crypto/crypto.go +++ b/pkg/interop/native/crypto/crypto.go @@ -43,3 +43,52 @@ func Murmur32(b []byte, seed int) []byte { func VerifyWithECDsa(msg []byte, pub interop.PublicKey, sig interop.Signature, curve NamedCurve) bool { return neogointernal.CallWithToken(Hash, "verifyWithECDsa", int(contract.NoneFlag), msg, pub, sig, curve).(bool) } + +// Bls12381Point represents BLS12-381 curve point (G1 or G2 in the Affine or +// Jacobian form or GT). Bls12381Point structure is needed for the operations +// with the curve's points (serialization, addition, multiplication, pairing and +// equality checks). It's an opaque type that can only be created properly by +// Bls12381Deserialize, Bls12381Add, Bls12381Mul or Bls12381Pairing. The only +// way to expose the Bls12381Point out of the runtime to the outside world is by +// serializing it with Bls12381Serialize method call. +type Bls12381Point struct{} + +// Bls12381Serialize calls `bls12381Serialize` method of native CryptoLib contract +// and serializes given BLS12-381 point into byte array. +func Bls12381Serialize(g Bls12381Point) []byte { + return neogointernal.CallWithToken(Hash, "bls12381Serialize", int(contract.NoneFlag), g).([]byte) +} + +// Bls12381Deserialize calls `bls12381Deserialize` method of native CryptoLib +// contract and deserializes given BLS12-381 point from byte array. +func Bls12381Deserialize(data []byte) Bls12381Point { + return neogointernal.CallWithToken(Hash, "bls12381Deserialize", int(contract.NoneFlag), data).(Bls12381Point) +} + +// Bls12381Equal calls `bls12381Equal` method of native CryptoLib contract and +// checks whether two BLS12-381 points are equal. +func Bls12381Equal(x, y Bls12381Point) bool { + return neogointernal.CallWithToken(Hash, "bls12381Equal", int(contract.NoneFlag), x, y).(bool) +} + +// Bls12381Add calls `bls12381Add` method of native CryptoLib contract and +// performs addition operation over two BLS12-381 points. +func Bls12381Add(x, y Bls12381Point) Bls12381Point { + return neogointernal.CallWithToken(Hash, "bls12381Add", int(contract.NoneFlag), x, y).(Bls12381Point) +} + +// Bls12381Mul calls `bls12381Mul` method of native CryptoLib contract and +// performs multiplication operation over BLS12-381 point and the given scalar +// multiplicator. The multiplicator is the serialized LE representation of the +// field element stored on 4 words (uint64) with 32-bytes length. The last +// argument denotes whether the multiplicator should be negative. +func Bls12381Mul(x Bls12381Point, mul []byte, neg bool) Bls12381Point { + return neogointernal.CallWithToken(Hash, "bls12381Mul", int(contract.NoneFlag), x, mul, neg).(Bls12381Point) +} + +// Bls12381Pairing calls `bls12381Pairing` method of native CryptoLib contract and +// performs pairing operation over two BLS12-381 points which must be G1 and G2 either +// in Affine or Jacobian forms. The result of this operation is GT point. +func Bls12381Pairing(g1, g2 Bls12381Point) Bls12381Point { + return neogointernal.CallWithToken(Hash, "bls12381Pairing", int(contract.NoneFlag), g1, g2).(Bls12381Point) +} diff --git a/pkg/interop/native/gas/gas.go b/pkg/interop/native/gas/gas.go index 538ac0a36..338a6f99d 100644 --- a/pkg/interop/native/gas/gas.go +++ b/pkg/interop/native/gas/gas.go @@ -34,7 +34,7 @@ func BalanceOf(addr interop.Hash160) int { } // Transfer represents `transfer` method of GAS native contract. -func Transfer(from, to interop.Hash160, amount int, data interface{}) bool { +func Transfer(from, to interop.Hash160, amount int, data any) bool { return neogointernal.CallWithToken(Hash, "transfer", int(contract.All), from, to, amount, data).(bool) } diff --git a/pkg/interop/native/ledger/block.go b/pkg/interop/native/ledger/block.go index 84e1a6407..0164803cb 100644 --- a/pkg/interop/native/ledger/block.go +++ b/pkg/interop/native/ledger/block.go @@ -51,5 +51,5 @@ type BlockSR struct { // ToBlockSR converts Block into BlockSR for chains with StateRootInHeader option. func (b *Block) ToBlockSR() *BlockSR { - return interface{}(b).(*BlockSR) + return any(b).(*BlockSR) } diff --git a/pkg/interop/native/ledger/ledger.go b/pkg/interop/native/ledger/ledger.go index f26a15fa4..591dccaea 100644 --- a/pkg/interop/native/ledger/ledger.go +++ b/pkg/interop/native/ledger/ledger.go @@ -39,7 +39,7 @@ func CurrentIndex() int { } // GetBlock represents `getBlock` method of Ledger native contract. -func GetBlock(indexOrHash interface{}) *Block { +func GetBlock(indexOrHash any) *Block { return neogointernal.CallWithToken(Hash, "getBlock", int(contract.ReadStates), indexOrHash).(*Block) } @@ -54,7 +54,7 @@ func GetTransactionHeight(hash interop.Hash256) int { } // GetTransactionFromBlock represents `getTransactionFromBlock` method of Ledger native contract. -func GetTransactionFromBlock(indexOrHash interface{}, txIndex int) *Transaction { +func GetTransactionFromBlock(indexOrHash any, txIndex int) *Transaction { return neogointernal.CallWithToken(Hash, "getTransactionFromBlock", int(contract.ReadStates), indexOrHash, txIndex).(*Transaction) } diff --git a/pkg/interop/native/ledger/transaction_signer.go b/pkg/interop/native/ledger/transaction_signer.go index 04f04772b..05270faf6 100644 --- a/pkg/interop/native/ledger/transaction_signer.go +++ b/pkg/interop/native/ledger/transaction_signer.go @@ -82,7 +82,7 @@ type WitnessCondition struct { // WitnessCalledByContract -> interop.Hash160 // WitnessCalledByGroup -> interop.PublicKey // WitnessCalledByEntry -> doesn't have value, thus, an attempt to access the Value leads to runtime exception. - Value interface{} + Value any } // WitnessConditionType represents the type of rule-based witness condition. diff --git a/pkg/interop/native/management/contract.go b/pkg/interop/native/management/contract.go index 31778e2e4..b05fc66bc 100644 --- a/pkg/interop/native/management/contract.go +++ b/pkg/interop/native/management/contract.go @@ -40,7 +40,7 @@ type Manifest struct { ABI ABI Permissions []Permission Trusts []interop.Hash160 - Extra interface{} + Extra any } // ABI represents contract's ABI. diff --git a/pkg/interop/native/management/management.go b/pkg/interop/native/management/management.go index 8f6517242..9e7a632c3 100644 --- a/pkg/interop/native/management/management.go +++ b/pkg/interop/native/management/management.go @@ -29,7 +29,7 @@ func Deploy(script, manifest []byte) *Contract { } // DeployWithData represents `deploy` method of Management native contract. -func DeployWithData(script, manifest []byte, data interface{}) *Contract { +func DeployWithData(script, manifest []byte, data any) *Contract { return neogointernal.CallWithToken(Hash, "deploy", int(contract.All), script, manifest, data).(*Contract) } @@ -80,7 +80,7 @@ func Update(script, manifest []byte) { } // UpdateWithData represents `update` method of Management native contract. -func UpdateWithData(script, manifest []byte, data interface{}) { +func UpdateWithData(script, manifest []byte, data any) { neogointernal.CallWithTokenNoRet(Hash, "update", int(contract.All), script, manifest, data) } diff --git a/pkg/interop/native/neo/neo.go b/pkg/interop/native/neo/neo.go index 4c5e4c9b0..4fde83829 100644 --- a/pkg/interop/native/neo/neo.go +++ b/pkg/interop/native/neo/neo.go @@ -15,9 +15,10 @@ import ( // AccountState contains info about a NEO holder. type AccountState struct { - Balance int - Height int - VoteTo interop.PublicKey + Balance int + Height int + VoteTo interop.PublicKey + LastGasPerVote int } // Hash represents NEO contract hash. @@ -44,7 +45,7 @@ func BalanceOf(addr interop.Hash160) int { } // Transfer represents `transfer` method of NEO native contract. -func Transfer(from, to interop.Hash160, amount int, data interface{}) bool { +func Transfer(from, to interop.Hash160, amount int, data any) bool { return neogointernal.CallWithToken(Hash, "transfer", int(contract.All), from, to, amount, data).(bool) } diff --git a/pkg/interop/native/oracle/oracle.go b/pkg/interop/native/oracle/oracle.go index 46a627a17..14155b5df 100644 --- a/pkg/interop/native/oracle/oracle.go +++ b/pkg/interop/native/oracle/oracle.go @@ -50,7 +50,7 @@ const MinimumResponseGas = 10_000_000 // of the same contract that invokes Request and it must have the following // signature for correct invocation: // -// - Method(url string, userData interface{}, code int, result []byte) +// - Method(url string, userData any, code int, result []byte) // where url is the same url specified for Request, userData is anything // passed in the next parameter, code is the status of the reply and // result is the data returned from the request if any. @@ -64,7 +64,7 @@ const MinimumResponseGas = 10_000_000 // GAS is used for oracle transaction's network and system fees, // so it should be enough to pay for reply data as well as // its processing. -func Request(url string, filter []byte, cb string, userData interface{}, gasForResponse int) { +func Request(url string, filter []byte, cb string, userData any, gasForResponse int) { neogointernal.CallWithTokenNoRet(Hash, "request", int(contract.States|contract.AllowNotify), url, filter, cb, userData, gasForResponse) diff --git a/pkg/interop/native/std/std.go b/pkg/interop/native/std/std.go index 10d7ddd47..e79e32d3d 100644 --- a/pkg/interop/native/std/std.go +++ b/pkg/interop/native/std/std.go @@ -16,14 +16,14 @@ const Hash = "\xc0\xef\x39\xce\xe0\xe4\xe9\x25\xc6\xc2\xa0\x6a\x79\xe1\x44\x0d\x // any given item into a byte slice. It works for all regular VM types (not ones // from interop package) and allows to save them in the storage or pass them into Notify // and then Deserialize them on the next run or in the external event receiver. -func Serialize(item interface{}) []byte { +func Serialize(item any) []byte { return neogointernal.CallWithToken(Hash, "serialize", int(contract.NoneFlag), item).([]byte) } // Deserialize calls `deserialize` method of StdLib native contract and unpacks // a previously serialized value from a byte slice, it's the opposite of Serialize. -func Deserialize(b []byte) interface{} { +func Deserialize(b []byte) any { return neogointernal.CallWithToken(Hash, "deserialize", int(contract.NoneFlag), b) } @@ -37,8 +37,9 @@ func Deserialize(b []byte) interface{} { // string -> base64 encoded sequence of underlying bytes // (u)int* -> integer, only value in -2^53..2^53 are allowed // []interface{} -> json array +// []any -> json array // map[type1]type2 -> json object with string keys marshaled as strings (not base64). -func JSONSerialize(item interface{}) []byte { +func JSONSerialize(item any) []byte { return neogointernal.CallWithToken(Hash, "jsonSerialize", int(contract.NoneFlag), item).([]byte) } @@ -52,7 +53,7 @@ func JSONSerialize(item interface{}) []byte { // null -> interface{}(nil) // arrays -> []interface{} // maps -> map[string]interface{} -func JSONDeserialize(data []byte) interface{} { +func JSONDeserialize(data []byte) any { return neogointernal.CallWithToken(Hash, "jsonDeserialize", int(contract.NoneFlag), data) } diff --git a/pkg/interop/neogointernal/call.go b/pkg/interop/neogointernal/call.go index 0309bfe94..f32e65fad 100644 --- a/pkg/interop/neogointernal/call.go +++ b/pkg/interop/neogointernal/call.go @@ -3,10 +3,10 @@ package neogointernal // CallWithToken performs contract call using CALLT instruction. It only works // for static script hashes and methods, requiring additional metadata compared to // ordinary contract.Call. It's more efficient though. -func CallWithToken(scriptHash string, method string, flags int, args ...interface{}) interface{} { +func CallWithToken(scriptHash string, method string, flags int, args ...any) any { return nil } // CallWithTokenNoRet is a version of CallWithToken that does not return anything. -func CallWithTokenNoRet(scriptHash string, method string, flags int, args ...interface{}) { +func CallWithTokenNoRet(scriptHash string, method string, flags int, args ...any) { } diff --git a/pkg/interop/neogointernal/opcode.go b/pkg/interop/neogointernal/opcode.go index 1667c5e05..a64e85733 100644 --- a/pkg/interop/neogointernal/opcode.go +++ b/pkg/interop/neogointernal/opcode.go @@ -5,20 +5,20 @@ func Opcode0NoReturn(op string) { } // Opcode1 emits opcode with 1 argument. -func Opcode1(op string, arg interface{}) interface{} { +func Opcode1(op string, arg any) any { return nil } // Opcode2 emits opcode with 2 arguments. -func Opcode2(op string, arg1, arg2 interface{}) interface{} { +func Opcode2(op string, arg1, arg2 any) any { return nil } // Opcode2NoReturn emits opcode with 2 arguments. -func Opcode2NoReturn(op string, arg1, arg2 interface{}) { +func Opcode2NoReturn(op string, arg1, arg2 any) { } // Opcode3 emits opcode with 3 arguments. -func Opcode3(op string, arg1, arg2, arg3 interface{}) interface{} { +func Opcode3(op string, arg1, arg2, arg3 any) any { return nil } diff --git a/pkg/interop/neogointernal/syscall.go b/pkg/interop/neogointernal/syscall.go index f42ed2735..d106d2c1a 100644 --- a/pkg/interop/neogointernal/syscall.go +++ b/pkg/interop/neogointernal/syscall.go @@ -1,7 +1,7 @@ package neogointernal // Syscall0 performs syscall with 0 arguments. -func Syscall0(name string) interface{} { +func Syscall0(name string) any { return nil } @@ -10,37 +10,37 @@ func Syscall0NoReturn(name string) { } // Syscall1 performs syscall with 1 arguments. -func Syscall1(name string, arg interface{}) interface{} { +func Syscall1(name string, arg any) any { return nil } // Syscall1NoReturn performs syscall with 1 arguments. -func Syscall1NoReturn(name string, arg interface{}) { +func Syscall1NoReturn(name string, arg any) { } // Syscall2 performs syscall with 2 arguments. -func Syscall2(name string, arg1, arg2 interface{}) interface{} { +func Syscall2(name string, arg1, arg2 any) any { return nil } // Syscall2NoReturn performs syscall with 2 arguments. -func Syscall2NoReturn(name string, arg1, arg2 interface{}) { +func Syscall2NoReturn(name string, arg1, arg2 any) { } // Syscall3 performs syscall with 3 arguments. -func Syscall3(name string, arg1, arg2, arg3 interface{}) interface{} { +func Syscall3(name string, arg1, arg2, arg3 any) any { return nil } // Syscall3NoReturn performs syscall with 3 arguments. -func Syscall3NoReturn(name string, arg1, arg2, arg3 interface{}) { +func Syscall3NoReturn(name string, arg1, arg2, arg3 any) { } // Syscall4 performs syscall with 4 arguments. -func Syscall4(name string, arg1, arg2, arg3, arg4 interface{}) interface{} { +func Syscall4(name string, arg1, arg2, arg3, arg4 any) any { return nil } // Syscall4NoReturn performs syscall with 4 arguments. -func Syscall4NoReturn(name string, arg1, arg2, arg3, arg4 interface{}) { +func Syscall4NoReturn(name string, arg1, arg2, arg3, arg4 any) { } diff --git a/pkg/interop/runtime/runtime.go b/pkg/interop/runtime/runtime.go index ab8415fb2..a23039473 100644 --- a/pkg/interop/runtime/runtime.go +++ b/pkg/interop/runtime/runtime.go @@ -41,7 +41,7 @@ func CheckWitness(hashOrKey []byte) bool { // script can ABORT the transaction or THROW an exception, make sure you // appropriately handle exceptions if bytecode comes from untrusted source. // This function uses `System.Runtime.LoadScript` syscall. -func LoadScript(script []byte, f contract.CallFlag, args ...interface{}) interface{} { +func LoadScript(script []byte, f contract.CallFlag, args ...any) any { return neogointernal.Syscall3("System.Runtime.LoadScript", script, f, args) } @@ -58,7 +58,7 @@ func Log(message string) { // part of contract's API to external systems, these events can be monitored // from outside and act upon accordingly. This function uses // `System.Runtime.Notify` syscall. -func Notify(name string, args ...interface{}) { +func Notify(name string, args ...any) { neogointernal.Syscall2NoReturn("System.Runtime.Notify", name, args) } @@ -105,8 +105,8 @@ func GasLeft() int { // 'nil' literal means no filtering. It returns slice consisting of following elements: // [ scripthash of notification's contract , emitted item ]. // This function uses `System.Runtime.GetNotifications` syscall. -func GetNotifications(h interop.Hash160) [][]interface{} { - return neogointernal.Syscall1("System.Runtime.GetNotifications", h).([][]interface{}) +func GetNotifications(h interop.Hash160) [][]any { + return neogointernal.Syscall1("System.Runtime.GetNotifications", h).([][]any) } // GetInvocationCounter returns how many times current contract was invoked during current tx execution. diff --git a/pkg/interop/storage/storage.go b/pkg/interop/storage/storage.go index 15f5de118..30b649f7a 100644 --- a/pkg/interop/storage/storage.go +++ b/pkg/interop/storage/storage.go @@ -36,6 +36,8 @@ const ( PickField0 FindFlags = 1 << 4 // PickField1 is used to get second field in a serialized struct or array. PickField1 FindFlags = 1 << 5 + // Backwards is used to iterate over elements in reversed (descending) order. + Backwards FindFlags = 1 << 7 ) // ConvertContextToReadOnly returns new context from the given one, but with @@ -61,25 +63,25 @@ func GetReadOnlyContext() Context { } // Put saves given value with given key in the storage using given Context. -// Even though it accepts interface{} for both, you can only pass simple types -// there like string, []byte, int or bool (not structures or slices of more -// complex types). To put more complex types there serialize them first using -// runtime.Serialize. This function uses `System.Storage.Put` syscall. -func Put(ctx Context, key interface{}, value interface{}) { +// Even though it accepts interface{} hidden under `any` for both, you can only +// pass simple types there like string, []byte, int or bool (not structures or +// slices of more complex types). To put more complex types there serialize them +// first using runtime.Serialize. This function uses `System.Storage.Put` syscall. +func Put(ctx Context, key any, value any) { neogointernal.Syscall3NoReturn("System.Storage.Put", ctx, key, value) } // Get retrieves value stored for the given key using given Context. See Put // documentation on possible key and value types. If the value is not present in // the database it returns nil. This function uses `System.Storage.Get` syscall. -func Get(ctx Context, key interface{}) interface{} { +func Get(ctx Context, key any) any { return neogointernal.Syscall2("System.Storage.Get", ctx, key) } // Delete removes key-value pair from storage by the given key using given // Context. See Put documentation on possible key types. This function uses // `System.Storage.Delete` syscall. -func Delete(ctx Context, key interface{}) { +func Delete(ctx Context, key any) { neogointernal.Syscall2NoReturn("System.Storage.Delete", ctx, key) } @@ -87,6 +89,6 @@ func Delete(ctx Context, key interface{}) { // that match the given key (contain it as a prefix). See Put documentation on // possible key types and iterator package documentation on how to use the // returned value. This function uses `System.Storage.Find` syscall. -func Find(ctx Context, key interface{}, options FindFlags) iterator.Iterator { +func Find(ctx Context, key any, options FindFlags) iterator.Iterator { return neogointernal.Syscall3("System.Storage.Find", ctx, key, options).(iterator.Iterator) } diff --git a/pkg/interop/types.go b/pkg/interop/types.go index c0ffd4ece..a99dcd413 100644 --- a/pkg/interop/types.go +++ b/pkg/interop/types.go @@ -36,14 +36,14 @@ type PublicKey []byte // Interface represents interop interface type which is needed for // transparent handling of VM-internal types (e.g. storage.Context). -type Interface interface{} +type Interface any // Equals compares Hash160 with the provided stackitem using EQUAL opcode. // The provided stackitem `b` must be either one of the primitive type (int, // bool, string, []byte) or derived from the primitive type, otherwise Equals // will fail on .(string) conversion. -func (a Hash160) Equals(b interface{}) bool { - ha := interface{}(a) +func (a Hash160) Equals(b any) bool { + ha := any(a) return bytesEquals(ha, b) } @@ -51,8 +51,8 @@ func (a Hash160) Equals(b interface{}) bool { // The provided stackitem `b` must be either one of the primitive type (int, // bool, string, []byte) or derived from the primitive type, otherwise Equals // will fail on .(string) conversion. -func (a Hash256) Equals(b interface{}) bool { - ha := interface{}(a) +func (a Hash256) Equals(b any) bool { + ha := any(a) return bytesEquals(ha, b) } @@ -60,8 +60,8 @@ func (a Hash256) Equals(b interface{}) bool { // The provided stackitem `b` must be either one of the primitive type (int, // bool, string, []byte) or derived from the primitive type, otherwise Equals // will fail on .(string) conversion. -func (a PublicKey) Equals(b interface{}) bool { - ha := interface{}(a) +func (a PublicKey) Equals(b any) bool { + ha := any(a) return bytesEquals(ha, b) } @@ -69,14 +69,14 @@ func (a PublicKey) Equals(b interface{}) bool { // The provided stackitem `b` must be either one of the primitive types (int, // bool, string, []byte) or derived from the primitive type, otherwise Equals // will fail on .(string) conversion. -func (a Signature) Equals(b interface{}) bool { - ha := interface{}(a) +func (a Signature) Equals(b any) bool { + ha := any(a) return bytesEquals(ha, b) } // bytesEquals is an internal helper function allowed to compare types that can be // converted to ByteString. -func bytesEquals(a interface{}, b interface{}) bool { +func bytesEquals(a any, b any) bool { return (a == nil && b == nil) || (a != nil && b != nil && neogointernal.Opcode2("EQUAL", a.(string), b.(string)).(bool)) } diff --git a/pkg/interop/util/util.go b/pkg/interop/util/util.go index 038b17aa9..9e93ed6c3 100644 --- a/pkg/interop/util/util.go +++ b/pkg/interop/util/util.go @@ -4,7 +4,6 @@ Package util contains some special useful functions that are provided by compile package util import ( - "github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" ) @@ -14,25 +13,15 @@ func Abort() { neogointernal.Opcode0NoReturn("ABORT") } -// FromAddress is an utility function that converts a Neo address to its hash -// (160 bit BE value in a 20 byte slice). It can only be used for strings known -// at compilation time, because the conversion is actually being done by the -// compiler. -// -// Deprecated: use address.ToHash160 instead. -func FromAddress(address string) interop.Hash160 { - return nil -} - // Equals compares a with b and will return true when a and b are equal. It's // implemented as an EQUAL VM opcode, so the rules of comparison are those // of EQUAL. -func Equals(a, b interface{}) bool { +func Equals(a, b any) bool { return neogointernal.Opcode2("EQUAL", a, b).(bool) } // Remove removes element with index i from slice. // This is done in place and slice must have type other than `[]byte`. -func Remove(slice interface{}, i int) { +func Remove(slice any, i int) { neogointernal.Opcode2NoReturn("REMOVE", slice, i) } diff --git a/pkg/io/binaryReader.go b/pkg/io/binaryReader.go index 34abd7976..642eae245 100644 --- a/pkg/io/binaryReader.go +++ b/pkg/io/binaryReader.go @@ -100,7 +100,7 @@ func (r *BinReader) ReadBool() bool { // ReadArray reads an array into a value which must be // a pointer to a slice. -func (r *BinReader) ReadArray(t interface{}, maxSize ...int) { +func (r *BinReader) ReadArray(t any, maxSize ...int) { value := reflect.ValueOf(t) if value.Kind() != reflect.Ptr || value.Elem().Kind() != reflect.Slice { panic(value.Type().String() + " is not a pointer to a slice") diff --git a/pkg/io/binaryWriter.go b/pkg/io/binaryWriter.go index 6188a70d8..9abadb7cf 100644 --- a/pkg/io/binaryWriter.go +++ b/pkg/io/binaryWriter.go @@ -68,7 +68,7 @@ func (w *BinWriter) WriteBool(b bool) { // WriteArray writes a slice or an array arr into w. Note that nil slices and // empty slices are gonna be treated the same resulting in an equal zero-length // array encoded. -func (w *BinWriter) WriteArray(arr interface{}) { +func (w *BinWriter) WriteArray(arr any) { switch val := reflect.ValueOf(arr); val.Kind() { case reflect.Slice, reflect.Array: if w.Err != nil { diff --git a/pkg/io/size.go b/pkg/io/size.go index ba83a403e..25dd51686 100644 --- a/pkg/io/size.go +++ b/pkg/io/size.go @@ -38,7 +38,7 @@ func getVarIntSize(value int) int { // them with variable-length encoding that is used in Neo), strings, pointers to Serializable structures, // slices and arrays of ints/uints or Serializable structures. It's similar to GetVarSize(this T[] value) // used in C#, but differs in that it also supports things like Uint160 or Uint256. -func GetVarSize(value interface{}) int { +func GetVarSize(value any) int { v := reflect.ValueOf(value) switch v.Kind() { case reflect.String: diff --git a/pkg/io/size_test.go b/pkg/io/size_test.go index 26af4c026..66223838b 100644 --- a/pkg/io/size_test.go +++ b/pkg/io/size_test.go @@ -31,7 +31,7 @@ func (*smthNotReallySerializable) EncodeBinary(bw *io.BinWriter) { func TestVarSize(t *testing.T) { testCases := []struct { - variable interface{} + variable any name string expected int }{ @@ -189,7 +189,7 @@ func TestVarSize(t *testing.T) { } } -func panicVarSize(t *testing.T, v interface{}) { +func panicVarSize(t *testing.T, v any) { defer func() { r := recover() assert.NotNil(t, r) diff --git a/pkg/neorpc/result/peers.go b/pkg/neorpc/result/peers.go index 4a4c4f923..33051f71d 100644 --- a/pkg/neorpc/result/peers.go +++ b/pkg/neorpc/result/peers.go @@ -1,7 +1,9 @@ package result import ( - "strings" + "encoding/json" + "net" + "strconv" ) type ( @@ -18,7 +20,7 @@ type ( // Peer represents a peer. Peer struct { Address string `json:"address"` - Port string `json:"port"` + Port uint16 `json:"port"` } ) @@ -49,12 +51,50 @@ func (g *GetPeers) AddBad(addrs []string) { // addPeers adds a set of peers to the given peer slice. func (p *Peers) addPeers(addrs []string) { for i := range addrs { - addressParts := strings.Split(addrs[i], ":") + host, portStr, err := net.SplitHostPort(addrs[i]) + if err != nil { + continue + } + port, err := strconv.ParseUint(portStr, 10, 16) + if err != nil { + port = 0 + } peer := Peer{ - Address: addressParts[0], - Port: addressParts[1], + Address: host, + Port: uint16(port), } *p = append(*p, peer) } } + +func (p *Peer) UnmarshalJSON(data []byte) error { + type NewPeer Peer + var np NewPeer + + err := json.Unmarshal(data, &np) + if err == nil { + *p = Peer(np) + return nil + } + + type OldPeer struct { + Address string `json:"address"` + Port string `json:"port"` + } + var op OldPeer + + err = json.Unmarshal(data, &op) + if err == nil { + port, err := strconv.ParseUint(op.Port, 10, 16) + if err != nil { + return err + } + + *p = Peer{ + Address: op.Address, + Port: uint16(port), + } + } + return err +} diff --git a/pkg/neorpc/result/peers_test.go b/pkg/neorpc/result/peers_test.go index 6e5a9339e..8680ac207 100644 --- a/pkg/neorpc/result/peers_test.go +++ b/pkg/neorpc/result/peers_test.go @@ -1,6 +1,7 @@ package result import ( + "encoding/json" "testing" "github.com/stretchr/testify/require" @@ -13,14 +14,31 @@ func TestGetPeers(t *testing.T) { require.Equal(t, 0, len(gp.Bad)) gp.AddUnconnected([]string{"1.1.1.1:53", "8.8.8.8:53", "9.9.9.9:53"}) - gp.AddConnected([]string{"192.168.0.1:10333"}) - gp.AddBad([]string{"127.0.0.1:20333"}) + unsupportedFormat := "2001:DB0:0:123A:::30" + gp.AddConnected([]string{"192.168.0.1:10333", unsupportedFormat, "[2001:DB0:0:123A::]:30"}) + gp.AddBad([]string{"127.0.0.1:20333", "127.0.0.1:65536"}) require.Equal(t, 3, len(gp.Unconnected)) - require.Equal(t, 1, len(gp.Connected)) - require.Equal(t, 1, len(gp.Bad)) + require.Equal(t, 2, len(gp.Connected)) + require.Equal(t, 2, len(gp.Bad)) require.Equal(t, "192.168.0.1", gp.Connected[0].Address) - require.Equal(t, "10333", gp.Connected[0].Port) + require.Equal(t, uint16(10333), gp.Connected[0].Port) + require.Equal(t, uint16(30), gp.Connected[1].Port) require.Equal(t, "127.0.0.1", gp.Bad[0].Address) - require.Equal(t, "20333", gp.Bad[0].Port) + require.Equal(t, uint16(20333), gp.Bad[0].Port) + require.Equal(t, uint16(0), gp.Bad[1].Port) + + gps := GetPeers{} + oldPeerFormat := `{"unconnected": [{"address": "20.109.188.128","port": "10333"},{"address": "27.188.182.47","port": "10333"}],"connected": [{"address": "54.227.43.72","port": "10333"},{"address": "157.90.177.38","port": "10333"}],"bad": [{"address": "5.226.142.226","port": "10333"}]}` + err := json.Unmarshal([]byte(oldPeerFormat), &gps) + require.NoError(t, err) + newPeerFormat := `{"unconnected": [{"address": "20.109.188.128","port": 10333},{"address": "27.188.182.47","port": 10333}],"connected": [{"address": "54.227.43.72","port": 10333},{"address": "157.90.177.38","port": 10333}],"bad": [{"address": "5.226.142.226","port": 10333},{"address": "54.208.117.178","port": 10333}]}` + err = json.Unmarshal([]byte(newPeerFormat), &gps) + require.NoError(t, err) + badIntFormat := `{"unconnected": [{"address": "20.109.188.128","port": 65536}],"connected": [],"bad": []}` + err = json.Unmarshal([]byte(badIntFormat), &gps) + require.Error(t, err) + badStringFormat := `{"unconnected": [{"address": "20.109.188.128","port": "badport"}],"connected": [],"bad": []}` + err = json.Unmarshal([]byte(badStringFormat), &gps) + require.Error(t, err) } diff --git a/pkg/neorpc/result/validate_address.go b/pkg/neorpc/result/validate_address.go index 12c092afc..468a94cab 100644 --- a/pkg/neorpc/result/validate_address.go +++ b/pkg/neorpc/result/validate_address.go @@ -4,6 +4,6 @@ package result // Address is an interface{} here because the server echoes back whatever address // value a user has sent to it, even if it's not a string. type ValidateAddress struct { - Address interface{} `json:"address"` - IsValid bool `json:"isvalid"` + Address any `json:"address"` + IsValid bool `json:"isvalid"` } diff --git a/pkg/neorpc/rpcevent/filter.go b/pkg/neorpc/rpcevent/filter.go index 049943498..ecba3e199 100644 --- a/pkg/neorpc/rpcevent/filter.go +++ b/pkg/neorpc/rpcevent/filter.go @@ -13,13 +13,13 @@ type ( // filter notifications. Comparator interface { EventID() neorpc.EventID - Filter() interface{} + Filter() any } // Container is an interface required from notification event to be able to // pass filter. Container interface { EventID() neorpc.EventID - EventPayload() interface{} + EventPayload() any } ) diff --git a/pkg/neorpc/rpcevent/filter_test.go b/pkg/neorpc/rpcevent/filter_test.go index 6afd20d13..de56a08fc 100644 --- a/pkg/neorpc/rpcevent/filter_test.go +++ b/pkg/neorpc/rpcevent/filter_test.go @@ -17,24 +17,24 @@ import ( type ( testComparator struct { id neorpc.EventID - filter interface{} + filter any } testContainer struct { id neorpc.EventID - pld interface{} + pld any } ) func (c testComparator) EventID() neorpc.EventID { return c.id } -func (c testComparator) Filter() interface{} { +func (c testComparator) Filter() any { return c.filter } func (c testContainer) EventID() neorpc.EventID { return c.id } -func (c testContainer) EventPayload() interface{} { +func (c testContainer) EventPayload() any { return c.pld } diff --git a/pkg/neorpc/types.go b/pkg/neorpc/types.go index 225cd28fc..8a2a24dd2 100644 --- a/pkg/neorpc/types.go +++ b/pkg/neorpc/types.go @@ -35,7 +35,7 @@ type ( // used by the method implementation on the server side. While JSON-RPC // technically allows it to be an object, all Neo calls expect params // to be an array. - Params []interface{} `json:"params"` + Params []any `json:"params"` // ID is an identifier associated with this request. JSON-RPC itself allows // any strings to be used for it as well, but NeoGo RPC client uses numeric // identifiers. @@ -66,9 +66,9 @@ type ( // special in that they look like requests but they don't have IDs and their // "method" is actually an event name. Notification struct { - JSONRPC string `json:"jsonrpc"` - Event EventID `json:"method"` - Payload []interface{} `json:"params"` + JSONRPC string `json:"jsonrpc"` + Event EventID `json:"method"` + Payload []any `json:"params"` } // SignerWithWitness represents transaction's signer with the corresponding witness. @@ -165,6 +165,6 @@ func (n *Notification) EventID() EventID { // EventPayload implements EventContainer interface and returns notification // object. -func (n *Notification) EventPayload() interface{} { +func (n *Notification) EventPayload() any { return n.Payload[0] } diff --git a/pkg/neotest/basic.go b/pkg/neotest/basic.go index 8c1c5b307..d904f39ab 100644 --- a/pkg/neotest/basic.go +++ b/pkg/neotest/basic.go @@ -80,7 +80,7 @@ func (e *Executor) NativeID(t testing.TB, name string) int32 { } // NewUnsignedTx creates a new unsigned transaction which invokes the method of the contract with the hash. -func (e *Executor) NewUnsignedTx(t testing.TB, hash util.Uint160, method string, args ...interface{}) *transaction.Transaction { +func (e *Executor) NewUnsignedTx(t testing.TB, hash util.Uint160, method string, args ...any) *transaction.Transaction { script, err := smartcontract.CreateCallScript(hash, method, args...) require.NoError(t, err) @@ -93,7 +93,7 @@ func (e *Executor) NewUnsignedTx(t testing.TB, hash util.Uint160, method string, // NewTx creates a new transaction which invokes the contract method. // The transaction is signed by the signers. func (e *Executor) NewTx(t testing.TB, signers []Signer, - hash util.Uint160, method string, args ...interface{}) *transaction.Transaction { + hash util.Uint160, method string, args ...any) *transaction.Transaction { tx := e.NewUnsignedTx(t, hash, method, args...) return e.SignTx(t, tx, -1, signers...) } @@ -137,7 +137,7 @@ func (e *Executor) NewAccount(t testing.TB, expectedGASBalance ...int64) Signer // the precalculated contract hash matches the actual one. // data is an optional argument to `_deploy`. // It returns the hash of the deploy transaction. -func (e *Executor) DeployContract(t testing.TB, c *Contract, data interface{}) util.Uint256 { +func (e *Executor) DeployContract(t testing.TB, c *Contract, data any) util.Uint256 { return e.DeployContractBy(t, e.Validator, c, data) } @@ -145,7 +145,7 @@ func (e *Executor) DeployContract(t testing.TB, c *Contract, data interface{}) u // It also checks that the precalculated contract hash matches the actual one. // data is an optional argument to `_deploy`. // It returns the hash of the deploy transaction. -func (e *Executor) DeployContractBy(t testing.TB, signer Signer, c *Contract, data interface{}) util.Uint256 { +func (e *Executor) DeployContractBy(t testing.TB, signer Signer, c *Contract, data any) util.Uint256 { tx := NewDeployTxBy(t, e.Chain, signer, c, data) e.AddNewBlock(t, tx) e.CheckHalt(t, tx.Hash()) @@ -164,7 +164,7 @@ func (e *Executor) DeployContractBy(t testing.TB, signer Signer, c *Contract, da // DeployContractCheckFAULT compiles and deploys a contract to the bc using the validator // account. It checks that the deploy transaction FAULTed with the specified error. -func (e *Executor) DeployContractCheckFAULT(t testing.TB, c *Contract, data interface{}, errMessage string) { +func (e *Executor) DeployContractCheckFAULT(t testing.TB, c *Contract, data any, errMessage string) { tx := e.NewDeployTx(t, e.Chain, c, data) e.AddNewBlock(t, tx) e.CheckFault(t, tx.Hash(), errMessage) @@ -181,13 +181,18 @@ func (e *Executor) InvokeScript(t testing.TB, script []byte, signers []Signer) u // PrepareInvocation creates a transaction with the specified script and signs it // by the provided signer. func (e *Executor) PrepareInvocation(t testing.TB, script []byte, signers []Signer, validUntilBlock ...uint32) *transaction.Transaction { + tx := e.PrepareInvocationNoSign(t, script, validUntilBlock...) + e.SignTx(t, tx, -1, signers...) + return tx +} + +func (e *Executor) PrepareInvocationNoSign(t testing.TB, script []byte, validUntilBlock ...uint32) *transaction.Transaction { tx := transaction.New(script, 0) tx.Nonce = Nonce() tx.ValidUntilBlock = e.Chain.BlockHeight() + 1 if len(validUntilBlock) != 0 { tx.ValidUntilBlock = validUntilBlock[0] } - e.SignTx(t, tx, -1, signers...) return tx } @@ -253,12 +258,12 @@ func (e *Executor) EnsureGASBalance(t testing.TB, acc util.Uint160, isOk func(ba } // NewDeployTx returns a new deployment tx for the contract signed by the committee. -func (e *Executor) NewDeployTx(t testing.TB, bc *core.Blockchain, c *Contract, data interface{}) *transaction.Transaction { +func (e *Executor) NewDeployTx(t testing.TB, bc *core.Blockchain, c *Contract, data any) *transaction.Transaction { return NewDeployTxBy(t, bc, e.Validator, c, data) } // NewDeployTxBy returns a new deployment tx for the contract signed by the specified signer. -func NewDeployTxBy(t testing.TB, bc *core.Blockchain, signer Signer, c *Contract, data interface{}) *transaction.Transaction { +func NewDeployTxBy(t testing.TB, bc *core.Blockchain, signer Signer, c *Contract, data any) *transaction.Transaction { rawManifest, err := json.Marshal(c.Manifest) require.NoError(t, err) diff --git a/pkg/neotest/client.go b/pkg/neotest/client.go index 25ac8c158..bd96bab47 100644 --- a/pkg/neotest/client.go +++ b/pkg/neotest/client.go @@ -47,8 +47,29 @@ func (e *Executor) ValidatorInvoker(h util.Uint160) *ContractInvoker { } } -// TestInvoke creates test the VM and invokes the method with the args. -func (c *ContractInvoker) TestInvoke(t testing.TB, method string, args ...interface{}) (*vm.Stack, error) { +// TestInvokeScript creates test VM and invokes the script with the args and signers. +func (c *ContractInvoker) TestInvokeScript(t testing.TB, script []byte, signers []Signer, validUntilBlock ...uint32) (*vm.Stack, error) { + tx := c.PrepareInvocationNoSign(t, script, validUntilBlock...) + for _, acc := range signers { + tx.Signers = append(tx.Signers, transaction.Signer{ + Account: acc.ScriptHash(), + Scopes: transaction.Global, + }) + } + b := c.NewUnsignedBlock(t, tx) + ic, err := c.Chain.GetTestVM(trigger.Application, tx, b) + if err != nil { + return nil, err + } + t.Cleanup(ic.Finalize) + + ic.VM.LoadWithFlags(tx.Script, callflag.All) + err = ic.VM.Run() + return ic.VM.Estack(), err +} + +// TestInvoke creates test VM and invokes the method with the args. +func (c *ContractInvoker) TestInvoke(t testing.TB, method string, args ...any) (*vm.Stack, error) { tx := c.PrepareInvokeNoSign(t, method, args...) b := c.NewUnsignedBlock(t, tx) ic, err := c.Chain.GetTestVM(trigger.Application, tx, b) @@ -70,18 +91,18 @@ func (c *ContractInvoker) WithSigners(signers ...Signer) *ContractInvoker { } // PrepareInvoke creates a new invocation transaction. -func (c *ContractInvoker) PrepareInvoke(t testing.TB, method string, args ...interface{}) *transaction.Transaction { +func (c *ContractInvoker) PrepareInvoke(t testing.TB, method string, args ...any) *transaction.Transaction { return c.Executor.NewTx(t, c.Signers, c.Hash, method, args...) } // PrepareInvokeNoSign creates a new unsigned invocation transaction. -func (c *ContractInvoker) PrepareInvokeNoSign(t testing.TB, method string, args ...interface{}) *transaction.Transaction { +func (c *ContractInvoker) PrepareInvokeNoSign(t testing.TB, method string, args ...any) *transaction.Transaction { return c.Executor.NewUnsignedTx(t, c.Hash, method, args...) } // Invoke invokes the method with the args, persists the transaction and checks the result. // Returns transaction hash. -func (c *ContractInvoker) Invoke(t testing.TB, result interface{}, method string, args ...interface{}) util.Uint256 { +func (c *ContractInvoker) Invoke(t testing.TB, result any, method string, args ...any) util.Uint256 { tx := c.PrepareInvoke(t, method, args...) c.AddNewBlock(t, tx) c.CheckHalt(t, tx.Hash(), stackitem.Make(result)) @@ -90,7 +111,7 @@ func (c *ContractInvoker) Invoke(t testing.TB, result interface{}, method string // InvokeAndCheck invokes the method with the args, persists the transaction and checks the result // using the provided function. It returns the transaction hash. -func (c *ContractInvoker) InvokeAndCheck(t testing.TB, checkResult func(t testing.TB, stack []stackitem.Item), method string, args ...interface{}) util.Uint256 { +func (c *ContractInvoker) InvokeAndCheck(t testing.TB, checkResult func(t testing.TB, stack []stackitem.Item), method string, args ...any) util.Uint256 { tx := c.PrepareInvoke(t, method, args...) c.AddNewBlock(t, tx) aer, err := c.Chain.GetAppExecResults(tx.Hash(), trigger.Application) @@ -103,7 +124,7 @@ func (c *ContractInvoker) InvokeAndCheck(t testing.TB, checkResult func(t testin } // InvokeWithFeeFail is like InvokeFail but sets the custom system fee for the transaction. -func (c *ContractInvoker) InvokeWithFeeFail(t testing.TB, message string, sysFee int64, method string, args ...interface{}) util.Uint256 { +func (c *ContractInvoker) InvokeWithFeeFail(t testing.TB, message string, sysFee int64, method string, args ...any) util.Uint256 { tx := c.PrepareInvokeNoSign(t, method, args...) c.Executor.SignTx(t, tx, sysFee, c.Signers...) c.AddNewBlock(t, tx) @@ -113,7 +134,7 @@ func (c *ContractInvoker) InvokeWithFeeFail(t testing.TB, message string, sysFee // InvokeFail invokes the method with the args, persists the transaction and checks the error message. // It returns the transaction hash. -func (c *ContractInvoker) InvokeFail(t testing.TB, message string, method string, args ...interface{}) util.Uint256 { +func (c *ContractInvoker) InvokeFail(t testing.TB, message string, method string, args ...any) util.Uint256 { tx := c.PrepareInvoke(t, method, args...) c.AddNewBlock(t, tx) c.CheckFault(t, tx.Hash(), message) diff --git a/pkg/neotest/compile.go b/pkg/neotest/compile.go index fee77c17e..70645c11c 100644 --- a/pkg/neotest/compile.go +++ b/pkg/neotest/compile.go @@ -60,6 +60,7 @@ func CompileFile(t testing.TB, sender util.Uint160, srcPath string, configPath s o := &compiler.Options{} o.Name = conf.Name 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 { diff --git a/pkg/network/bqueue/queue.go b/pkg/network/bqueue/queue.go index 1cf8ec548..c80c6e04a 100644 --- a/pkg/network/bqueue/queue.go +++ b/pkg/network/bqueue/queue.go @@ -153,7 +153,7 @@ func (bq *Queue) LastQueued() (uint32, int) { // Discard stops the queue and prevents it from accepting more blocks to enqueue. func (bq *Queue) Discard() { - if bq.discarded.CAS(false, true) { + if bq.discarded.CompareAndSwap(false, true) { bq.queueLock.Lock() close(bq.checkBlocks) // Technically we could bq.queue = nil, but this would cost diff --git a/pkg/network/extpool/pool_test.go b/pkg/network/extpool/pool_test.go index 49486caed..89802a257 100644 --- a/pkg/network/extpool/pool_test.go +++ b/pkg/network/extpool/pool_test.go @@ -107,7 +107,7 @@ func TestRemoveStale(t *testing.T) { func (p *Pool) testAdd(t *testing.T, expectedOk bool, expectedErr error, ep *payload.Extensible) { ok, err := p.Add(ep) if expectedErr != nil { - require.True(t, errors.Is(err, expectedErr), "got: %v", err) + require.ErrorIs(t, err, expectedErr) } else { require.NoError(t, err) } diff --git a/pkg/network/payload/address_test.go b/pkg/network/payload/address_test.go index 843aeecc0..55adb9882 100644 --- a/pkg/network/payload/address_test.go +++ b/pkg/network/payload/address_test.go @@ -25,9 +25,15 @@ func TestEncodeDecodeAddress(t *testing.T) { ) assert.Equal(t, ts.UTC().Unix(), int64(addr.Timestamp)) + + // On Windows or macOS localhost can be resolved to 4-bytes IPv4. + expected := make(net.IP, 16) + copy(expected, e.IP[:]) + aatip := make(net.IP, 16) copy(aatip, addr.IP[:]) - assert.Equal(t, e.IP, aatip) + + assert.Equal(t, expected, aatip) assert.Equal(t, 1, len(addr.Capabilities)) assert.Equal(t, capability.Capability{ Type: capability.TCPServer, diff --git a/pkg/network/payload/extensible_test.go b/pkg/network/payload/extensible_test.go index f1f6b28c8..a849d8f2c 100644 --- a/pkg/network/payload/extensible_test.go +++ b/pkg/network/payload/extensible_test.go @@ -1,7 +1,6 @@ package payload import ( - "errors" gio "io" "testing" @@ -34,11 +33,11 @@ func TestExtensible_Serializable(t *testing.T) { t.Run("unexpected EOF", func(t *testing.T) { err := testserdes.DecodeBinary(unsigned, new(Extensible)) - require.True(t, errors.Is(err, gio.EOF)) + require.ErrorIs(t, err, gio.EOF) }) t.Run("invalid padding", func(t *testing.T) { err := testserdes.DecodeBinary(append(unsigned, 42), new(Extensible)) - require.True(t, errors.Is(err, errInvalidPadding)) + require.ErrorIs(t, err, errInvalidPadding) }) t.Run("too large data size", func(t *testing.T) { expected.Data = make([]byte, MaxSize+1) diff --git a/pkg/network/payload/merkleblock_test.go b/pkg/network/payload/merkleblock_test.go index 5bcd1e47c..811574705 100644 --- a/pkg/network/payload/merkleblock_test.go +++ b/pkg/network/payload/merkleblock_test.go @@ -1,7 +1,6 @@ package payload import ( - "errors" "testing" "github.com/stretchr/testify/require" @@ -52,7 +51,7 @@ func TestMerkleBlock_EncodeDecodeBinary(t *testing.T) { } data, err := testserdes.EncodeBinary(expected) require.NoError(t, err) - require.True(t, errors.Is(block.ErrMaxContentsPerBlock, testserdes.DecodeBinary(data, new(MerkleBlock)))) + require.ErrorIs(t, testserdes.DecodeBinary(data, new(MerkleBlock)), block.ErrMaxContentsPerBlock) }) t.Run("bad flags size", func(t *testing.T) { diff --git a/pkg/network/prometheus.go b/pkg/network/prometheus.go index bc1156afe..b0ed5982b 100644 --- a/pkg/network/prometheus.go +++ b/pkg/network/prometheus.go @@ -25,6 +25,7 @@ var ( }, ) + // Deprecated: please, use neogoVersion and serverID instead. servAndNodeVersion = prometheus.NewGaugeVec( prometheus.GaugeOpts{ Help: "Server and Node versions", @@ -34,6 +35,22 @@ var ( []string{"description", "value"}, ) + neogoVersion = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Help: "NeoGo version", + Name: "version", + Namespace: "neogo", + }, + []string{"version"}) + + serverID = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Help: "network server ID", + Name: "server_id", + Namespace: "neogo", + }, + []string{"server_id"}) + poolCount = prometheus.NewGauge( prometheus.GaugeOpts{ Help: "Number of available node addresses", @@ -50,6 +67,15 @@ var ( }, ) p2pCmds = make(map[CommandType]prometheus.Histogram) + + // notarypoolUnsortedTx prometheus metric. + notarypoolUnsortedTx = prometheus.NewGauge( + prometheus.GaugeOpts{ + Help: "Notary request pool fallback txs", + Name: "notarypool_unsorted_tx", + Namespace: "neogo", + }, + ) ) func init() { @@ -57,8 +83,11 @@ func init() { estimatedNetworkSize, peersConnected, servAndNodeVersion, + neogoVersion, + serverID, poolCount, blockQueueLength, + notarypoolUnsortedTx, ) for _, cmd := range []CommandType{CMDVersion, CMDVerack, CMDGetAddr, CMDAddr, CMDPing, CMDPong, CMDGetHeaders, CMDHeaders, CMDGetBlocks, @@ -92,10 +121,21 @@ func updatePoolCountMetric(pCount int) { func updatePeersConnectedMetric(pConnected int) { peersConnected.Set(float64(pConnected)) } + +// Deprecated: please, use setNeoGoVersion and setSeverID instead. func setServerAndNodeVersions(nodeVer string, serverID string) { servAndNodeVersion.WithLabelValues("Node version: ", nodeVer).Add(0) servAndNodeVersion.WithLabelValues("Server id: ", serverID).Add(0) } + +func setNeoGoVersion(nodeVer string) { + neogoVersion.WithLabelValues(nodeVer).Add(1) +} + +func setSeverID(id string) { + serverID.WithLabelValues(id).Add(1) +} + func addCmdTimeMetric(cmd CommandType, t time.Duration) { // Shouldn't happen, message decoder checks the type, but better safe than sorry. if p2pCmds[cmd] == nil { @@ -103,3 +143,9 @@ func addCmdTimeMetric(cmd CommandType, t time.Duration) { } p2pCmds[cmd].Observe(t.Seconds()) } + +// updateNotarypoolMetrics updates metric of the number of fallback txs inside +// the notary request pool. +func updateNotarypoolMetrics(unsortedTxnLen int) { + notarypoolUnsortedTx.Set(float64(unsortedTxnLen)) +} diff --git a/pkg/network/server.go b/pkg/network/server.go index 90de8d3a6..8f97665cd 100644 --- a/pkg/network/server.go +++ b/pkg/network/server.go @@ -73,7 +73,7 @@ type ( HeaderHeight() uint32 P2PSigExtensionsEnabled() bool PoolTx(t *transaction.Transaction, pools ...*mempool.Pool) error - PoolTxWithData(t *transaction.Transaction, data interface{}, mp *mempool.Pool, feer mempool.Feer, verificationFunction func(t *transaction.Transaction, data interface{}) error) error + PoolTxWithData(t *transaction.Transaction, data any, mp *mempool.Pool, feer mempool.Feer, verificationFunction func(t *transaction.Transaction, data any) error) error RegisterPostBlock(f func(func(*transaction.Transaction, *mempool.Pool, bool) bool, *mempool.Pool, *block.Block)) SubscribeForBlocks(ch chan *block.Block) UnsubscribeFromBlocks(ch chan *block.Block) @@ -198,7 +198,7 @@ func newServerFromConstructors(config ServerConfig, chain Ledger, stSync StateSy } if chain.P2PSigExtensionsEnabled() { s.notaryFeer = NewNotaryFeer(chain) - s.notaryRequestPool = mempool.New(s.config.P2PNotaryRequestPayloadPoolSize, 1, true) + s.notaryRequestPool = mempool.New(s.config.P2PNotaryRequestPayloadPoolSize, 1, true, updateNotarypoolMetrics) chain.RegisterPostBlock(func(isRelevant func(*transaction.Transaction, *mempool.Pool, bool) bool, txpool *mempool.Pool, _ *block.Block) { s.notaryRequestPool.RemoveStale(func(t *transaction.Transaction) bool { return isRelevant(t, txpool, true) @@ -285,6 +285,8 @@ func (s *Server) Start() { go tr.Accept() } setServerAndNodeVersions(s.UserAgent, strconv.FormatUint(uint64(s.id), 10)) + setNeoGoVersion(config.Version) + setSeverID(strconv.FormatUint(uint64(s.id), 10)) s.run() } @@ -541,7 +543,7 @@ func (s *Server) tryStartServices() { return } - if s.IsInSync() && s.syncReached.CAS(false, true) { + if s.IsInSync() && s.syncReached.CompareAndSwap(false, true) { s.log.Info("node reached synchronized state, starting services") if s.chain.P2PSigExtensionsEnabled() { s.notaryRequestPool.RunSubscriptions() // WSClient is also a subscriber. @@ -1189,7 +1191,7 @@ func (s *Server) verifyAndPoolNotaryRequest(r *payload.P2PNotaryRequest) error { } // verifyNotaryRequest is a function for state-dependant P2PNotaryRequest payload verification which is executed before ordinary blockchain's verification. -func (s *Server) verifyNotaryRequest(_ *transaction.Transaction, data interface{}) error { +func (s *Server) verifyNotaryRequest(_ *transaction.Transaction, data any) error { r := data.(*payload.P2PNotaryRequest) payer := r.FallbackTransaction.Signers[1].Account if _, err := s.chain.VerifyWitness(payer, r, &r.Witness, s.chain.GetMaxVerificationGAS()); err != nil { @@ -1209,7 +1211,7 @@ func (s *Server) verifyNotaryRequest(_ *transaction.Transaction, data interface{ return nil } -func (s *Server) broadcastP2PNotaryRequestPayload(_ *transaction.Transaction, data interface{}) { +func (s *Server) broadcastP2PNotaryRequestPayload(_ *transaction.Transaction, data any) { r := data.(*payload.P2PNotaryRequest) // we can guarantee that cast is successful msg := NewMessage(CMDInv, payload.NewInventory(payload.P2PNotaryRequestType, []util.Uint256{r.FallbackTransaction.Hash()})) s.broadcastMessage(msg) @@ -1280,14 +1282,14 @@ func getRequestBlocksPayload(p Peer, currHeight uint32, lastRequestedHeight *ato old := lastRequestedHeight.Load() if old <= currHeight { needHeight = currHeight + 1 - if !lastRequestedHeight.CAS(old, needHeight) { + if !lastRequestedHeight.CompareAndSwap(old, needHeight) { continue } } else if old < currHeight+(bqueue.CacheSize-payload.MaxHashesCount) { needHeight = currHeight + 1 if peerHeight > old+payload.MaxHashesCount { needHeight = old + payload.MaxHashesCount - if !lastRequestedHeight.CAS(old, needHeight) { + if !lastRequestedHeight.CompareAndSwap(old, needHeight) { continue } } @@ -1595,7 +1597,7 @@ func (s *Server) RelayTxn(t *transaction.Transaction) error { } // broadcastTX broadcasts an inventory message about new transaction. -func (s *Server) broadcastTX(t *transaction.Transaction, _ interface{}) { +func (s *Server) broadcastTX(t *transaction.Transaction, _ any) { select { case s.transactions <- t: case <-s.quit: diff --git a/pkg/network/server_test.go b/pkg/network/server_test.go index 4d1967988..1f862d105 100644 --- a/pkg/network/server_test.go +++ b/pkg/network/server_test.go @@ -104,7 +104,7 @@ func TestServerStartAndShutdown(t *testing.T) { require.True(t, s.transports[0].(*fakeTransp).closed.Load()) err, ok := p.droppedWith.Load().(error) require.True(t, ok) - require.True(t, errors.Is(err, errServerShutdown)) + require.ErrorIs(t, err, errServerShutdown) }) t.Run("with consensus", func(t *testing.T) { s := newTestServer(t, ServerConfig{}) @@ -161,7 +161,7 @@ func TestServerRegisterPeer(t *testing.T) { require.True(t, index >= 0) err, ok := ps[index].droppedWith.Load().(error) require.True(t, ok) - require.True(t, errors.Is(err, errMaxPeers)) + require.ErrorIs(t, err, errMaxPeers) index = (index + 1) % peerCount s.unregister <- peerDrop{ps[index], errIdenticalID} diff --git a/pkg/network/tcp_peer_test.go b/pkg/network/tcp_peer_test.go index 7e03f84bb..993459177 100644 --- a/pkg/network/tcp_peer_test.go +++ b/pkg/network/tcp_peer_test.go @@ -11,7 +11,8 @@ import ( func connReadStub(conn net.Conn) { b := make([]byte, 1024) var err error - for ; err == nil; _, err = conn.Read(b) { + for err == nil { + _, err = conn.Read(b) } } diff --git a/pkg/rpcclient/actor/actor.go b/pkg/rpcclient/actor/actor.go index f8926a958..35ae9346a 100644 --- a/pkg/rpcclient/actor/actor.go +++ b/pkg/rpcclient/actor/actor.go @@ -245,7 +245,7 @@ func (a *Actor) sendWrapper(tx *transaction.Transaction, err error) (util.Uint25 // SendCall creates a transaction that calls the given method of the given // contract with the given parameters (see also MakeCall) and sends it to the // network. -func (a *Actor) SendCall(contract util.Uint160, method string, params ...interface{}) (util.Uint256, uint32, error) { +func (a *Actor) SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) { return a.sendWrapper(a.MakeCall(contract, method, params...)) } @@ -253,7 +253,7 @@ func (a *Actor) SendCall(contract util.Uint160, method string, params ...interfa // contract with the given parameters (see also MakeTunedCall) and attributes, // allowing to check for execution results of this call and modify transaction // before it's signed; this transaction is then sent to the network. -func (a *Actor) SendTunedCall(contract util.Uint160, method string, attrs []transaction.Attribute, txHook TransactionCheckerModifier, params ...interface{}) (util.Uint256, uint32, error) { +func (a *Actor) SendTunedCall(contract util.Uint160, method string, attrs []transaction.Attribute, txHook TransactionCheckerModifier, params ...any) (util.Uint256, uint32, error) { return a.sendWrapper(a.MakeTunedCall(contract, method, attrs, txHook, params...)) } diff --git a/pkg/rpcclient/actor/doc_test.go b/pkg/rpcclient/actor/doc_test.go index a1297b2bc..71035603f 100644 --- a/pkg/rpcclient/actor/doc_test.go +++ b/pkg/rpcclient/actor/doc_test.go @@ -33,7 +33,7 @@ func ExampleActor() { // have a signer with the first wallet account and CalledByEntry scope. res, _ := a.Call(customContract, "method", 1, 2, 3) if res.State != vmstate.Halt.String() { - // The call failed. + panic("failed") } // All of the side-effects in res can be analyzed. diff --git a/pkg/rpcclient/actor/maker.go b/pkg/rpcclient/actor/maker.go index f58547cb0..0aa346ab7 100644 --- a/pkg/rpcclient/actor/maker.go +++ b/pkg/rpcclient/actor/maker.go @@ -52,7 +52,7 @@ func DefaultCheckerModifier(r *result.Invoke, t *transaction.Transaction) error // Actor-configured TransactionCheckerModifier. The resulting transaction has // Actor-configured attributes added as well. If you need to override attributes // and/or TransactionCheckerModifier use MakeTunedCall. -func (a *Actor) MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) { +func (a *Actor) MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) { return a.MakeTunedCall(contract, method, nil, nil, params...) } @@ -61,7 +61,7 @@ func (a *Actor) MakeCall(contract util.Uint160, method string, params ...interfa // parameters. It's filtered through the provided callback (or Actor default // one's if nil, see TransactionCheckerModifier documentation also), so the // process can be aborted and transaction can be modified before signing. -func (a *Actor) MakeTunedCall(contract util.Uint160, method string, attrs []transaction.Attribute, txHook TransactionCheckerModifier, params ...interface{}) (*transaction.Transaction, error) { +func (a *Actor) MakeTunedCall(contract util.Uint160, method string, attrs []transaction.Attribute, txHook TransactionCheckerModifier, params ...any) (*transaction.Transaction, error) { r, err := a.Call(contract, method, params...) return a.makeUncheckedWrapper(r, err, attrs, txHook) } @@ -130,7 +130,7 @@ func (a *Actor) MakeUncheckedRun(script []byte, sysfee int64, attrs []transactio // transaction returned has correct SystemFee and NetworkFee values. // TransactionModifier is not applied to the result of this method, but default // attributes are used if attrs is nil. -func (a *Actor) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error) { +func (a *Actor) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) { r, err := a.Call(contract, method, params...) return a.makeUnsignedWrapper(r, err, attrs) } diff --git a/pkg/rpcclient/actor/waiter.go b/pkg/rpcclient/actor/waiter.go index 8de06a6a8..d37b4383b 100644 --- a/pkg/rpcclient/actor/waiter.go +++ b/pkg/rpcclient/actor/waiter.go @@ -331,10 +331,10 @@ func (w *EventWaiter) WaitAny(ctx context.Context, vub uint32, hashes ...util.Ui case unsubErr := <-unsubErrs: if unsubErr != nil { errFmt := "unsubscription error: %v" - errArgs := []interface{}{unsubErr} + errArgs := []any{unsubErr} if waitErr != nil { errFmt = "%w; " + errFmt - errArgs = append([]interface{}{waitErr}, errArgs...) + errArgs = append([]any{waitErr}, errArgs...) } waitErr = fmt.Errorf(errFmt, errArgs...) } diff --git a/pkg/rpcclient/client.go b/pkg/rpcclient/client.go index 67cf3f10d..9c67ad43a 100644 --- a/pkg/rpcclient/client.go +++ b/pkg/rpcclient/client.go @@ -181,9 +181,9 @@ func (c *Client) Close() { c.cli.CloseIdleConnections() } -func (c *Client) performRequest(method string, p []interface{}, v interface{}) error { +func (c *Client) performRequest(method string, p []any, v any) error { if p == nil { - p = []interface{}{} // neo-project/neo-modules#742 + p = []any{} // neo-project/neo-modules#742 } var r = neorpc.Request{ JSONRPC: neorpc.JSONRPCVersion, diff --git a/pkg/rpcclient/gas/gas_test.go b/pkg/rpcclient/gas/gas_test.go index eb49affda..3ca49e5c5 100644 --- a/pkg/rpcclient/gas/gas_test.go +++ b/pkg/rpcclient/gas/gas_test.go @@ -17,7 +17,7 @@ type testAct struct { vub uint32 } -func (t *testAct) Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) { +func (t *testAct) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) { return t.res, t.err } func (t *testAct) MakeRun(script []byte) (*transaction.Transaction, error) { diff --git a/pkg/rpcclient/helper.go b/pkg/rpcclient/helper.go index 587816c93..af394cde9 100644 --- a/pkg/rpcclient/helper.go +++ b/pkg/rpcclient/helper.go @@ -36,7 +36,7 @@ func (c *Client) InvokeAndPackIteratorResults(contract util.Uint160, operation s if err != nil { return nil, fmt.Errorf("expanding params to emitable: %w", err) } - bytes, err := smartcontract.CreateCallAndUnwrapIteratorScript(contract, operation, max, values.([]interface{})...) + bytes, err := smartcontract.CreateCallAndUnwrapIteratorScript(contract, operation, max, values.([]any)...) if err != nil { return nil, fmt.Errorf("failed to create iterator unwrapper script: %w", err) } diff --git a/pkg/rpcclient/invoker/doc_test.go b/pkg/rpcclient/invoker/doc_test.go index fe01b9490..882f35a65 100644 --- a/pkg/rpcclient/invoker/doc_test.go +++ b/pkg/rpcclient/invoker/doc_test.go @@ -33,9 +33,9 @@ func ExampleInvoker() { // Test-invoke transfer call. res, _ := inv.Call(neo.Hash, "transfer", acc, util.Uint160{1, 2, 3}, 1, nil) if res.State == vmstate.Halt.String() { - // NEO is broken! inv has no signers and transfer requires a witness to be performed. + panic("NEO is broken!") // inv has no signers and transfer requires a witness to be performed. } else { - // OK, this actually should fail. + println("ok") // this actually should fail } // A historic invoker with no signers at block 1000000. @@ -57,9 +57,11 @@ func ExampleInvoker() { // transfer actually returns a value, so check it too. ok, _ := unwrap.Bool(res, nil) if ok { - // OK, as expected. res.Script contains the corresponding - // script and res.GasConsumed has an appropriate system fee - // required for a transaction. + // OK, as expected. + // res.Script contains the corresponding script. + _ = res.Script + // res.GasConsumed has an appropriate system fee required for a transaction. + _ = res.GasConsumed } } diff --git a/pkg/rpcclient/invoker/invoker.go b/pkg/rpcclient/invoker/invoker.go index dfab2c803..a56be5942 100644 --- a/pkg/rpcclient/invoker/invoker.go +++ b/pkg/rpcclient/invoker/invoker.go @@ -139,7 +139,7 @@ func (h *historicConverter) TraverseIterator(sessionID, iteratorID uuid.UUID, ma // Call invokes a method of the contract with the given parameters (and // Invoker-specific list of signers) and returns the result as is. -func (v *Invoker) Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) { +func (v *Invoker) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) { ps, err := smartcontract.NewParametersFromValues(params...) if err != nil { return nil, err @@ -155,7 +155,7 @@ func (v *Invoker) Call(contract util.Uint160, operation string, params ...interf // result of the whole script is an array containing up to maxResultItems elements // from the iterator returned from the contract's method call. This script is executed // using regular JSON-API (according to the way Iterator is set up). -func (v *Invoker) CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...interface{}) (*result.Invoke, error) { +func (v *Invoker) CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...any) (*result.Invoke, error) { bytes, err := smartcontract.CreateCallAndUnwrapIteratorScript(contract, method, maxItems, params...) if err != nil { return nil, fmt.Errorf("iterator unwrapper script: %w", err) @@ -165,7 +165,7 @@ func (v *Invoker) CallAndExpandIterator(contract util.Uint160, method string, ma // Verify invokes contract's verify method in the verification context with // Invoker-specific signers and given witnesses and parameters. -func (v *Invoker) Verify(contract util.Uint160, witnesses []transaction.Witness, params ...interface{}) (*result.Invoke, error) { +func (v *Invoker) Verify(contract util.Uint160, witnesses []transaction.Witness, params ...any) (*result.Invoke, error) { ps, err := smartcontract.NewParametersFromValues(params...) if err != nil { return nil, err diff --git a/pkg/rpcclient/local.go b/pkg/rpcclient/local.go index 19e67ceaa..345566d71 100644 --- a/pkg/rpcclient/local.go +++ b/pkg/rpcclient/local.go @@ -34,7 +34,7 @@ func NewInternal(ctx context.Context, register InternalHook) (*Internal, error) done: make(chan struct{}), closeCalled: *atomic.NewBool(false), subscriptions: make(map[string]notificationReceiver), - receivers: make(map[interface{}][]string), + receivers: make(map[any][]string), }, events: make(chan neorpc.Notification), } diff --git a/pkg/rpcclient/management/management.go b/pkg/rpcclient/management/management.go index 8f13776c2..4862cd8b6 100644 --- a/pkg/rpcclient/management/management.go +++ b/pkg/rpcclient/management/management.go @@ -27,8 +27,8 @@ import ( // Invoker is used by ContractReader to call various methods. type Invoker interface { - Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) - CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...interface{}) (*result.Invoke, error) + 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) } @@ -37,11 +37,11 @@ type Invoker interface { type Actor interface { Invoker - MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) + 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 ...interface{}) (*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 ...interface{}) (util.Uint256, uint32, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) SendRun(script []byte) (util.Uint256, uint32, error) } @@ -229,7 +229,7 @@ func (c *ContractReader) HasMethod(hash util.Uint160, method string, pcount int) // to the invocation and will be used for "_deploy" method invocation done by // the ContractManagement contract. If successful, this method returns deployed // contract state that can be retrieved from the stack after execution. -func (c *Contract) Deploy(exe *nef.File, manif *manifest.Manifest, data interface{}) (util.Uint256, uint32, error) { +func (c *Contract) Deploy(exe *nef.File, manif *manifest.Manifest, data any) (util.Uint256, uint32, error) { script, err := mkDeployScript(exe, manif, data) if err != nil { return util.Uint256{}, 0, err @@ -242,7 +242,7 @@ func (c *Contract) Deploy(exe *nef.File, manif *manifest.Manifest, data interfac // to the invocation and will be used for "_deploy" method invocation done by // the ContractManagement contract. If successful, this method returns deployed // contract state that can be retrieved from the stack after execution. -func (c *Contract) DeployTransaction(exe *nef.File, manif *manifest.Manifest, data interface{}) (*transaction.Transaction, error) { +func (c *Contract) DeployTransaction(exe *nef.File, manif *manifest.Manifest, data any) (*transaction.Transaction, error) { script, err := mkDeployScript(exe, manif, data) if err != nil { return nil, err @@ -255,7 +255,7 @@ func (c *Contract) DeployTransaction(exe *nef.File, manif *manifest.Manifest, da // to the invocation and will be used for "_deploy" method invocation done by // the ContractManagement contract. If successful, this method returns deployed // contract state that can be retrieved from the stack after execution. -func (c *Contract) DeployUnsigned(exe *nef.File, manif *manifest.Manifest, data interface{}) (*transaction.Transaction, error) { +func (c *Contract) DeployUnsigned(exe *nef.File, manif *manifest.Manifest, data any) (*transaction.Transaction, error) { script, err := mkDeployScript(exe, manif, data) if err != nil { return nil, err @@ -263,7 +263,7 @@ func (c *Contract) DeployUnsigned(exe *nef.File, manif *manifest.Manifest, data return c.actor.MakeUnsignedRun(script, nil) } -func mkDeployScript(exe *nef.File, manif *manifest.Manifest, data interface{}) ([]byte, error) { +func mkDeployScript(exe *nef.File, manif *manifest.Manifest, data any) ([]byte, error) { exeB, err := exe.Bytes() if err != nil { return nil, fmt.Errorf("bad NEF: %w", err) diff --git a/pkg/rpcclient/management/management_test.go b/pkg/rpcclient/management/management_test.go index 5ed8b89bc..51f6d3390 100644 --- a/pkg/rpcclient/management/management_test.go +++ b/pkg/rpcclient/management/management_test.go @@ -23,16 +23,16 @@ type testAct struct { vub uint32 } -func (t *testAct) Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) { +func (t *testAct) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) { return t.res, t.err } -func (t *testAct) MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) { +func (t *testAct) MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) { return t.tx, t.err } -func (t *testAct) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error) { +func (t *testAct) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) { return t.tx, t.err } -func (t *testAct) SendCall(contract util.Uint160, method string, params ...interface{}) (util.Uint256, uint32, error) { +func (t *testAct) SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) { return t.txh, t.vub, t.err } func (t *testAct) MakeRun(script []byte) (*transaction.Transaction, error) { @@ -44,7 +44,7 @@ func (t *testAct) MakeUnsignedRun(script []byte, attrs []transaction.Attribute) func (t *testAct) SendRun(script []byte) (util.Uint256, uint32, error) { return t.txh, t.vub, t.err } -func (t *testAct) CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...interface{}) (*result.Invoke, error) { +func (t *testAct) CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...any) (*result.Invoke, error) { return t.res, t.err } func (t *testAct) TerminateSession(sessionID uuid.UUID) error { @@ -294,7 +294,7 @@ func TestDeploy(t *testing.T) { _, _, err := man.Deploy(nefFile, manif, nil) require.Error(t, err) - for _, m := range []func(exe *nef.File, manif *manifest.Manifest, data interface{}) (*transaction.Transaction, error){ + for _, m := range []func(exe *nef.File, manif *manifest.Manifest, data any) (*transaction.Transaction, error){ man.DeployTransaction, man.DeployUnsigned, } { @@ -312,7 +312,7 @@ func TestDeploy(t *testing.T) { require.Equal(t, ta.vub, vub) ta.tx = transaction.New([]byte{1, 2, 3}, 100500) - for _, m := range []func(exe *nef.File, manif *manifest.Manifest, data interface{}) (*transaction.Transaction, error){ + for _, m := range []func(exe *nef.File, manif *manifest.Manifest, data any) (*transaction.Transaction, error){ man.DeployTransaction, man.DeployUnsigned, } { diff --git a/pkg/rpcclient/neo/neo.go b/pkg/rpcclient/neo/neo.go index b9b17ed33..3da8b47a0 100644 --- a/pkg/rpcclient/neo/neo.go +++ b/pkg/rpcclient/neo/neo.go @@ -33,7 +33,7 @@ const ( type Invoker interface { nep17.Invoker - CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...interface{}) (*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) } @@ -44,10 +44,10 @@ type Actor interface { Invoker Run(script []byte) (*result.Invoke, error) - MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) - MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error) + MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) + MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) MakeUnsignedUncheckedRun(script []byte, sysFee int64, attrs []transaction.Attribute) (*transaction.Transaction, error) - SendCall(contract util.Uint160, method string, params ...interface{}) (util.Uint256, uint32, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) Sign(tx *transaction.Transaction) error SignAndSend(tx *transaction.Transaction) (util.Uint256, uint32, error) } @@ -398,7 +398,7 @@ func (c *Contract) VoteUnsigned(account util.Uint160, voteTo *keys.PublicKey) (* } func voteScript(account util.Uint160, voteTo *keys.PublicKey) []byte { - var param interface{} + var param any if voteTo != nil { param = voteTo.Bytes() diff --git a/pkg/rpcclient/neo/neo_test.go b/pkg/rpcclient/neo/neo_test.go index 5cf7e8235..d99dc86f9 100644 --- a/pkg/rpcclient/neo/neo_test.go +++ b/pkg/rpcclient/neo/neo_test.go @@ -27,7 +27,7 @@ type testAct struct { inv *result.Invoke } -func (t *testAct) Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) { +func (t *testAct) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) { return t.res, t.err } func (t *testAct) MakeRun(script []byte) (*transaction.Transaction, error) { @@ -39,13 +39,13 @@ func (t *testAct) MakeUnsignedRun(script []byte, attrs []transaction.Attribute) func (t *testAct) SendRun(script []byte) (util.Uint256, uint32, error) { return t.txh, t.vub, t.err } -func (t *testAct) MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) { +func (t *testAct) MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) { return t.tx, t.err } -func (t *testAct) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error) { +func (t *testAct) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) { return t.tx, t.err } -func (t *testAct) SendCall(contract util.Uint160, method string, params ...interface{}) (util.Uint256, uint32, error) { +func (t *testAct) SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) { return t.txh, t.vub, t.err } func (t *testAct) Run(script []byte) (*result.Invoke, error) { @@ -60,7 +60,7 @@ func (t *testAct) Sign(tx *transaction.Transaction) error { func (t *testAct) SignAndSend(tx *transaction.Transaction) (util.Uint256, uint32, error) { return t.txh, t.vub, t.err } -func (t *testAct) CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...interface{}) (*result.Invoke, error) { +func (t *testAct) CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...any) (*result.Invoke, error) { return t.inv, t.err } func (t *testAct) TerminateSession(sessionID uuid.UUID) error { diff --git a/pkg/rpcclient/nep.go b/pkg/rpcclient/nep.go index 4b2497315..3fa41a913 100644 --- a/pkg/rpcclient/nep.go +++ b/pkg/rpcclient/nep.go @@ -25,7 +25,7 @@ func (c *Client) nepTotalSupply(tokenHash util.Uint160) (int64, error) { // nepBalanceOf invokes `balanceOf` NEP* method on the specified contract. func (c *Client) nepBalanceOf(tokenHash, acc util.Uint160, tokenID []byte) (int64, error) { - params := []interface{}{acc} + params := []any{acc} if tokenID != nil { params = append(params, tokenID) } diff --git a/pkg/rpcclient/nep11.go b/pkg/rpcclient/nep11.go index 723922759..e80999c04 100644 --- a/pkg/rpcclient/nep11.go +++ b/pkg/rpcclient/nep11.go @@ -62,7 +62,7 @@ func (c *Client) NEP11TokenInfo(tokenHash util.Uint160) (*wallet.Token, error) { // Deprecated: please use nep11 package, this method will be removed in future // versions. func (c *Client) TransferNEP11(acc *wallet.Account, to util.Uint160, - tokenHash util.Uint160, tokenID string, data interface{}, gas int64, cosigners []SignerAccount) (util.Uint256, error) { + tokenHash util.Uint160, tokenID string, data any, gas int64, cosigners []SignerAccount) (util.Uint256, error) { tx, err := c.CreateNEP11TransferTx(acc, tokenHash, gas, cosigners, to, tokenID, data) if err != nil { return util.Uint256{}, err @@ -82,7 +82,7 @@ func (c *Client) TransferNEP11(acc *wallet.Account, to util.Uint160, // Deprecated: please use nep11 package, this method will be removed in future // versions. func (c *Client) CreateNEP11TransferTx(acc *wallet.Account, tokenHash util.Uint160, - gas int64, cosigners []SignerAccount, args ...interface{}) (*transaction.Transaction, error) { + gas int64, cosigners []SignerAccount, args ...any) (*transaction.Transaction, error) { script, err := smartcontract.CreateCallWithAssertScript(tokenHash, "transfer", args...) if err != nil { return nil, fmt.Errorf("failed to create NEP-11 transfer script: %w", err) @@ -142,7 +142,7 @@ func (c *Client) NEP11NDOwnerOf(tokenHash util.Uint160, tokenID []byte) (util.Ui // Deprecated: please use nep11 package, this method will be removed in future // versions. func (c *Client) TransferNEP11D(acc *wallet.Account, to util.Uint160, - tokenHash util.Uint160, amount int64, tokenID []byte, data interface{}, gas int64, cosigners []SignerAccount) (util.Uint256, error) { + tokenHash util.Uint160, amount int64, tokenID []byte, data any, gas int64, cosigners []SignerAccount) (util.Uint256, error) { tx, err := c.CreateNEP11TransferTx(acc, tokenHash, gas, cosigners, acc.ScriptHash(), to, amount, tokenID, data) if err != nil { return util.Uint256{}, err diff --git a/pkg/rpcclient/nep11/base.go b/pkg/rpcclient/nep11/base.go index 71c75ef7a..db906cb3d 100644 --- a/pkg/rpcclient/nep11/base.go +++ b/pkg/rpcclient/nep11/base.go @@ -10,6 +10,7 @@ purposes, otherwise more specific types are recommended. package nep11 import ( + "errors" "fmt" "math/big" "unicode/utf8" @@ -28,7 +29,7 @@ import ( type Invoker interface { neptoken.Invoker - CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...interface{}) (*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) } @@ -149,7 +150,7 @@ func (t *BaseReader) TokensOfExpanded(account util.Uint160, num int) ([][]byte, // transaction if it's not true. It works for divisible NFTs only when there is // one owner for the particular token. The returned values are transaction hash, // its ValidUntilBlock value and an error if any. -func (t *BaseWriter) Transfer(to util.Uint160, id []byte, data interface{}) (util.Uint256, uint32, error) { +func (t *BaseWriter) Transfer(to util.Uint160, id []byte, data any) (util.Uint256, uint32, error) { script, err := t.transferScript(to, id, data) if err != nil { return util.Uint256{}, 0, err @@ -162,7 +163,7 @@ func (t *BaseWriter) Transfer(to util.Uint160, id []byte, data interface{}) (uti // transaction if it's not true. It works for divisible NFTs only when there is // one owner for the particular token. This transaction is signed, but not sent // to the network, instead it's returned to the caller. -func (t *BaseWriter) TransferTransaction(to util.Uint160, id []byte, data interface{}) (*transaction.Transaction, error) { +func (t *BaseWriter) TransferTransaction(to util.Uint160, id []byte, data any) (*transaction.Transaction, error) { script, err := t.transferScript(to, id, data) if err != nil { return nil, err @@ -175,7 +176,7 @@ func (t *BaseWriter) TransferTransaction(to util.Uint160, id []byte, data interf // transaction if it's not true. It works for divisible NFTs only when there is // one owner for the particular token. This transaction is not signed and just // returned to the caller. -func (t *BaseWriter) TransferUnsigned(to util.Uint160, id []byte, data interface{}) (*transaction.Transaction, error) { +func (t *BaseWriter) TransferUnsigned(to util.Uint160, id []byte, data any) (*transaction.Transaction, error) { script, err := t.transferScript(to, id, data) if err != nil { return nil, err @@ -183,7 +184,7 @@ func (t *BaseWriter) TransferUnsigned(to util.Uint160, id []byte, data interface return t.actor.MakeUnsignedRun(script, nil) } -func (t *BaseWriter) transferScript(params ...interface{}) ([]byte, error) { +func (t *BaseWriter) transferScript(params ...any) ([]byte, error) { return smartcontract.CreateCallWithAssertScript(t.hash, "transfer", params...) } @@ -246,3 +247,71 @@ func UnwrapKnownProperties(m *stackitem.Map, err error) (map[string]string, erro } return res, nil } + +// TransferEventsFromApplicationLog retrieves all emitted TransferEvents from the +// provided [result.ApplicationLog]. +func TransferEventsFromApplicationLog(log *result.ApplicationLog) ([]*TransferEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + var res []*TransferEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "Transfer" { + continue + } + event := new(TransferEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to decode event from stackitem (event #%d, execution #%d): %w", j, i, err) + } + res = append(res, event) + } + } + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to TransferEvent or returns an +// error if it's not possible to do to so. +func (e *TransferEvent) 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 event parameters") + } + + b, err := arr[0].TryBytes() + if err != nil { + return fmt.Errorf("invalid From: %w", err) + } + e.From, err = util.Uint160DecodeBytesBE(b) + if err != nil { + return fmt.Errorf("failed to decode From: %w", err) + } + + b, err = arr[1].TryBytes() + if err != nil { + return fmt.Errorf("invalid To: %w", err) + } + e.To, err = util.Uint160DecodeBytesBE(b) + if err != nil { + return fmt.Errorf("failed to decode To: %w", err) + } + + e.Amount, err = arr[2].TryInteger() + if err != nil { + return fmt.Errorf("field to decode Avount: %w", err) + } + + e.ID, err = arr[3].TryBytes() + if err != nil { + return fmt.Errorf("failed to decode ID: %w", err) + } + + return nil +} diff --git a/pkg/rpcclient/nep11/base_test.go b/pkg/rpcclient/nep11/base_test.go index 33938c0aa..0aacd9f73 100644 --- a/pkg/rpcclient/nep11/base_test.go +++ b/pkg/rpcclient/nep11/base_test.go @@ -21,7 +21,7 @@ type testAct struct { vub uint32 } -func (t *testAct) Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) { +func (t *testAct) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) { return t.res, t.err } func (t *testAct) MakeRun(script []byte) (*transaction.Transaction, error) { @@ -33,7 +33,7 @@ func (t *testAct) MakeUnsignedRun(script []byte, attrs []transaction.Attribute) func (t *testAct) SendRun(script []byte) (util.Uint256, uint32, error) { return t.txh, t.vub, t.err } -func (t *testAct) CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...interface{}) (*result.Invoke, error) { +func (t *testAct) CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...any) (*result.Invoke, error) { return t.res, t.err } func (t *testAct) TerminateSession(sessionID uuid.UUID) error { @@ -217,6 +217,22 @@ func TestReaderTokensOf(t *testing.T) { } } +type tData struct { + someInt int + someString string +} + +func (d *tData) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.Make(d.someInt), + stackitem.Make(d.someString), + }), nil +} + +func (d *tData) FromStackItem(si stackitem.Item) error { + panic("TODO") +} + func TestTokenTransfer(t *testing.T) { ta := new(testAct) tok := NewBase(ta, util.Uint160{1, 2, 3}) @@ -233,7 +249,18 @@ func TestTokenTransfer(t *testing.T) { require.Equal(t, ta.txh, h) require.Equal(t, ta.vub, vub) - _, _, err = tok.Transfer(util.Uint160{3, 2, 1}, []byte{3, 2, 1}, stackitem.NewMap()) + ta.err = nil + ta.txh = util.Uint256{1, 2, 3} + ta.vub = 42 + h, vub, err = tok.Transfer(util.Uint160{3, 2, 1}, []byte{3, 2, 1}, &tData{ + someInt: 5, + someString: "ur", + }) + require.NoError(t, err) + require.Equal(t, ta.txh, h) + require.Equal(t, ta.vub, vub) + + _, _, err = tok.Transfer(util.Uint160{3, 2, 1}, []byte{3, 2, 1}, stackitem.NewPointer(123, []byte{123})) require.Error(t, err) } @@ -241,7 +268,7 @@ func TestTokenTransferTransaction(t *testing.T) { ta := new(testAct) tok := NewBase(ta, util.Uint160{1, 2, 3}) - for _, fun := range []func(to util.Uint160, token []byte, data interface{}) (*transaction.Transaction, error){ + for _, fun := range []func(to util.Uint160, token []byte, data any) (*transaction.Transaction, error){ tok.TransferTransaction, tok.TransferUnsigned, } { @@ -255,7 +282,16 @@ func TestTokenTransferTransaction(t *testing.T) { require.NoError(t, err) require.Equal(t, ta.tx, tx) - _, err = fun(util.Uint160{3, 2, 1}, []byte{3, 2, 1}, stackitem.NewMap()) + ta.err = nil + ta.tx = &transaction.Transaction{Nonce: 100500, ValidUntilBlock: 42} + tx, err = fun(util.Uint160{3, 2, 1}, []byte{3, 2, 1}, &tData{ + someInt: 5, + someString: "ur", + }) + require.NoError(t, err) + require.Equal(t, ta.tx, tx) + + _, err = fun(util.Uint160{3, 2, 1}, []byte{3, 2, 1}, stackitem.NewInterop(nil)) require.Error(t, err) } } diff --git a/pkg/rpcclient/nep11/divisible.go b/pkg/rpcclient/nep11/divisible.go index d558ab05b..59600048a 100644 --- a/pkg/rpcclient/nep11/divisible.go +++ b/pkg/rpcclient/nep11/divisible.go @@ -79,7 +79,7 @@ func (t *DivisibleReader) BalanceOfD(owner util.Uint160, token []byte) (*big.Int // method call using the given parameters and checks for this call result, // failing the transaction if it's not true. The returned values are transaction // hash, its ValidUntilBlock value and an error if any. -func (t *DivisibleWriter) TransferD(from util.Uint160, to util.Uint160, amount *big.Int, id []byte, data interface{}) (util.Uint256, uint32, error) { +func (t *DivisibleWriter) TransferD(from util.Uint160, to util.Uint160, amount *big.Int, id []byte, data any) (util.Uint256, uint32, error) { script, err := t.transferScript(from, to, amount, id, data) if err != nil { return util.Uint256{}, 0, err @@ -92,7 +92,7 @@ func (t *DivisibleWriter) TransferD(from util.Uint160, to util.Uint160, amount * // `transfer` method call using the given parameters and checks for this call // result, failing the transaction if it's not true. This transaction is signed, // but not sent to the network, instead it's returned to the caller. -func (t *DivisibleWriter) TransferDTransaction(from util.Uint160, to util.Uint160, amount *big.Int, id []byte, data interface{}) (*transaction.Transaction, error) { +func (t *DivisibleWriter) TransferDTransaction(from util.Uint160, to util.Uint160, amount *big.Int, id []byte, data any) (*transaction.Transaction, error) { script, err := t.transferScript(from, to, amount, id, data) if err != nil { return nil, err @@ -105,7 +105,7 @@ func (t *DivisibleWriter) TransferDTransaction(from util.Uint160, to util.Uint16 // `transfer` method call using the given parameters and checks for this call // result, failing the transaction if it's not true. This transaction is not // signed and just returned to the caller. -func (t *DivisibleWriter) TransferDUnsigned(from util.Uint160, to util.Uint160, amount *big.Int, id []byte, data interface{}) (*transaction.Transaction, error) { +func (t *DivisibleWriter) TransferDUnsigned(from util.Uint160, to util.Uint160, amount *big.Int, id []byte, data any) (*transaction.Transaction, error) { script, err := t.transferScript(from, to, amount, id, data) if err != nil { return nil, err diff --git a/pkg/rpcclient/nep11/divisible_test.go b/pkg/rpcclient/nep11/divisible_test.go index f44e83c31..84be9469b 100644 --- a/pkg/rpcclient/nep11/divisible_test.go +++ b/pkg/rpcclient/nep11/divisible_test.go @@ -190,7 +190,18 @@ func TestDivisibleTransfer(t *testing.T) { require.Equal(t, ta.txh, h) require.Equal(t, ta.vub, vub) - _, _, err = tok.TransferD(util.Uint160{1, 2, 3}, util.Uint160{3, 2, 1}, big.NewInt(10), []byte{3, 2, 1}, stackitem.NewMap()) + ta.err = nil + ta.txh = util.Uint256{1, 2, 3} + ta.vub = 42 + h, vub, err = tok.TransferD(util.Uint160{1, 2, 3}, util.Uint160{3, 2, 1}, big.NewInt(10), []byte{3, 2, 1}, &tData{ + someInt: 5, + someString: "ur", + }) + require.NoError(t, err) + require.Equal(t, ta.txh, h) + require.Equal(t, ta.vub, vub) + + _, _, err = tok.TransferD(util.Uint160{1, 2, 3}, util.Uint160{3, 2, 1}, big.NewInt(10), []byte{3, 2, 1}, stackitem.NewInterop(nil)) require.Error(t, err) } @@ -198,7 +209,7 @@ func TestDivisibleTransferTransaction(t *testing.T) { ta := new(testAct) tok := NewDivisible(ta, util.Uint160{1, 2, 3}) - for _, fun := range []func(from util.Uint160, to util.Uint160, amount *big.Int, id []byte, data interface{}) (*transaction.Transaction, error){ + for _, fun := range []func(from util.Uint160, to util.Uint160, amount *big.Int, id []byte, data any) (*transaction.Transaction, error){ tok.TransferDTransaction, tok.TransferDUnsigned, } { @@ -212,7 +223,16 @@ func TestDivisibleTransferTransaction(t *testing.T) { require.NoError(t, err) require.Equal(t, ta.tx, tx) - _, err = fun(util.Uint160{1, 2, 3}, util.Uint160{3, 2, 1}, big.NewInt(10), []byte{3, 2, 1}, stackitem.NewMap()) + ta.err = nil + ta.tx = &transaction.Transaction{Nonce: 100500, ValidUntilBlock: 42} + tx, err = fun(util.Uint160{1, 2, 3}, util.Uint160{3, 2, 1}, big.NewInt(10), []byte{3, 2, 1}, &tData{ + someInt: 5, + someString: "ur", + }) + require.NoError(t, err) + require.Equal(t, ta.tx, tx) + + _, err = fun(util.Uint160{1, 2, 3}, util.Uint160{3, 2, 1}, big.NewInt(10), []byte{3, 2, 1}, stackitem.NewInterop(nil)) require.Error(t, err) } } diff --git a/pkg/rpcclient/nep17.go b/pkg/rpcclient/nep17.go index aa73a8c28..ea2c5c894 100644 --- a/pkg/rpcclient/nep17.go +++ b/pkg/rpcclient/nep17.go @@ -15,7 +15,7 @@ type TransferTarget struct { Token util.Uint160 Address util.Uint160 Amount int64 - Data interface{} + Data any } // SignerAccount represents combination of the transaction.Signer and the @@ -75,7 +75,7 @@ func (c *Client) NEP17TokenInfo(tokenHash util.Uint160) (*wallet.Token, error) { // Deprecated: please use nep17 package, this method will be removed in future // versions. func (c *Client) CreateNEP17TransferTx(acc *wallet.Account, to util.Uint160, - token util.Uint160, amount int64, gas int64, data interface{}, cosigners []SignerAccount) (*transaction.Transaction, error) { + token util.Uint160, amount int64, gas int64, data any, cosigners []SignerAccount) (*transaction.Transaction, error) { return c.CreateNEP17MultiTransferTx(acc, gas, []TransferTarget{ {Token: token, Address: to, @@ -164,7 +164,7 @@ func (c *Client) CreateTxFromScript(script []byte, acc *wallet.Account, sysFee, // Deprecated: please use nep17 package, this method will be removed in future // versions. func (c *Client) TransferNEP17(acc *wallet.Account, to util.Uint160, token util.Uint160, - amount int64, gas int64, data interface{}, cosigners []SignerAccount) (util.Uint256, error) { + amount int64, gas int64, data any, cosigners []SignerAccount) (util.Uint256, error) { tx, err := c.CreateNEP17TransferTx(acc, to, token, amount, gas, data, cosigners) if err != nil { return util.Uint256{}, err diff --git a/pkg/rpcclient/nep17/nep17.go b/pkg/rpcclient/nep17/nep17.go index e2f1d71e4..dec570a22 100644 --- a/pkg/rpcclient/nep17/nep17.go +++ b/pkg/rpcclient/nep17/nep17.go @@ -8,12 +8,15 @@ package nep17 import ( "errors" + "fmt" "math/big" "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/neptoken" "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" ) // Invoker is used by TokenReader to call various safe methods. @@ -63,7 +66,7 @@ type TransferParameters struct { From util.Uint160 To util.Uint160 Amount *big.Int - Data interface{} + Data any } // NewReader creates an instance of TokenReader for contract with the given @@ -82,7 +85,7 @@ func New(actor Actor, hash util.Uint160) *Token { // call using the given parameters and checks for this call result, failing the // transaction if it's not true. The returned values are transaction hash, its // ValidUntilBlock value and an error if any. -func (t *TokenWriter) Transfer(from util.Uint160, to util.Uint160, amount *big.Int, data interface{}) (util.Uint256, uint32, error) { +func (t *TokenWriter) Transfer(from util.Uint160, to util.Uint160, amount *big.Int, data any) (util.Uint256, uint32, error) { return t.MultiTransfer([]TransferParameters{{from, to, amount, data}}) } @@ -90,7 +93,7 @@ func (t *TokenWriter) Transfer(from util.Uint160, to util.Uint160, amount *big.I // call using the given parameters and checks for this call result, failing the // transaction if it's not true. This transaction is signed, but not sent to the // network, instead it's returned to the caller. -func (t *TokenWriter) TransferTransaction(from util.Uint160, to util.Uint160, amount *big.Int, data interface{}) (*transaction.Transaction, error) { +func (t *TokenWriter) TransferTransaction(from util.Uint160, to util.Uint160, amount *big.Int, data any) (*transaction.Transaction, error) { return t.MultiTransferTransaction([]TransferParameters{{from, to, amount, data}}) } @@ -98,7 +101,7 @@ func (t *TokenWriter) TransferTransaction(from util.Uint160, to util.Uint160, am // call using the given parameters and checks for this call result, failing the // transaction if it's not true. This transaction is not signed and just returned // to the caller. -func (t *TokenWriter) TransferUnsigned(from util.Uint160, to util.Uint160, amount *big.Int, data interface{}) (*transaction.Transaction, error) { +func (t *TokenWriter) TransferUnsigned(from util.Uint160, to util.Uint160, amount *big.Int, data any) (*transaction.Transaction, error) { return t.MultiTransferUnsigned([]TransferParameters{{from, to, amount, data}}) } @@ -147,3 +150,66 @@ func (t *TokenWriter) MultiTransferUnsigned(params []TransferParameters) (*trans } return t.actor.MakeUnsignedRun(script, nil) } + +// TransferEventsFromApplicationLog retrieves all emitted TransferEvents from the +// provided [result.ApplicationLog]. +func TransferEventsFromApplicationLog(log *result.ApplicationLog) ([]*TransferEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + var res []*TransferEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "Transfer" { + continue + } + event := new(TransferEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to decode event from stackitem (event #%d, execution #%d): %w", j, i, err) + } + res = append(res, event) + } + } + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to TransferEvent or returns an +// error if it's not possible to do to so. +func (e *TransferEvent) 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 event parameters") + } + + b, err := arr[0].TryBytes() + if err != nil { + return fmt.Errorf("invalid From: %w", err) + } + e.From, err = util.Uint160DecodeBytesBE(b) + if err != nil { + return fmt.Errorf("failed to decode From: %w", err) + } + + b, err = arr[1].TryBytes() + if err != nil { + return fmt.Errorf("invalid To: %w", err) + } + e.To, err = util.Uint160DecodeBytesBE(b) + if err != nil { + return fmt.Errorf("failed to decode To: %w", err) + } + + e.Amount, err = arr[2].TryInteger() + if err != nil { + return fmt.Errorf("field to decode Avount: %w", err) + } + + return nil +} diff --git a/pkg/rpcclient/nep17/nep17_test.go b/pkg/rpcclient/nep17/nep17_test.go index b14e84dda..ca9908646 100644 --- a/pkg/rpcclient/nep17/nep17_test.go +++ b/pkg/rpcclient/nep17/nep17_test.go @@ -20,7 +20,7 @@ type testAct struct { vub uint32 } -func (t *testAct) Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) { +func (t *testAct) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) { return t.res, t.err } func (t *testAct) MakeRun(script []byte) (*transaction.Transaction, error) { @@ -62,13 +62,29 @@ func TestReaderBalanceOf(t *testing.T) { require.Error(t, err) } +type tData struct { + someInt int + someString string +} + +func (d *tData) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.Make(d.someInt), + stackitem.Make(d.someString), + }), nil +} + +func (d *tData) FromStackItem(si stackitem.Item) error { + panic("TODO") +} + func TestTokenTransfer(t *testing.T) { ta := new(testAct) tok := New(ta, util.Uint160{1, 2, 3}) - for name, fun := range map[string]func(from util.Uint160, to util.Uint160, amount *big.Int, data interface{}) (util.Uint256, uint32, error){ + for name, fun := range map[string]func(from util.Uint160, to util.Uint160, amount *big.Int, data any) (util.Uint256, uint32, error){ "Tranfer": tok.Transfer, - "MultiTransfer": func(from util.Uint160, to util.Uint160, amount *big.Int, data interface{}) (util.Uint256, uint32, error) { + "MultiTransfer": func(from util.Uint160, to util.Uint160, amount *big.Int, data any) (util.Uint256, uint32, error) { return tok.MultiTransfer([]TransferParameters{{from, to, amount, data}, {from, to, amount, data}}) }, } { @@ -85,7 +101,18 @@ func TestTokenTransfer(t *testing.T) { require.Equal(t, ta.txh, h) require.Equal(t, ta.vub, vub) - _, _, err = fun(util.Uint160{3, 2, 1}, util.Uint160{3, 2, 1}, big.NewInt(1), stackitem.NewMap()) + ta.err = nil + ta.txh = util.Uint256{1, 2, 3} + ta.vub = 42 + h, vub, err = fun(util.Uint160{3, 2, 1}, util.Uint160{3, 2, 1}, big.NewInt(1), &tData{ + someInt: 5, + someString: "ur", + }) + require.NoError(t, err) + require.Equal(t, ta.txh, h) + require.Equal(t, ta.vub, vub) + + _, _, err = fun(util.Uint160{3, 2, 1}, util.Uint160{3, 2, 1}, big.NewInt(1), stackitem.NewInterop(nil)) require.Error(t, err) }) } @@ -99,13 +126,13 @@ func TestTokenTransferTransaction(t *testing.T) { ta := new(testAct) tok := New(ta, util.Uint160{1, 2, 3}) - for name, fun := range map[string]func(from util.Uint160, to util.Uint160, amount *big.Int, data interface{}) (*transaction.Transaction, error){ + for name, fun := range map[string]func(from util.Uint160, to util.Uint160, amount *big.Int, data any) (*transaction.Transaction, error){ "TransferTransaction": tok.TransferTransaction, "TransferUnsigned": tok.TransferUnsigned, - "MultiTransferTransaction": func(from util.Uint160, to util.Uint160, amount *big.Int, data interface{}) (*transaction.Transaction, error) { + "MultiTransferTransaction": func(from util.Uint160, to util.Uint160, amount *big.Int, data any) (*transaction.Transaction, error) { return tok.MultiTransferTransaction([]TransferParameters{{from, to, amount, data}, {from, to, amount, data}}) }, - "MultiTransferUnsigned": func(from util.Uint160, to util.Uint160, amount *big.Int, data interface{}) (*transaction.Transaction, error) { + "MultiTransferUnsigned": func(from util.Uint160, to util.Uint160, amount *big.Int, data any) (*transaction.Transaction, error) { return tok.MultiTransferUnsigned([]TransferParameters{{from, to, amount, data}, {from, to, amount, data}}) }, } { @@ -120,7 +147,16 @@ func TestTokenTransferTransaction(t *testing.T) { require.NoError(t, err) require.Equal(t, ta.tx, tx) - _, err = fun(util.Uint160{3, 2, 1}, util.Uint160{3, 2, 1}, big.NewInt(1), stackitem.NewMap()) + ta.err = nil + ta.tx = &transaction.Transaction{Nonce: 100500, ValidUntilBlock: 42} + tx, err = fun(util.Uint160{3, 2, 1}, util.Uint160{3, 2, 1}, big.NewInt(1), &tData{ + someInt: 5, + someString: "ur", + }) + require.NoError(t, err) + require.Equal(t, ta.tx, tx) + + _, err = fun(util.Uint160{3, 2, 1}, util.Uint160{3, 2, 1}, big.NewInt(1), stackitem.NewInterop(nil)) require.Error(t, err) }) } diff --git a/pkg/rpcclient/neptoken/base.go b/pkg/rpcclient/neptoken/base.go index 610e2be8e..57b504f0a 100644 --- a/pkg/rpcclient/neptoken/base.go +++ b/pkg/rpcclient/neptoken/base.go @@ -24,7 +24,7 @@ const ( // Invoker is used by Base to call various methods. type Invoker interface { - Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) + Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) } // Base is a reader interface for common NEP-11 and NEP-17 methods built diff --git a/pkg/rpcclient/neptoken/base_test.go b/pkg/rpcclient/neptoken/base_test.go index 7f7320d7c..f1c85332e 100644 --- a/pkg/rpcclient/neptoken/base_test.go +++ b/pkg/rpcclient/neptoken/base_test.go @@ -16,7 +16,7 @@ type testInv struct { res *result.Invoke } -func (t *testInv) Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) { +func (t *testInv) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) { return t.res, t.err } diff --git a/pkg/rpcclient/nns/contract_test.go b/pkg/rpcclient/nns/contract_test.go index f69079e68..98a4ef20d 100644 --- a/pkg/rpcclient/nns/contract_test.go +++ b/pkg/rpcclient/nns/contract_test.go @@ -16,10 +16,10 @@ type testAct struct { res *result.Invoke } -func (t *testAct) Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) { +func (t *testAct) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) { return t.res, t.err } -func (t *testAct) CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...interface{}) (*result.Invoke, error) { +func (t *testAct) CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...any) (*result.Invoke, error) { return t.res, t.err } func (t *testAct) TerminateSession(sessionID uuid.UUID) error { diff --git a/pkg/rpcclient/notary/contract.go b/pkg/rpcclient/notary/contract.go index 9bf3b6373..3ded21bc5 100644 --- a/pkg/rpcclient/notary/contract.go +++ b/pkg/rpcclient/notary/contract.go @@ -8,6 +8,8 @@ creation of notary requests. package notary import ( + "errors" + "fmt" "math" "math/big" @@ -18,6 +20,7 @@ import ( "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" ) const ( @@ -27,18 +30,18 @@ const ( // ContractInvoker is used by ContractReader to perform read-only calls. type ContractInvoker interface { - Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) + Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) } // ContractActor is used by Contract to create and send transactions. type ContractActor interface { ContractInvoker - MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) + 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 ...interface{}) (*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 ...interface{}) (util.Uint256, uint32, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) SendRun(script []byte) (util.Uint256, uint32, error) } @@ -68,6 +71,10 @@ type OnNEP17PaymentData struct { Till uint32 } +// OnNEP17PaymentData have to implement stackitem.Convertible interface to be +// compatible with emit package. +var _ = stackitem.Convertible(&OnNEP17PaymentData{}) + // Hash stores the hash of the native Notary contract. var Hash = state.CreateNativeContractHash(nativenames.Notary) @@ -234,3 +241,48 @@ func withdrawScript(from util.Uint160, to util.Uint160) []byte { script, _ := smartcontract.CreateCallWithAssertScript(Hash, "withdraw", from.BytesBE(), to.BytesBE()) return script } + +// ToStackItem implements stackitem.Convertible interface. +func (d *OnNEP17PaymentData) ToStackItem() (stackitem.Item, error) { + return stackitem.NewArray([]stackitem.Item{ + stackitem.Make(d.Account), + stackitem.Make(d.Till), + }), nil +} + +// FromStackItem implements stackitem.Convertible interface. +func (d *OnNEP17PaymentData) FromStackItem(si stackitem.Item) error { + arr, ok := si.Value().([]stackitem.Item) + if !ok { + return fmt.Errorf("unexpected stackitem type: %s", si.Type()) + } + if len(arr) != 2 { + return fmt.Errorf("unexpected number of fields: %d vs %d", len(arr), 2) + } + + if arr[0] != stackitem.Item(stackitem.Null{}) { + accBytes, err := arr[0].TryBytes() + if err != nil { + return fmt.Errorf("failed to retrieve account bytes: %w", err) + } + acc, err := util.Uint160DecodeBytesBE(accBytes) + if err != nil { + return fmt.Errorf("failed to decode account bytes: %w", err) + } + d.Account = &acc + } + till, err := arr[1].TryInteger() + if err != nil { + return fmt.Errorf("failed to retrieve till: %w", err) + } + if !till.IsInt64() { + return errors.New("till is not an int64") + } + val := till.Int64() + if val > math.MaxUint32 { + return fmt.Errorf("till is larger than max uint32 value: %d", val) + } + d.Till = uint32(val) + + return nil +} diff --git a/pkg/rpcclient/notary/contract_test.go b/pkg/rpcclient/notary/contract_test.go index c8498ffbf..336c6ce88 100644 --- a/pkg/rpcclient/notary/contract_test.go +++ b/pkg/rpcclient/notary/contract_test.go @@ -2,9 +2,12 @@ package notary import ( "errors" + "math" "math/big" + "strings" "testing" + "github.com/nspcc-dev/neo-go/internal/testserdes" "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" @@ -20,7 +23,7 @@ type testAct struct { vub uint32 } -func (t *testAct) Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) { +func (t *testAct) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) { return t.res, t.err } func (t *testAct) MakeRun(script []byte) (*transaction.Transaction, error) { @@ -32,13 +35,13 @@ func (t *testAct) MakeUnsignedRun(script []byte, attrs []transaction.Attribute) func (t *testAct) SendRun(script []byte) (util.Uint256, uint32, error) { return t.txh, t.vub, t.err } -func (t *testAct) MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) { +func (t *testAct) MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) { return t.tx, t.err } -func (t *testAct) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error) { +func (t *testAct) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) { return t.tx, t.err } -func (t *testAct) SendCall(contract util.Uint160, method string, params ...interface{}) (util.Uint256, uint32, error) { +func (t *testAct) SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) { return t.txh, t.vub, t.err } @@ -197,3 +200,75 @@ func TestTxMakers(t *testing.T) { }) } } + +func TestOnNEP17PaymentData_Convertible(t *testing.T) { + t.Run("non-empty owner", func(t *testing.T) { + d := &OnNEP17PaymentData{ + Account: &util.Uint160{1, 2, 3}, + Till: 123, + } + testserdes.ToFromStackItem(t, d, new(OnNEP17PaymentData)) + }) + t.Run("empty owner", func(t *testing.T) { + d := &OnNEP17PaymentData{ + Account: nil, + Till: 123, + } + testserdes.ToFromStackItem(t, d, new(OnNEP17PaymentData)) + }) +} + +func TestOnNEP17PaymentDataToStackItem(t *testing.T) { + testCases := map[string]struct { + data *OnNEP17PaymentData + expected stackitem.Item + }{ + "non-empty owner": { + data: &OnNEP17PaymentData{ + Account: &util.Uint160{1, 2, 3}, + Till: 123, + }, + expected: stackitem.NewArray([]stackitem.Item{ + stackitem.Make(util.Uint160{1, 2, 3}), + stackitem.Make(123), + }), + }, + "empty owner": { + data: &OnNEP17PaymentData{ + Account: nil, + Till: 123, + }, + expected: stackitem.NewArray([]stackitem.Item{ + stackitem.Null{}, + stackitem.Make(123), + }), + }, + } + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + actual, err := tc.data.ToStackItem() + require.NoError(t, err) + require.Equal(t, tc.expected, actual) + }) + } +} + +func TestOnNEP17PaymentData_FromStackItem(t *testing.T) { + errCases := map[string]stackitem.Item{ + "unexpected stackitem type": stackitem.NewBool(true), + "unexpected number of fields": stackitem.NewArray([]stackitem.Item{stackitem.NewBool(true)}), + "failed to retrieve account bytes": stackitem.NewArray([]stackitem.Item{stackitem.NewInterop(nil), stackitem.Make(1)}), + "failed to decode account bytes": stackitem.NewArray([]stackitem.Item{stackitem.Make([]byte{1}), stackitem.Make(1)}), + "failed to retrieve till": stackitem.NewArray([]stackitem.Item{stackitem.Make(util.Uint160{1}), stackitem.NewInterop(nil)}), + "till is not an int64": stackitem.NewArray([]stackitem.Item{stackitem.Make(util.Uint160{1}), stackitem.NewBigInteger(new(big.Int).Add(big.NewInt(math.MaxInt64), big.NewInt(1)))}), + "till is larger than max uint32 value": stackitem.NewArray([]stackitem.Item{stackitem.Make(util.Uint160{1}), stackitem.Make(math.MaxUint32 + 1)}), + } + for name, errCase := range errCases { + t.Run(name, func(t *testing.T) { + d := new(OnNEP17PaymentData) + err := d.FromStackItem(errCase) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), name), name) + }) + } +} diff --git a/pkg/rpcclient/notary/doc_test.go b/pkg/rpcclient/notary/doc_test.go index 33e2524f8..fa6f6909a 100644 --- a/pkg/rpcclient/notary/doc_test.go +++ b/pkg/rpcclient/notary/doc_test.go @@ -32,7 +32,7 @@ func ExampleActor() { // Transfer some GAS to the Notary contract to be able to send notary requests // from the first account. gasSingle := gas.New(single) - txid, vub, _ := gasSingle.Transfer(single.Sender(), notary.Hash, big.NewInt(10_0000_0000), notary.OnNEP17PaymentData{Till: 10000000}) + txid, vub, _ := gasSingle.Transfer(single.Sender(), notary.Hash, big.NewInt(10_0000_0000), ¬ary.OnNEP17PaymentData{Till: 10000000}) var depositOK bool // Wait for transaction to be persisted, either it gets in and we get diff --git a/pkg/rpcclient/oracle/oracle.go b/pkg/rpcclient/oracle/oracle.go index fe3a79964..39a12e023 100644 --- a/pkg/rpcclient/oracle/oracle.go +++ b/pkg/rpcclient/oracle/oracle.go @@ -19,16 +19,16 @@ import ( // Invoker is used by ContractReader to call various methods. type Invoker interface { - Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) + Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) } // Actor is used by Contract to create and send transactions. type Actor interface { Invoker - MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) - MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error) - SendCall(contract util.Uint160, method string, params ...interface{}) (util.Uint256, uint32, error) + MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) + MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) } // Hash stores the hash of the native OracleContract contract. diff --git a/pkg/rpcclient/oracle/oracle_test.go b/pkg/rpcclient/oracle/oracle_test.go index 25e960d67..aad3386f3 100644 --- a/pkg/rpcclient/oracle/oracle_test.go +++ b/pkg/rpcclient/oracle/oracle_test.go @@ -20,16 +20,16 @@ type testAct struct { vub uint32 } -func (t *testAct) Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) { +func (t *testAct) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) { return t.res, t.err } -func (t *testAct) MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) { +func (t *testAct) MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) { return t.tx, t.err } -func (t *testAct) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error) { +func (t *testAct) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) { return t.tx, t.err } -func (t *testAct) SendCall(contract util.Uint160, method string, params ...interface{}) (util.Uint256, uint32, error) { +func (t *testAct) SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) { return t.txh, t.vub, t.err } diff --git a/pkg/rpcclient/policy/policy.go b/pkg/rpcclient/policy/policy.go index 2f583db0a..52dd6ba5b 100644 --- a/pkg/rpcclient/policy/policy.go +++ b/pkg/rpcclient/policy/policy.go @@ -18,18 +18,18 @@ import ( // Invoker is used by ContractReader to call various methods. type Invoker interface { - Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) + Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) } // Actor is used by Contract to create and send transactions. type Actor interface { Invoker - MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) + 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 ...interface{}) (*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 ...interface{}) (util.Uint256, uint32, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) SendRun(script []byte) (util.Uint256, uint32, error) } diff --git a/pkg/rpcclient/policy/policy_test.go b/pkg/rpcclient/policy/policy_test.go index aed3dcd16..458c77efd 100644 --- a/pkg/rpcclient/policy/policy_test.go +++ b/pkg/rpcclient/policy/policy_test.go @@ -19,16 +19,16 @@ type testAct struct { vub uint32 } -func (t *testAct) Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) { +func (t *testAct) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) { return t.res, t.err } -func (t *testAct) MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) { +func (t *testAct) MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) { return t.tx, t.err } -func (t *testAct) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error) { +func (t *testAct) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) { return t.tx, t.err } -func (t *testAct) SendCall(contract util.Uint160, method string, params ...interface{}) (util.Uint256, uint32, error) { +func (t *testAct) SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) { return t.txh, t.vub, t.err } func (t *testAct) MakeRun(script []byte) (*transaction.Transaction, error) { diff --git a/pkg/rpcclient/rolemgmt/roles.go b/pkg/rpcclient/rolemgmt/roles.go index c7593e4b5..5bb8dbb91 100644 --- a/pkg/rpcclient/rolemgmt/roles.go +++ b/pkg/rpcclient/rolemgmt/roles.go @@ -19,16 +19,16 @@ import ( // Invoker is used by ContractReader to call various methods. type Invoker interface { - Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) + Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) } // Actor is used by Contract to create and send transactions. type Actor interface { Invoker - MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) - MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error) - SendCall(contract util.Uint160, method string, params ...interface{}) (util.Uint256, uint32, error) + MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) + MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) } // Hash stores the hash of the native RoleManagement contract. diff --git a/pkg/rpcclient/rolemgmt/roles_test.go b/pkg/rpcclient/rolemgmt/roles_test.go index f1899ed80..ffb764fac 100644 --- a/pkg/rpcclient/rolemgmt/roles_test.go +++ b/pkg/rpcclient/rolemgmt/roles_test.go @@ -21,16 +21,16 @@ type testAct struct { vub uint32 } -func (t *testAct) Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) { +func (t *testAct) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) { return t.res, t.err } -func (t *testAct) MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) { +func (t *testAct) MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) { return t.tx, t.err } -func (t *testAct) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error) { +func (t *testAct) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) { return t.tx, t.err } -func (t *testAct) SendCall(contract util.Uint160, method string, params ...interface{}) (util.Uint256, uint32, error) { +func (t *testAct) SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) { return t.txh, t.vub, t.err } diff --git a/pkg/rpcclient/rpc.go b/pkg/rpcclient/rpc.go index 0e7147baa..07cfbe077 100644 --- a/pkg/rpcclient/rpc.go +++ b/pkg/rpcclient/rpc.go @@ -39,7 +39,7 @@ var errNetworkNotInitialized = errors.New("RPC client network is not initialized // filled for standard sig/multisig signers. func (c *Client) CalculateNetworkFee(tx *transaction.Transaction) (int64, error) { var ( - params = []interface{}{tx.Bytes()} + params = []any{tx.Bytes()} resp = new(result.NetworkFee) ) if err := c.performRequest("calculatenetworkfee", params, resp); err != nil { @@ -51,7 +51,7 @@ func (c *Client) CalculateNetworkFee(tx *transaction.Transaction) (int64, error) // GetApplicationLog returns a contract log based on the specified txid. func (c *Client) GetApplicationLog(hash util.Uint256, trig *trigger.Type) (*result.ApplicationLog, error) { var ( - params = []interface{}{hash.StringLE()} + params = []any{hash.StringLE()} resp = new(result.ApplicationLog) ) if trig != nil { @@ -93,13 +93,13 @@ func (c *Client) GetBlockByHash(hash util.Uint256) (*block.Block, error) { return c.getBlock(hash.StringLE()) } -func (c *Client) getBlock(param interface{}) (*block.Block, error) { +func (c *Client) getBlock(param any) (*block.Block, error) { var ( resp []byte err error b *block.Block ) - if err = c.performRequest("getblock", []interface{}{param}, &resp); err != nil { + if err = c.performRequest("getblock", []any{param}, &resp); err != nil { return nil, err } r := io.NewBinReaderFromBuf(resp) @@ -130,9 +130,9 @@ func (c *Client) GetBlockByHashVerbose(hash util.Uint256) (*result.Block, error) return c.getBlockVerbose(hash.StringLE()) } -func (c *Client) getBlockVerbose(param interface{}) (*result.Block, error) { +func (c *Client) getBlockVerbose(param any) (*result.Block, error) { var ( - params = []interface{}{param, 1} // 1 for verbose. + params = []any{param, 1} // 1 for verbose. resp = &result.Block{} err error ) @@ -150,7 +150,7 @@ func (c *Client) getBlockVerbose(param interface{}) (*result.Block, error) { // GetBlockHash returns the hash value of the corresponding block based on the specified index. func (c *Client) GetBlockHash(index uint32) (util.Uint256, error) { var ( - params = []interface{}{index} + params = []any{index} resp = util.Uint256{} ) if err := c.performRequest("getblockhash", params, &resp); err != nil { @@ -164,7 +164,7 @@ func (c *Client) GetBlockHash(index uint32) (util.Uint256, error) { // initialized with Init before calling this method. func (c *Client) GetBlockHeader(hash util.Uint256) (*block.Header, error) { var ( - params = []interface{}{hash.StringLE()} + params = []any{hash.StringLE()} resp []byte h *block.Header ) @@ -199,7 +199,7 @@ func (c *Client) GetBlockHeaderCount() (uint32, error) { // initialized with Init before calling this method. func (c *Client) GetBlockHeaderVerbose(hash util.Uint256) (*result.Header, error) { var ( - params = []interface{}{hash.StringLE(), 1} + params = []any{hash.StringLE(), 1} resp = &result.Header{} ) if err := c.performRequest("getblockheader", params, resp); err != nil { @@ -212,7 +212,7 @@ func (c *Client) GetBlockHeaderVerbose(hash util.Uint256) (*result.Header, error // This method is only supported by NeoGo servers. func (c *Client) GetBlockSysFee(index uint32) (fixedn.Fixed8, error) { var ( - params = []interface{}{index} + params = []any{index} resp fixedn.Fixed8 ) if err := c.performRequest("getblocksysfee", params, &resp); err != nil { @@ -261,9 +261,9 @@ func (c *Client) GetContractStateByID(id int32) (*state.Contract, error) { } // getContractState is an internal representation of GetContractStateBy* methods. -func (c *Client) getContractState(param interface{}) (*state.Contract, error) { +func (c *Client) getContractState(param any) (*state.Contract, error) { var ( - params = []interface{}{param} + params = []any{param} resp = &state.Contract{} ) if err := c.performRequest("getcontractstate", params, resp); err != nil { @@ -291,7 +291,7 @@ func (c *Client) GetNativeContracts() ([]state.NativeContract, error) { // GetNEP11Balances is a wrapper for getnep11balances RPC. func (c *Client) GetNEP11Balances(address util.Uint160) (*result.NEP11Balances, error) { - params := []interface{}{address.StringLE()} + params := []any{address.StringLE()} resp := new(result.NEP11Balances) if err := c.performRequest("getnep11balances", params, resp); err != nil { return nil, err @@ -301,7 +301,7 @@ func (c *Client) GetNEP11Balances(address util.Uint160) (*result.NEP11Balances, // GetNEP17Balances is a wrapper for getnep17balances RPC. func (c *Client) GetNEP17Balances(address util.Uint160) (*result.NEP17Balances, error) { - params := []interface{}{address.StringLE()} + params := []any{address.StringLE()} resp := new(result.NEP17Balances) if err := c.performRequest("getnep17balances", params, resp); err != nil { return nil, err @@ -314,9 +314,9 @@ func (c *Client) GetNEP17Balances(address util.Uint160) (*result.NEP17Balances, // This method is provided mostly for the sake of completeness. For well-known // attributes like "description", "image", "name" and "tokenURI" it returns strings, // while for all others []byte (which can be nil). -func (c *Client) GetNEP11Properties(asset util.Uint160, token []byte) (map[string]interface{}, error) { - params := []interface{}{asset.StringLE(), hex.EncodeToString(token)} - resp := make(map[string]interface{}) +func (c *Client) GetNEP11Properties(asset util.Uint160, token []byte) (map[string]any, error) { + params := []any{asset.StringLE(), hex.EncodeToString(token)} + resp := make(map[string]any) if err := c.performRequest("getnep11properties", params, &resp); err != nil { return nil, err } @@ -355,8 +355,8 @@ func (c *Client) GetNEP11Transfers(address util.Uint160, start, stop *uint64, li return resp, nil } -func packTransfersParams(address util.Uint160, start, stop *uint64, limit, page *int) ([]interface{}, error) { - params := []interface{}{address.StringLE()} +func packTransfersParams(address util.Uint160, start, stop *uint64, limit, page *int) ([]any, error) { + params := []any{address.StringLE()} if start != nil { params = append(params, *start) if stop != nil { @@ -418,7 +418,7 @@ func (c *Client) GetRawMemPool() ([]util.Uint256, error) { // GetRawTransaction returns a transaction by hash. func (c *Client) GetRawTransaction(hash util.Uint256) (*transaction.Transaction, error) { var ( - params = []interface{}{hash.StringLE()} + params = []any{hash.StringLE()} resp []byte err error ) @@ -437,7 +437,7 @@ func (c *Client) GetRawTransaction(hash util.Uint256) (*transaction.Transaction, // NOTE: to get transaction.ID and transaction.Size, use t.Hash() and io.GetVarSize(t) respectively. func (c *Client) GetRawTransactionVerbose(hash util.Uint256) (*result.TransactionOutputRaw, error) { var ( - params = []interface{}{hash.StringLE(), 1} // 1 for verbose. + params = []any{hash.StringLE(), 1} // 1 for verbose. resp = &result.TransactionOutputRaw{} err error ) @@ -447,11 +447,36 @@ func (c *Client) GetRawTransactionVerbose(hash util.Uint256) (*result.Transactio return resp, nil } +// GetProof returns existence proof of storage item state by the given stateroot +// historical contract hash and historical item key. +func (c *Client) GetProof(stateroot util.Uint256, historicalContractHash util.Uint160, historicalKey []byte) (*result.ProofWithKey, error) { + var ( + params = []any{stateroot.StringLE(), historicalContractHash.StringLE(), historicalKey} + resp = &result.ProofWithKey{} + ) + if err := c.performRequest("getproof", params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// VerifyProof returns value by the given stateroot and proof. +func (c *Client) VerifyProof(stateroot util.Uint256, proof *result.ProofWithKey) ([]byte, error) { + var ( + params = []any{stateroot.StringLE(), proof.String()} + resp []byte + ) + if err := c.performRequest("verifyproof", params, &resp); err != nil { + return nil, err + } + return resp, nil +} + // GetState returns historical contract storage item state by the given stateroot, // historical contract hash and historical item key. func (c *Client) GetState(stateroot util.Uint256, historicalContractHash util.Uint160, historicalKey []byte) ([]byte, error) { var ( - params = []interface{}{stateroot.StringLE(), historicalContractHash.StringLE(), historicalKey} + params = []any{stateroot.StringLE(), historicalContractHash.StringLE(), historicalKey} resp []byte ) if err := c.performRequest("getstate", params, &resp); err != nil { @@ -470,7 +495,7 @@ func (c *Client) FindStates(stateroot util.Uint256, historicalContractHash util. historicalPrefix = []byte{} } var ( - params = []interface{}{stateroot.StringLE(), historicalContractHash.StringLE(), historicalPrefix} + params = []any{stateroot.StringLE(), historicalContractHash.StringLE(), historicalPrefix} resp result.FindStates ) if start == nil && maxCount != nil { @@ -498,9 +523,9 @@ func (c *Client) GetStateRootByBlockHash(hash util.Uint256) (*state.MPTRoot, err return c.getStateRoot(hash) } -func (c *Client) getStateRoot(param interface{}) (*state.MPTRoot, error) { +func (c *Client) getStateRoot(param any) (*state.MPTRoot, error) { var resp = new(state.MPTRoot) - if err := c.performRequest("getstateroot", []interface{}{param}, resp); err != nil { + if err := c.performRequest("getstateroot", []any{param}, resp); err != nil { return nil, err } return resp, nil @@ -518,15 +543,15 @@ func (c *Client) GetStateHeight() (*result.StateHeight, error) { // GetStorageByID returns the stored value according to the contract ID and the stored key. func (c *Client) GetStorageByID(id int32, key []byte) ([]byte, error) { - return c.getStorage([]interface{}{id, key}) + return c.getStorage([]any{id, key}) } // GetStorageByHash returns the stored value according to the contract script hash and the stored key. func (c *Client) GetStorageByHash(hash util.Uint160, key []byte) ([]byte, error) { - return c.getStorage([]interface{}{hash.StringLE(), key}) + return c.getStorage([]any{hash.StringLE(), key}) } -func (c *Client) getStorage(params []interface{}) ([]byte, error) { +func (c *Client) getStorage(params []any) ([]byte, error) { var resp []byte if err := c.performRequest("getstorage", params, &resp); err != nil { return nil, err @@ -537,7 +562,7 @@ func (c *Client) getStorage(params []interface{}) ([]byte, error) { // GetTransactionHeight returns the block index where the transaction is found. func (c *Client) GetTransactionHeight(hash util.Uint256) (uint32, error) { var ( - params = []interface{}{hash.StringLE()} + params = []any{hash.StringLE()} resp uint32 ) if err := c.performRequest("gettransactionheight", params, &resp); err != nil { @@ -549,7 +574,7 @@ func (c *Client) GetTransactionHeight(hash util.Uint256) (uint32, error) { // GetUnclaimedGas returns the unclaimed GAS amount for the specified address. func (c *Client) GetUnclaimedGas(address string) (result.UnclaimedGas, error) { var ( - params = []interface{}{address} + params = []any{address} resp result.UnclaimedGas ) if err := c.performRequest("getunclaimedgas", params, &resp); err != nil { @@ -592,7 +617,7 @@ func (c *Client) GetVersion() (*result.Version, error) { // InvokeScript returns the result of the given script after running it true the VM. // NOTE: This is a test invoke and will not affect the blockchain. func (c *Client) InvokeScript(script []byte, signers []transaction.Signer) (*result.Invoke, error) { - var p = []interface{}{script} + var p = []any{script} return c.invokeSomething("invokescript", p, signers) } @@ -601,7 +626,7 @@ func (c *Client) InvokeScript(script []byte, signers []transaction.Signer) (*res // height. // NOTE: This is a test invoke and will not affect the blockchain. func (c *Client) InvokeScriptAtHeight(height uint32, script []byte, signers []transaction.Signer) (*result.Invoke, error) { - var p = []interface{}{height, script} + var p = []any{height, script} return c.invokeSomething("invokescripthistoric", p, signers) } @@ -610,7 +635,7 @@ func (c *Client) InvokeScriptAtHeight(height uint32, script []byte, signers []tr // state root or block hash. // NOTE: This is a test invoke and will not affect the blockchain. func (c *Client) InvokeScriptWithState(stateOrBlock util.Uint256, script []byte, signers []transaction.Signer) (*result.Invoke, error) { - var p = []interface{}{stateOrBlock.StringLE(), script} + var p = []any{stateOrBlock.StringLE(), script} return c.invokeSomething("invokescripthistoric", p, signers) } @@ -618,7 +643,7 @@ func (c *Client) InvokeScriptWithState(stateOrBlock util.Uint256, script []byte, // with the given operation and parameters. // NOTE: this is test invoke and will not affect the blockchain. func (c *Client) InvokeFunction(contract util.Uint160, operation string, params []smartcontract.Parameter, signers []transaction.Signer) (*result.Invoke, error) { - var p = []interface{}{contract.StringLE(), operation, params} + var p = []any{contract.StringLE(), operation, params} return c.invokeSomething("invokefunction", p, signers) } @@ -627,7 +652,7 @@ func (c *Client) InvokeFunction(contract util.Uint160, operation string, params // specified by the blockchain height. // NOTE: this is test invoke and will not affect the blockchain. func (c *Client) InvokeFunctionAtHeight(height uint32, contract util.Uint160, operation string, params []smartcontract.Parameter, signers []transaction.Signer) (*result.Invoke, error) { - var p = []interface{}{height, contract.StringLE(), operation, params} + var p = []any{height, contract.StringLE(), operation, params} return c.invokeSomething("invokefunctionhistoric", p, signers) } @@ -636,7 +661,7 @@ func (c *Client) InvokeFunctionAtHeight(height uint32, contract util.Uint160, op // by the specified state root or block hash. // NOTE: this is test invoke and will not affect the blockchain. func (c *Client) InvokeFunctionWithState(stateOrBlock util.Uint256, contract util.Uint160, operation string, params []smartcontract.Parameter, signers []transaction.Signer) (*result.Invoke, error) { - var p = []interface{}{stateOrBlock.StringLE(), contract.StringLE(), operation, params} + var p = []any{stateOrBlock.StringLE(), contract.StringLE(), operation, params} return c.invokeSomething("invokefunctionhistoric", p, signers) } @@ -644,7 +669,7 @@ func (c *Client) InvokeFunctionWithState(stateOrBlock util.Uint256, contract uti // with the given parameters under verification trigger type. // NOTE: this is test invoke and will not affect the blockchain. func (c *Client) InvokeContractVerify(contract util.Uint160, params []smartcontract.Parameter, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error) { - var p = []interface{}{contract.StringLE(), params} + var p = []any{contract.StringLE(), params} return c.invokeSomething("invokecontractverify", p, signers, witnesses...) } @@ -653,7 +678,7 @@ func (c *Client) InvokeContractVerify(contract util.Uint160, params []smartcontr // at the blockchain state specified by the blockchain height. // NOTE: this is test invoke and will not affect the blockchain. func (c *Client) InvokeContractVerifyAtHeight(height uint32, contract util.Uint160, params []smartcontract.Parameter, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error) { - var p = []interface{}{height, contract.StringLE(), params} + var p = []any{height, contract.StringLE(), params} return c.invokeSomething("invokecontractverifyhistoric", p, signers, witnesses...) } @@ -662,12 +687,12 @@ func (c *Client) InvokeContractVerifyAtHeight(height uint32, contract util.Uint1 // at the blockchain state specified by the state root or block hash. // NOTE: this is test invoke and will not affect the blockchain. func (c *Client) InvokeContractVerifyWithState(stateOrBlock util.Uint256, contract util.Uint160, params []smartcontract.Parameter, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error) { - var p = []interface{}{stateOrBlock.StringLE(), contract.StringLE(), params} + var p = []any{stateOrBlock.StringLE(), contract.StringLE(), params} return c.invokeSomething("invokecontractverifyhistoric", p, signers, witnesses...) } // invokeSomething is an inner wrapper for Invoke* functions. -func (c *Client) invokeSomething(method string, p []interface{}, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error) { +func (c *Client) invokeSomething(method string, p []any, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error) { var resp = new(result.Invoke) if signers != nil { if witnesses == nil { @@ -697,7 +722,7 @@ func (c *Client) invokeSomething(method string, p []interface{}, signers []trans // hash returned from server, when not it's a locally calculated rawTX hash. func (c *Client) SendRawTransaction(rawTX *transaction.Transaction) (util.Uint256, error) { var ( - params = []interface{}{rawTX.Bytes()} + params = []any{rawTX.Bytes()} resp = new(result.RelayResult) ) if err := c.performRequest("sendrawtransaction", params, resp); err != nil { @@ -709,7 +734,7 @@ func (c *Client) SendRawTransaction(rawTX *transaction.Transaction) (util.Uint25 // SubmitBlock broadcasts a raw block over the NEO network. func (c *Client) SubmitBlock(b block.Block) (util.Uint256, error) { var ( - params []interface{} + params []any resp = new(result.RelayResult) ) buf := io.NewBufBinWriter() @@ -717,7 +742,7 @@ func (c *Client) SubmitBlock(b block.Block) (util.Uint256, error) { if err := buf.Err; err != nil { return util.Uint256{}, err } - params = []interface{}{buf.Bytes()} + params = []any{buf.Bytes()} if err := c.performRequest("submitblock", params, resp); err != nil { return util.Uint256{}, err @@ -727,7 +752,7 @@ func (c *Client) SubmitBlock(b block.Block) (util.Uint256, error) { // SubmitRawOracleResponse submits a raw oracle response to the oracle node. // Raw params are used to avoid excessive marshalling. -func (c *Client) SubmitRawOracleResponse(ps []interface{}) error { +func (c *Client) SubmitRawOracleResponse(ps []any) error { return c.performRequest("submitoracleresponse", ps, new(result.RelayResult)) } @@ -966,7 +991,7 @@ func (c *Client) SubmitP2PNotaryRequest(req *payload.P2PNotaryRequest) (util.Uin if err != nil { return util.Uint256{}, fmt.Errorf("failed to encode request: %w", err) } - params := []interface{}{bytes} + params := []any{bytes} if err := c.performRequest("submitnotaryrequest", params, resp); err != nil { return util.Uint256{}, err } @@ -977,7 +1002,7 @@ func (c *Client) SubmitP2PNotaryRequest(req *payload.P2PNotaryRequest) (util.Uin // Consider using [address] package instead to do it locally. func (c *Client) ValidateAddress(address string) error { var ( - params = []interface{}{address} + params = []any{address} resp = &result.ValidateAddress{} ) @@ -1143,7 +1168,7 @@ func (c *Client) TraverseIterator(sessionID, iteratorID uuid.UUID, maxItemsCount maxItemsCount = config.DefaultMaxIteratorResultItems } var ( - params = []interface{}{sessionID.String(), iteratorID.String(), maxItemsCount} + params = []any{sessionID.String(), iteratorID.String(), maxItemsCount} resp []json.RawMessage ) if err := c.performRequest("traverseiterator", params, &resp); err != nil { @@ -1165,7 +1190,7 @@ func (c *Client) TraverseIterator(sessionID, iteratorID uuid.UUID, maxItemsCount // the specified session was found on server. func (c *Client) TerminateSession(sessionID uuid.UUID) (bool, error) { var resp bool - params := []interface{}{sessionID.String()} + params := []any{sessionID.String()} if err := c.performRequest("terminatesession", params, &resp); err != nil { return false, err } diff --git a/pkg/rpcclient/rpc_test.go b/pkg/rpcclient/rpc_test.go index 815798f4b..590b82872 100644 --- a/pkg/rpcclient/rpc_test.go +++ b/pkg/rpcclient/rpc_test.go @@ -6,7 +6,6 @@ import ( "encoding/base64" "encoding/hex" "encoding/json" - "errors" "fmt" "math/big" "net/http" @@ -25,6 +24,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/neorpc/result" @@ -43,11 +43,11 @@ import ( type rpcClientTestCase struct { name string - invoke func(c *Client) (interface{}, error) + invoke func(c *Client) (any, error) fails bool serverResponse string - result func(c *Client) interface{} - check func(t *testing.T, c *Client, result interface{}) + result func(c *Client) any + check func(t *testing.T, c *Client, result any) } const base64B1 = "AAAAAMSdeyVO3QCekJTbQ4D7YEEGFv2nXPd6CfO5Kn3htI8P9tVA4+VXLyyMG12BUj6qB3CZ+JIFwWCcDF+KBHH0VSjJICSkegEAAAAAAAAAAAAAAQAAAABouegejA5skHm0udEF6HinbT8iPwHGDEBg0hpK90iZlB4ZSCG7BOr7BsvPXGDax360lvqKeNFuzaGI1RYNH50/dhQLxocy90JdsIOyodd1sOJGEjZIt7ztDEAHc2avJzz6tK+FOQMIZO/FEEikJdLJX0+iZXFcsmDRpB7lo2wWMSQbcoTXNg7leuR0VeDsKJ+YdvCuTG5WbiqWDECa6Yjj+bK4te5KR5jdLF5kLt03csyozZcd/X7NPt89IsX01zpX8ec3e+B2qySJIOhEf3cK0i+5U5wyXiFcRI8xkxMMIQIQOn990BZVhZf3lg0nxRakOU/ZaLnmUVXrSwE+QEBAbgwhAqe8Vf6GhOARl2jRBLoweVvcyGYZ6GSt0mFWcj7Rhc1iDCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcIMIQPZDAffY+aQzneRLhCrUazJRLZoYCN7YIxPj4MJ5x7mmRRBntDcOgIAAgAAAMDYpwAAAAAADHlDAAAAAACwBAAAAWi56B6MDmyQebS50QXoeKdtPyI/AQBbCwIY3fUFDBTunqIsJ+NL0BSPxBCOCPdOj1BIsgwUaLnoHowObJB5tLnRBeh4p20/Ij8UwB8MCHRyYW5zZmVyDBT1Y+pAvCg9TQ4FxI6jBbPyoHNA70FifVtSOQHGDEC8InWg8rQHWjklRojobu7kn4r0xZY2xWYs15ggVX4PQyEHpNTU6vZHT2TXRdPXAOKHhgWAttO0oTvo+9VZAjIVDEBF0qvBMlvmYJIYLqSoCjhBykcSN78UXrBjO5BKL8BpHtejWCld1VT6Z7nYrEBLgySD6HeMcp/fa6vqHzU220e/DECXtm5AA1jy9GFA7t8U6a+1uPrQFk4Ufp0UyXsun0PvN0NdhrHc37xm8k9Z0dB85V/7WLtkMaLLyjVNVIKImC76kxMMIQIQOn990BZVhZf3lg0nxRakOU/ZaLnmUVXrSwE+QEBAbgwhAqe8Vf6GhOARl2jRBLoweVvcyGYZ6GSt0mFWcj7Rhc1iDCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcIMIQPZDAffY+aQzneRLhCrUazJRLZoYCN7YIxPj4MJ5x7mmRRBntDcOgADAAAAwNinAAAAAACsiEMAAAAAALAEAAABaLnoHowObJB5tLnRBeh4p20/Ij8BAF8LAwDodkgXAAAADBTunqIsJ+NL0BSPxBCOCPdOj1BIsgwUaLnoHowObJB5tLnRBeh4p20/Ij8UwB8MCHRyYW5zZmVyDBTPduKL0AYsSkeO41VhARMZ88+k0kFifVtSOQHGDEDgj/SQT84EbWRZ4ZKhyjJTuLwVPDgVlQO3CGmgacItvni9nziJvTxziZXBG/0Hqkv68ddS1EH94RtWlqLQWRCjDEAWZUeSQ8KskILSvoWPN3836xpg/TYzOGiFVoePv91CFnap4fRFxdbporBgnZ/sUsjFZ74U8f+r0riqtvkdMMyGDEDx5iho79oDVYOCwIDH3K1UeDjAT6Hq9YsD9SCfJSE1rRsAdJPh2StYxdh9Jah1lwGbW0U+Wu6zpbVFf5CS6fFckxMMIQIQOn990BZVhZf3lg0nxRakOU/ZaLnmUVXrSwE+QEBAbgwhAqe8Vf6GhOARl2jRBLoweVvcyGYZ6GSt0mFWcj7Rhc1iDCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcIMIQPZDAffY+aQzneRLhCrUazJRLZoYCN7YIxPj4MJ5x7mmRRBntDcOg==" @@ -115,11 +115,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getapplicationlog": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetApplicationLog(util.Uint256{}, nil) }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"txid":"0x17145a039fca704fcdbeb46e6b210af98a1a9e5b9768e46ffc38f71c79ac2521","executions":[{"trigger":"Application","vmstate":"HALT","gasconsumed":"1","stack":[{"type":"Integer","value":"1"}],"notifications":[]}]}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { txHash, err := util.Uint256DecodeStringLE("17145a039fca704fcdbeb46e6b210af98a1a9e5b9768e46ffc38f71c79ac2521") if err != nil { panic(err) @@ -142,11 +142,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getbestblockhash": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBestBlockHash() }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":"0x773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { result, err := util.Uint256DecodeStringLE("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e") if err != nil { panic(err) @@ -158,29 +158,29 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getblock": { { name: "byIndex_positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockByIndex(1) }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":"` + base64B1 + `"}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { b := getResultBlock1() return &b.Block }, }, { name: "byIndex_verbose_positive", - invoke: func(c *Client) (i interface{}, err error) { + invoke: func(c *Client) (i any, err error) { return c.GetBlockByIndexVerbose(1) }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":` + b1Verbose + `}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { res := getResultBlock1() return res }, }, { name: "byHash_positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := util.Uint256DecodeStringLE("d151651e86680a7ecbc87babf3346a42e7bc9974414ce192c9c22ac4f2e9d043") if err != nil { panic(err) @@ -188,14 +188,14 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ return c.GetBlockByHash(hash) }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":"` + base64B1 + `"}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { b := getResultBlock1() return &b.Block }, }, { name: "byHash_verbose_positive", - invoke: func(c *Client) (i interface{}, err error) { + invoke: func(c *Client) (i any, err error) { hash, err := util.Uint256DecodeStringLE("86fe1061140b2ea791b0739fb9732abc6e5e47de4927228a1ac41de3d93eb7cb") if err != nil { panic(err) @@ -203,7 +203,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ return c.GetBlockByHashVerbose(hash) }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":` + b1Verbose + `}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { res := getResultBlock1() return res }, @@ -212,11 +212,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getblockcount": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockCount() }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":991991}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return uint32(991991) }, }, @@ -224,11 +224,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getblockhash": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockHash(1) }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":"0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2"}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { hash, err := util.Uint256DecodeStringLE("4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2") if err != nil { panic(err) @@ -240,7 +240,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getblockheader": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := util.Uint256DecodeStringLE("68e4bd688b852e807eef13a0ff7da7b02223e359a35153667e88f9cb4a3b0801") if err != nil { panic(err) @@ -248,14 +248,14 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ return c.GetBlockHeader(hash) }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":"` + base64Header1 + `"}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { b := getResultBlock1() return &b.Header }, }, { name: "verbose_positive", - invoke: func(c *Client) (i interface{}, err error) { + invoke: func(c *Client) (i any, err error) { hash, err := util.Uint256DecodeStringLE("cbb73ed9e31dc41a8a222749de475e6ebc2a73b99f73b091a72e0b146110fe86") if err != nil { panic(err) @@ -263,7 +263,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ return c.GetBlockHeaderVerbose(hash) }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":` + header1Verbose + `}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { b := getResultBlock1() return &result.Header{ Header: b.Header, @@ -279,11 +279,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getblockheadercount": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockHeaderCount() }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":2021}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return uint32(2021) }, }, @@ -291,11 +291,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getblocksysfee": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockSysFee(1) }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":"195500"}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return fixedn.Fixed8FromInt64(195500) }, }, @@ -303,11 +303,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getcommittee": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetCommittee() }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":["02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e"]}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { member, err := keys.NewPublicKeyFromString("02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e") if err != nil { panic(fmt.Errorf("failed to decode public key: %w", err)) @@ -319,11 +319,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getconnectioncount": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetConnectionCount() }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":10}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return 10 }, }, @@ -331,7 +331,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getcontractstate": { { name: "positive, by hash", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := util.Uint160DecodeStringLE("1b4357bff5a01bdf2a6581247cf9ed1e24629176") if err != nil { panic(err) @@ -339,7 +339,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ return c.GetContractStateByHash(hash) }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"nef":{"magic":860243278,"compiler":"neo-go-3.0","script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","checksum":2512077441},"manifest":{"name":"Test","abi":{"methods":[],"events":[]},"groups":[],"features":{},"permissions":[],"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { script, err := base64.StdEncoding.DecodeString("VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==") if err != nil { panic(err) @@ -358,11 +358,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ }, { name: "positive, by address", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetContractStateByAddressOrName("NWiu5oejTu925aeL9Hc1LX8SvaJhE23h15") }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"nef":{"magic":860243278,"compiler":"neo-go-3.0","script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","checksum":2512077441},"manifest":{"name":"Test","abi":{"methods":[],"events":[]},"groups":[],"features":{},"permissions":[],"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { script, err := base64.StdEncoding.DecodeString("VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==") if err != nil { panic(err) @@ -381,11 +381,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ }, { name: "positive, by id", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetContractStateByID(0) }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"nef":{"magic":860243278,"compiler":"neo-go-3.0","script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","checksum":2512077441},"manifest":{"name":"Test","abi":{"methods":[],"events":[]},"groups":[],"features":{},"permissions":[],"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { script, err := base64.StdEncoding.DecodeString("VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==") if err != nil { panic(err) @@ -406,11 +406,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getFeePerByte": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetFeePerByte() }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMDWdldEZlZVBlckJ5dGUMFJphpG7sl7iTBtfOgfFbRiCR0AkyQWJ9W1I=","stack":[{"type":"Integer","value":"1000"}],"tx":null}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return int64(1000) }, }, @@ -418,11 +418,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getExecFeeFactor": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetExecFeeFactor() }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMDWdldEZlZVBlckJ5dGUMFJphpG7sl7iTBtfOgfFbRiCR0AkyQWJ9W1I=","stack":[{"type":"Integer","value":"1000"}],"tx":null}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return int64(1000) }, }, @@ -430,11 +430,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getStoragePrice": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetStoragePrice() }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMDWdldEZlZVBlckJ5dGUMFJphpG7sl7iTBtfOgfFbRiCR0AkyQWJ9W1I=","stack":[{"type":"Integer","value":"100000"}],"tx":null}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return int64(100000) }, }, @@ -442,11 +442,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getOraclePrice": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetOraclePrice() }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMDWdldEZlZVBlckJ5dGUMFJphpG7sl7iTBtfOgfFbRiCR0AkyQWJ9W1I=","stack":[{"type":"Integer","value":"10000000"}],"tx":null}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return int64(10000000) }, }, @@ -454,11 +454,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getNNSPrice": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetNNSPrice(util.Uint160{1, 2, 3}) }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMDWdldEZlZVBlckJ5dGUMFJphpG7sl7iTBtfOgfFbRiCR0AkyQWJ9W1I=","stack":[{"type":"Integer","value":"1000000"}],"tx":null}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return int64(1000000) }, }, @@ -466,11 +466,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getGasPerBlock": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetGasPerBlock() }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMDWdldEZlZVBlckJ5dGUMFJphpG7sl7iTBtfOgfFbRiCR0AkyQWJ9W1I=","stack":[{"type":"Integer","value":"500000000"}],"tx":null}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return int64(500000000) }, }, @@ -478,11 +478,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getCandidateRegisterPrice": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetCandidateRegisterPrice() }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMDWdldEZlZVBlckJ5dGUMFJphpG7sl7iTBtfOgfFbRiCR0AkyQWJ9W1I=","stack":[{"type":"Integer","value":"100000000000"}],"tx":null}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return int64(100000000000) }, }, @@ -490,11 +490,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getDesignatedByRole": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetDesignatedByRole(noderoles.P2PNotary, 10) }, serverResponse: `{"id" : 1,"result" : {"stack" : [{"value" : [{"type":"ByteString","value":"Aw0WkQoDc8WqpG18xPMTEgfHO6gRTVtMN0Mw6zw06fzl"},{"type":"ByteString","value":"A+bmJ9wIaj96Ygr+uQQvQ0AaUrQmj2b3AGnztAOkU3/L"}],"type" : "Array"}],"exception" : null,"script" : "ERQSwB8ME2dldERlc2lnbmF0ZWRCeVJvbGUMFOKV45FUTBeK2U8D7E3N/3hTTs9JQWJ9W1I=","gasconsumed" : "2028150","state" : "HALT"}, "jsonrpc" : "2.0"}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { pk1Bytes, _ := base64.StdEncoding.DecodeString("Aw0WkQoDc8WqpG18xPMTEgfHO6gRTVtMN0Mw6zw06fzl") pk1, err := keys.NewPublicKeyFromBytes(pk1Bytes, elliptic.P256()) if err != nil { @@ -512,11 +512,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getMaxNotValidBeforeDelta": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetMaxNotValidBeforeDelta() }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMD2dldE1heEJsb2NrU2l6ZQwUmmGkbuyXuJMG186B8VtGIJHQCTJBYn1bUg==","stack":[{"type":"Integer","value":"262144"}],"tx":null}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return int64(262144) }, }, @@ -524,11 +524,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "isBlocked": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.IsBlocked(util.Uint160{1, 2, 3}) }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMEmdldEJsb2NrZWRBY2NvdW50cwwUmmGkbuyXuJMG186B8VtGIJHQCTJBYn1bUg==","stack":[{"type":"Boolean","value":false}],"tx":null}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return false }, }, @@ -536,7 +536,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getnep11balances": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := util.Uint160DecodeStringLE("1aada0032aba1ef6d1f07bbd8bec1d85f5380fb3") if err != nil { panic(err) @@ -544,7 +544,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ return c.GetNEP11Balances(hash) }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"balance":[{"assethash":"a48b6e1291ba24211ad11bb90ae2a10bf1fcd5a8","symbol":"SOME","decimals":"42","name":"Contract","tokens":[{"tokenid":"abcdef","amount":"1","lastupdatedblock":251604}]}],"address":"NcEkNmgWmf7HQVQvzhxpengpnt4DXjmZLe"}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { hash, err := util.Uint160DecodeStringLE("a48b6e1291ba24211ad11bb90ae2a10bf1fcd5a8") if err != nil { panic(err) @@ -569,7 +569,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getnep17balances": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := util.Uint160DecodeStringLE("1aada0032aba1ef6d1f07bbd8bec1d85f5380fb3") if err != nil { panic(err) @@ -577,7 +577,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ return c.GetNEP17Balances(hash) }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"balance":[{"assethash":"a48b6e1291ba24211ad11bb90ae2a10bf1fcd5a8","symbol":"N17","decimals":"8","name":"Token","amount":"50000000000","lastupdatedblock":251604}],"address":"AY6eqWjsUFCzsVELG7yG72XDukKvC34p2w"}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { hash, err := util.Uint160DecodeStringLE("a48b6e1291ba24211ad11bb90ae2a10bf1fcd5a8") if err != nil { panic(err) @@ -599,7 +599,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getnep11properties": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := util.Uint160DecodeStringLE("1aada0032aba1ef6d1f07bbd8bec1d85f5380fb3") if err != nil { panic(err) @@ -607,8 +607,8 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ return c.GetNEP11Properties(hash, []byte("abcdef")) }, // NcEkNmgWmf7HQVQvzhxpengpnt4DXjmZLe serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"name":"sometoken","field1":"c29tZXRoaW5n","field2":null}}`, - result: func(c *Client) interface{} { - return map[string]interface{}{ + result: func(c *Client) any { + return map[string]any{ "name": "sometoken", "field1": []byte("something"), "field2": nil, @@ -619,7 +619,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getnep11transfers": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := address.StringToUint160("NcEkNmgWmf7HQVQvzhxpengpnt4DXjmZLe") if err != nil { panic(err) @@ -627,7 +627,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ return c.GetNEP11Transfers(hash, nil, nil, nil, nil) }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"sent":[],"received":[{"timestamp":1555651816,"assethash":"600c4f5200db36177e3e8a09e9f18e2fc7d12a0f","transferaddress":"NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP","amount":"1","tokenid":"abcdef","blockindex":436036,"transfernotifyindex":0,"txhash":"df7683ece554ecfb85cf41492c5f143215dd43ef9ec61181a28f922da06aba58"}],"address":"NcEkNmgWmf7HQVQvzhxpengpnt4DXjmZLe"}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { assetHash, err := util.Uint160DecodeStringLE("600c4f5200db36177e3e8a09e9f18e2fc7d12a0f") if err != nil { panic(err) @@ -658,7 +658,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getnep17transfers": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := address.StringToUint160("NcEkNmgWmf7HQVQvzhxpengpnt4DXjmZLe") if err != nil { panic(err) @@ -666,7 +666,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ return c.GetNEP17Transfers(hash, nil, nil, nil, nil) }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"sent":[],"received":[{"timestamp":1555651816,"assethash":"600c4f5200db36177e3e8a09e9f18e2fc7d12a0f","transferaddress":"AYwgBNMepiv5ocGcyNT4mA8zPLTQ8pDBis","amount":"1000000","blockindex":436036,"transfernotifyindex":0,"txhash":"df7683ece554ecfb85cf41492c5f143215dd43ef9ec61181a28f922da06aba58"}],"address":"NcEkNmgWmf7HQVQvzhxpengpnt4DXjmZLe"}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { assetHash, err := util.Uint160DecodeStringLE("600c4f5200db36177e3e8a09e9f18e2fc7d12a0f") if err != nil { panic(err) @@ -696,28 +696,29 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getpeers": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetPeers() }, - serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"unconnected":[{"address":"172.200.0.1","port":"20333"}],"connected":[{"address":"127.0.0.1","port":"20335"}],"bad":[{"address":"172.200.0.254","port":"20332"}]}}`, - result: func(c *Client) interface{} { + serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"unconnected":[{"address":"172.200.0.1","port":20333}],"connected":[{"address":"127.0.0.1","port":20335}],"bad":[{"address":"172.200.0.254","port":20332}]}}`, + result: func(c *Client) any { + return &result.GetPeers{ Unconnected: result.Peers{ { Address: "172.200.0.1", - Port: "20333", + Port: 20333, }, }, Connected: result.Peers{ { Address: "127.0.0.1", - Port: "20335", + Port: 20335, }, }, Bad: result.Peers{ { Address: "172.200.0.254", - Port: "20332", + Port: 20332, }, }, } @@ -727,11 +728,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getrawmempool": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetRawMemPool() }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":["0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"]}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { hash, err := util.Uint256DecodeStringLE("9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e") if err != nil { panic(err) @@ -743,7 +744,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getrawtransaction": { { name: "positive", - invoke: func(c *Client) (i interface{}, err error) { + invoke: func(c *Client) (i any, err error) { hash, err := util.Uint256DecodeStringLE("f5fbd303799f24ba247529d7544d4276cca54ea79f4b98095f2b0557313c5275") if err != nil { panic(err) @@ -751,14 +752,14 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ return c.GetRawTransaction(hash) }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":"` + base64TxMoveNeo + `"}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { tx := getTxMoveNeo() return &tx.Transaction }, }, { name: "verbose_positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := util.Uint256DecodeStringLE("f5fbd303799f24ba247529d7544d4276cca54ea79f4b98095f2b0557313c5275") if err != nil { panic(err) @@ -771,7 +772,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ return out, nil }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":` + txMoveNeoVerbose + `}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return getTxMoveNeo() }, }, @@ -779,11 +780,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getstateroot": { { name: "positive, by height", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetStateRootByHeight(5) }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"version":0,"index":5,"roothash":"0x65d19151694321e70c6d184b37a2bcf7af4a2c60c099af332a4f7815e3670686","witnesses":[]}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { h, err := util.Uint256DecodeStringLE("65d19151694321e70c6d184b37a2bcf7af4a2c60c099af332a4f7815e3670686") if err != nil { panic(err) @@ -798,7 +799,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ }, { name: "positive, by hash", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := util.Uint256DecodeStringLE("86fe1061140b2ea791b0739fb9732abc6e5e47de4927228a1ac41de3d93eb7cb") if err != nil { panic(err) @@ -806,7 +807,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ return c.GetStateRootByBlockHash(hash) }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"version":0,"index":5,"roothash":"0x65d19151694321e70c6d184b37a2bcf7af4a2c60c099af332a4f7815e3670686","witnesses":[]}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { h, _ := util.Uint256DecodeStringLE("65d19151694321e70c6d184b37a2bcf7af4a2c60c099af332a4f7815e3670686") return &state.MPTRoot{ Version: 0, @@ -820,28 +821,88 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getstate": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { root, _ := util.Uint256DecodeStringLE("252e9d73d49c95c7618d40650da504e05183a1b2eed0685e42c360413c329170") cHash, _ := util.Uint160DecodeStringLE("5c9e40a12055c6b9e3f72271c9779958c842135d") return c.GetState(root, cHash, []byte("testkey")) }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":"dGVzdHZhbHVl"}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return []byte("testvalue") }, }, }, + "getproof": { + { + name: "positive", + invoke: func(c *Client) (any, error) { + root, _ := util.Uint256DecodeStringLE("272002b11a6a39035c719defec3e4e6a8d1f4ae37a995b44734911413fcc2ba5") + cHash, _ := util.Uint160DecodeStringLE("cc5e4edd9f5f8dba8bb65734541df7a1c081c67b") + key := []byte{10} + return c.GetProof(root, cHash, key) + }, + serverResponse: `{"jsonrpc":"2.0","id":1,"result":"Bfn///8KBiQBAQ8DogBnMdiiPTEW05A6bJPmQ2TNVpuca/nB1rJRdQX7R4SyAAQEBAQEBAQDHvo5Rc9v\u002BWSpfsnMXM75ku\u002BZjvbLJhWXn/lh6L\u002B1yB0EA4k\u002Bsx4f7IgmdHNm3wRMpj5kTU4l0gChSGppo5p5wZyWA2\u002BKSFn16W6tRrGSfJob\u002BgqJukLcNDk0DBFYW2wIS2/NAzkugdLfZRXHOLqq5XJr89ElzlqyXU1o9D87l9YOcXjGBAQEA7oDTOxuU4iMAKPuhn5eJjzsM56bQrx3uORa8LKm42oDBCkBBg8PDw8PDwN96s39UOSCwMJmMQZzNjfNAPCbRRyke1B4VRKqOZ0NHlIAA2woQ13XO4Ug2aQ/cW4WBricVcUVqobFUU0dnRPtfIHeAxuYERXsV6HwdGjW\u002BhtpM0FEkw/mllbH5pyhn\u002BBx4r8wBAQEBAQEBAQEBAQEBAQEJAEBCgPXvpMqBogTeGhXjtFY4Rsn9bY/PgNX0l4iYOHMzUBQQgQCAugD"}`, + result: func(c *Client) any { + b, _ := base64.StdEncoding.DecodeString("Bfn///8KBiQBAQ8DogBnMdiiPTEW05A6bJPmQ2TNVpuca/nB1rJRdQX7R4SyAAQEBAQEBAQDHvo5Rc9v\u002BWSpfsnMXM75ku\u002BZjvbLJhWXn/lh6L\u002B1yB0EA4k\u002Bsx4f7IgmdHNm3wRMpj5kTU4l0gChSGppo5p5wZyWA2\u002BKSFn16W6tRrGSfJob\u002BgqJukLcNDk0DBFYW2wIS2/NAzkugdLfZRXHOLqq5XJr89ElzlqyXU1o9D87l9YOcXjGBAQEA7oDTOxuU4iMAKPuhn5eJjzsM56bQrx3uORa8LKm42oDBCkBBg8PDw8PDwN96s39UOSCwMJmMQZzNjfNAPCbRRyke1B4VRKqOZ0NHlIAA2woQ13XO4Ug2aQ/cW4WBricVcUVqobFUU0dnRPtfIHeAxuYERXsV6HwdGjW\u002BhtpM0FEkw/mllbH5pyhn\u002BBx4r8wBAQEBAQEBAQEBAQEBAQEJAEBCgPXvpMqBogTeGhXjtFY4Rsn9bY/PgNX0l4iYOHMzUBQQgQCAugD") + proof := &result.ProofWithKey{} + r := io.NewBinReaderFromBuf(b) + proof.DecodeBinary(r) + return proof + }, + }, + { + name: "not found", + invoke: func(c *Client) (any, error) { + root, _ := util.Uint256DecodeStringLE("272002b11a6a39035c719defec3e4e6a8d1f4ae37a995b44734911413fcc2ba5") + cHash, _ := util.Uint160DecodeStringLE("cc5e4edd9f5f8dba8bb65734541df7a1c081c67b") + key := []byte{01} + return c.GetProof(root, cHash, key) + }, + serverResponse: `{"id":1,"jsonrpc":"2.0","error":{"code":-32603,"message":"Internal error","data":"failed to get proof: item not found"}}`, + fails: true, + }, + }, + "verifyproof": { + { + name: "positive", + invoke: func(c *Client) (any, error) { + root, _ := util.Uint256DecodeStringLE("272002b11a6a39035c719defec3e4e6a8d1f4ae37a995b44734911413fcc2ba5") + b, _ := base64.StdEncoding.DecodeString("Bfn///8KBiQBAQ8DogBnMdiiPTEW05A6bJPmQ2TNVpuca/nB1rJRdQX7R4SyAAQEBAQEBAQDHvo5Rc9v\u002BWSpfsnMXM75ku\u002BZjvbLJhWXn/lh6L\u002B1yB0EA4k\u002Bsx4f7IgmdHNm3wRMpj5kTU4l0gChSGppo5p5wZyWA2\u002BKSFn16W6tRrGSfJob\u002BgqJukLcNDk0DBFYW2wIS2/NAzkugdLfZRXHOLqq5XJr89ElzlqyXU1o9D87l9YOcXjGBAQEA7oDTOxuU4iMAKPuhn5eJjzsM56bQrx3uORa8LKm42oDBCkBBg8PDw8PDwN96s39UOSCwMJmMQZzNjfNAPCbRRyke1B4VRKqOZ0NHlIAA2woQ13XO4Ug2aQ/cW4WBricVcUVqobFUU0dnRPtfIHeAxuYERXsV6HwdGjW\u002BhtpM0FEkw/mllbH5pyhn\u002BBx4r8wBAQEBAQEBAQEBAQEBAQEJAEBCgPXvpMqBogTeGhXjtFY4Rsn9bY/PgNX0l4iYOHMzUBQQgQCAugD") + proof := &result.ProofWithKey{} + r := io.NewBinReaderFromBuf(b) + proof.DecodeBinary(r) + return c.VerifyProof(root, proof) + }, + serverResponse: `{"jsonrpc":"2.0","id":1,"result":"6AM="}`, + result: func(c *Client) any { + return bigint.ToPreallocatedBytes(big.NewInt(1000), nil) + }, + }, + { + name: "fail", + invoke: func(c *Client) (any, error) { + root, _ := util.Uint256DecodeStringLE("272002b11a6a39035c719defec3e4e6a8d1f4ae37a995b44734911413fcc2ba5") + b, _ := base64.StdEncoding.DecodeString("Bfn///8KBiQBAQ8DogBnMdiiPTEW05A6bJPmQ2TNVpuca/nB1rJRdQX7R4SyAAQEBAQEBAQDHvo5Rc9v\u002BWSpfsnMXM75ku\u002BZjvbLJhWXn/lh6L\u002B1yB0EA4k\u002Bsx4f7IgmdHNm3wRMpj5kTU4l0gChSGppo5p5wZyWA2\u002BKSFn16W6tRrGSfJob\u002BgqJukLcNDk0DBFYW2wIS2/NAzkugdLfZRXHOLqq5XJr89ElzlqyXU1o9D87l9YOcXjGBAQEA7oDTOxuU4iMAKPuhn5eJjzsM56bQrx3uORa8LKm42oDBCkBBg8PDw8PDwN96s39UOSCwMJmMQZzNjfNAPCbRRyke1B4VRKqOZ0NHlIAA2woQ13XO4Ug2aQ/cW4WBricVcUVqobFUU0dnRPtfIHeAxuYERXsV6HwdGjW\u002BhtpM0FEkw/mllbH5pyhn\u002BBx4r8wBAQEBAQEBAQEBAQEBAQEJAEBCgPXvpMqBogTeGhXjtFY4Rsn9bY/PgNX0l4iYOHMzUBQQgQCAugD") + proof := &result.ProofWithKey{} + r := io.NewBinReaderFromBuf(b) + proof.DecodeBinary(r) + return c.VerifyProof(root, proof) + }, + serverResponse: `{"id":1,"jsonrpc":"2.0","result":"invalid"}`, + fails: true, + }, + }, "findstates": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { root, _ := util.Uint256DecodeStringLE("252e9d73d49c95c7618d40650da504e05183a1b2eed0685e42c360413c329170") cHash, _ := util.Uint160DecodeStringLE("5c9e40a12055c6b9e3f72271c9779958c842135d") count := 1 return c.FindStates(root, cHash, []byte("aa"), []byte("aa00"), &count) }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"results":[{"key":"YWExMA==","value":"djI="}],"firstProof":"CAEAAABhYTEwCXIAA5KNHjQ1+LFX4lQBLjMAhaLtTJnfuI86O7WnNdlshsYWBAQEBAQEBAQDTzD7MJp2KW6E8BNVjjjgZMTAjI/GI3ZrTmR2UUOtSeIEBAQEBAPKPqb0qnb4Ywz6gqpNKCUNQsfBmAnKc5p3dxokSQRpwgRSAAQDPplG1wee4KOfkehaF94R5uoKSgvQL1j5gkFTN4ywYaIEBAOhOyI39MZfoKc940g57XeqwRnxh7P62fKjnfEtBzQxHQQEBAQEBAQEBAQEBCkBBgAAAAAAAAM6A1UrwFYZAEMfe6go3jX25xz2sHsovQ2UO/UHqZZOXLIABAOwg7pkXyaTR85yQIvYnoGaG/OVRLRHOj+nhZnXb6dVtAQEBAPnciBUp3uspLQTajKTlAxgrNe+3tlqlbwlNRkz0eNmhQMzoMcWOFi9nCyn+eM5lA6Pq67DxzTQDlHljh8g8kRtJAPq9hxzTgreK0qDTavsethixguZYfV7wDmKfumMglnoqQQEBAQEBAM1x2dVBdf5BJ0Xvw2qqhvpKqxdHb8/HMFWiXkJj1uAAQQEJgEDAQYBA5kV2WLkgey9C5z6gZT69VLKcEuwyY8P853rNtGhT3NeUgAEBAQDiX59K9PuJ5RE7Z1uj7q/QJ8FGf8avLdWM7hwmWkVH2gEBAQEBAQEBAQEBAQD1SubX5XhFHcUOWdUzg1bXmDwWJwt+wpU3FOdFkU1PXBSAAQDHCzfEQyqwOO263EE6HER1vWDrwz8JiEHEOXfZ3kX7NYEBAQDEH++Hy8wBcniKuWVevaAwzHCh60kzncU30E5fDC3gJsEBAQEBAQEBAQEBCUBAgMAA1wt18LbxMKdYcJ+nEDMMWZbRsu550l8HGhcYhpl6DjSBAICdjI=","truncated":true}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { proofB, _ := base64.StdEncoding.DecodeString("CAEAAABhYTEwCXIAA5KNHjQ1+LFX4lQBLjMAhaLtTJnfuI86O7WnNdlshsYWBAQEBAQEBAQDTzD7MJp2KW6E8BNVjjjgZMTAjI/GI3ZrTmR2UUOtSeIEBAQEBAPKPqb0qnb4Ywz6gqpNKCUNQsfBmAnKc5p3dxokSQRpwgRSAAQDPplG1wee4KOfkehaF94R5uoKSgvQL1j5gkFTN4ywYaIEBAOhOyI39MZfoKc940g57XeqwRnxh7P62fKjnfEtBzQxHQQEBAQEBAQEBAQEBCkBBgAAAAAAAAM6A1UrwFYZAEMfe6go3jX25xz2sHsovQ2UO/UHqZZOXLIABAOwg7pkXyaTR85yQIvYnoGaG/OVRLRHOj+nhZnXb6dVtAQEBAPnciBUp3uspLQTajKTlAxgrNe+3tlqlbwlNRkz0eNmhQMzoMcWOFi9nCyn+eM5lA6Pq67DxzTQDlHljh8g8kRtJAPq9hxzTgreK0qDTavsethixguZYfV7wDmKfumMglnoqQQEBAQEBAM1x2dVBdf5BJ0Xvw2qqhvpKqxdHb8/HMFWiXkJj1uAAQQEJgEDAQYBA5kV2WLkgey9C5z6gZT69VLKcEuwyY8P853rNtGhT3NeUgAEBAQDiX59K9PuJ5RE7Z1uj7q/QJ8FGf8avLdWM7hwmWkVH2gEBAQEBAQEBAQEBAQD1SubX5XhFHcUOWdUzg1bXmDwWJwt+wpU3FOdFkU1PXBSAAQDHCzfEQyqwOO263EE6HER1vWDrwz8JiEHEOXfZ3kX7NYEBAQDEH++Hy8wBcniKuWVevaAwzHCh60kzncU30E5fDC3gJsEBAQEBAQEBAQEBCUBAgMAA1wt18LbxMKdYcJ+nEDMMWZbRsu550l8HGhcYhpl6DjSBAICdjI=") proof := &result.ProofWithKey{} r := io.NewBinReaderFromBuf(proofB) @@ -857,11 +918,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getstateheight": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetStateHeight() }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"localrootindex":11646,"validatedrootindex":11645}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return &result.StateHeight{ Local: 11646, Validated: 11645, @@ -872,7 +933,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getstorage": { { name: "by hash, positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := util.Uint160DecodeStringLE("03febccf81ac85e3d795bc5cbd4e84e907812aa3") if err != nil { panic(err) @@ -884,7 +945,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ return c.GetStorageByHash(hash, key) }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":"TGlu"}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { value, err := hex.DecodeString("4c696e") if err != nil { panic(err) @@ -894,7 +955,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ }, { name: "by ID, positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { key, err := hex.DecodeString("5065746572") if err != nil { panic(err) @@ -902,7 +963,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ return c.GetStorageByID(-1, key) }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":"TGlu"}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { value, err := hex.DecodeString("4c696e") if err != nil { panic(err) @@ -914,7 +975,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "gettransactionheight": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := util.Uint256DecodeStringLE("cb6ddb5f99d6af4c94a6c396d5294472f2eebc91a2c933e0f527422296fa9fb2") if err != nil { panic(err) @@ -922,7 +983,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ return c.GetTransactionHeight(hash) }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":1}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return uint32(1) }, }, @@ -930,11 +991,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getunclaimedgas": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetUnclaimedGas("NMipL5VsNoLUBUJKPKLhxaEbPQVCZnyJyB") }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"address":"NMipL5VsNoLUBUJKPKLhxaEbPQVCZnyJyB","unclaimed":"897299680935"}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { addr, err := address.StringToUint160("NMipL5VsNoLUBUJKPKLhxaEbPQVCZnyJyB") if err != nil { panic(fmt.Errorf("failed to parse UnclaimedGas address: %w", err)) @@ -949,12 +1010,12 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getcandidates": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetCandidates() }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":[{"publickey":"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2","votes":"0","active":true},{"publickey":"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e","votes":"0","active":true},{"publickey":"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699","votes":"0","active":true},{"publickey":"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62","votes":"0","active":true}]}`, - result: func(c *Client) interface{} { return []result.Candidate{} }, - check: func(t *testing.T, c *Client, uns interface{}) { + result: func(c *Client) any { return []result.Candidate{} }, + check: func(t *testing.T, c *Client, uns any) { res, ok := uns.([]result.Candidate) require.True(t, ok) assert.Equal(t, 4, len(res)) @@ -964,12 +1025,12 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getvalidators": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetNextBlockValidators() }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":[{"publickey":"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2","votes":"0","active":true},{"publickey":"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e","votes":"0","active":true},{"publickey":"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699","votes":"0","active":true},{"publickey":"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62","votes":"0","active":true}]}`, - result: func(c *Client) interface{} { return []result.Validator{} }, - check: func(t *testing.T, c *Client, uns interface{}) { + result: func(c *Client) any { return []result.Validator{} }, + check: func(t *testing.T, c *Client, uns any) { res, ok := uns.([]result.Validator) require.True(t, ok) assert.Equal(t, 4, len(res)) @@ -979,11 +1040,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "getversion": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetVersion() }, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"tcpport":20332,"wsport":20342,"nonce":2153672787,"useragent":"/NEO-GO:0.73.1-pre-273-ge381358/"}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return &result.Version{ TCPPort: uint16(20332), WSPort: uint16(20342), @@ -999,7 +1060,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "invokefunction": { { name: "positive, by scripthash", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := util.Uint160DecodeStringLE("91b83e96f2a7c4fdf0c1688441ec61986c7cae26") if err != nil { panic(err) @@ -1018,10 +1079,10 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ }}) }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"FCaufGyYYexBhGjB8P3Ep/KWPriRUcEJYmFsYW5jZU9mZ74557Vi9gy/4q68o3Wi5e4oc3yv","state":"HALT","gasconsumed":"31100000","stack":[{"type":"ByteString","value":"JivsCEQy"}],"tx":"AAgAAACAlpgAAAAAAAIEEwAAAAAAsAQAAAGqis+FnU/kArNOZz8hVoIXlqSI6wEAVwHoAwwUqorPhZ1P5AKzTmc/IVaCF5akiOsMFOeetm08E0pKd27oB9LluEbdpP2wE8AMCHRyYW5zZmVyDBTnnrZtPBNKSndu6AfS5bhG3aT9sEFifVtSOAFCDEDYNAh3TUvYsZrocFYdBvJ0Trdnj1jRuQzy9Q6YroP2Cwgk4v7q3vbeZBikz8Q7vB+RbDPsWUy+ZiqdkkeG4XoUKQwhArNiK/QBe9/jF8WK7V9MdT8ga324lgRvp9d0u8S/f43CC0GVRA14"}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return &result.Invoke{} }, - check: func(t *testing.T, c *Client, uns interface{}) { + check: func(t *testing.T, c *Client, uns any) { res, ok := uns.(*result.Invoke) require.True(t, ok) bytes, err := hex.DecodeString("262bec084432") @@ -1041,7 +1102,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ }, { name: "positive, FAULT state", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := util.Uint160DecodeStringLE("91b83e96f2a7c4fdf0c1688441ec61986c7cae26") if err != nil { panic(err) @@ -1060,10 +1121,10 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ }}) }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"FCaufGyYYexBhGjB8P3Ep/KWPriRUcEJYmFsYW5jZU9mZ74557Vi9gy/4q68o3Wi5e4oc3yv","state":"FAULT","gasconsumed":"31100000","stack":[{"type":"ByteString","value":"JivsCEQy"}],"tx":"AAgAAACAlpgAAAAAAAIEEwAAAAAAsAQAAAGqis+FnU/kArNOZz8hVoIXlqSI6wEAVwHoAwwUqorPhZ1P5AKzTmc/IVaCF5akiOsMFOeetm08E0pKd27oB9LluEbdpP2wE8AMCHRyYW5zZmVyDBTnnrZtPBNKSndu6AfS5bhG3aT9sEFifVtSOAFCDEDYNAh3TUvYsZrocFYdBvJ0Trdnj1jRuQzy9Q6YroP2Cwgk4v7q3vbeZBikz8Q7vB+RbDPsWUy+ZiqdkkeG4XoUKQwhArNiK/QBe9/jF8WK7V9MdT8ga324lgRvp9d0u8S/f43CC0GVRA14","exception":"gas limit exceeded"}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return &result.Invoke{} }, - check: func(t *testing.T, c *Client, uns interface{}) { + check: func(t *testing.T, c *Client, uns any) { res, ok := uns.(*result.Invoke) require.True(t, ok) bytes, err := hex.DecodeString("262bec084432") @@ -1085,7 +1146,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "invokescript": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { script, err := base64.StdEncoding.DecodeString("AARuYW1lZyQFjl4bYAiEfNZicoVJCIqe6CGR") if err != nil { panic(err) @@ -1095,7 +1156,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ }}) }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"AARuYW1lZyQFjl4bYAiEfNZicoVJCIqe6CGR","state":"HALT","gasconsumed":"16100000","stack":[{"type":"ByteString","value":"TkVQNSBHQVM="}],"tx":null}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { bytes, err := hex.DecodeString("4e45503520474153") if err != nil { panic(err) @@ -1116,7 +1177,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "invokecontractverify": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { contr, err := util.Uint160DecodeStringLE("af7c7328eee5a275a3bcaee2bf0cf662b5e739be") if err != nil { panic(err) @@ -1124,10 +1185,10 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ return c.InvokeContractVerify(contr, nil, []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}, transaction.Witness{InvocationScript: []byte{1, 2, 3}}) }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"FCaufGyYYexBhGjB8P3Ep/KWPriRUcEJYmFsYW5jZU9mZ74557Vi9gy/4q68o3Wi5e4oc3yv","state":"HALT","gasconsumed":"31100000","stack":[{"type":"ByteString","value":"JivsCEQy"}],"tx":"AAgAAACAlpgAAAAAAAIEEwAAAAAAsAQAAAGqis+FnU/kArNOZz8hVoIXlqSI6wEAVwHoAwwUqorPhZ1P5AKzTmc/IVaCF5akiOsMFOeetm08E0pKd27oB9LluEbdpP2wE8AMCHRyYW5zZmVyDBTnnrZtPBNKSndu6AfS5bhG3aT9sEFifVtSOAFCDEDYNAh3TUvYsZrocFYdBvJ0Trdnj1jRuQzy9Q6YroP2Cwgk4v7q3vbeZBikz8Q7vB+RbDPsWUy+ZiqdkkeG4XoUKQwhArNiK/QBe9/jF8WK7V9MdT8ga324lgRvp9d0u8S/f43CC0GVRA14"}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { return &result.Invoke{} }, - check: func(t *testing.T, c *Client, uns interface{}) { + check: func(t *testing.T, c *Client, uns any) { res, ok := uns.(*result.Invoke) require.True(t, ok) bytes, err := hex.DecodeString("262bec084432") @@ -1147,7 +1208,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ }, { name: "bad witness number", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.InvokeContractVerify(util.Uint160{}, nil, []transaction.Signer{{}}, []transaction.Witness{{}, {}}...) }, fails: true, @@ -1156,11 +1217,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "sendrawtransaction": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.SendRawTransaction(transaction.New([]byte{byte(opcode.PUSH1)}, 0)) }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"hash":"0x72159b0cf1221110daad6e1df6ef4ff03012173b63c86910bd7134deb659c875"}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { h, err := util.Uint256DecodeStringLE("72159b0cf1221110daad6e1df6ef4ff03012173b63c86910bd7134deb659c875") if err != nil { panic(fmt.Errorf("can't decode `sendrawtransaction` result hash: %w", err)) @@ -1172,7 +1233,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "submitblock": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.SubmitBlock(block.Block{ Header: block.Header{}, Transactions: nil, @@ -1180,7 +1241,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ }) }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"hash":"0x1bdea8f80eb5bd97fade38d5e7fb93b02c9d3e01394e9f4324218132293f7ea6"}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { h, err := util.Uint256DecodeStringLE("1bdea8f80eb5bd97fade38d5e7fb93b02c9d3e01394e9f4324218132293f7ea6") if err != nil { panic(fmt.Errorf("can't decode `submitblock` result hash: %w", err)) @@ -1192,11 +1253,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ "validateaddress": { { name: "positive", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return nil, c.ValidateAddress("AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i") }, serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"address":"AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i","isvalid":true}}`, - result: func(c *Client) interface{} { + result: func(c *Client) any { // no error expected return nil }, @@ -1206,14 +1267,14 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ type rpcClientErrorCase struct { name string - invoke func(c *Client) (interface{}, error) + invoke func(c *Client) (any, error) } var rpcClientErrorCases = map[string][]rpcClientErrorCase{ `{"jsonrpc":"2.0","id":1,"result":{"name":"name","bad":42}}`: { { name: "getnep11properties_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetNEP11Properties(util.Uint160{}, []byte{}) }, }, @@ -1221,7 +1282,7 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ `{"jsonrpc":"2.0","id":1,"result":{"name":100500,"good":"c29tZXRoaW5n"}}`: { { name: "getnep11properties_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetNEP11Properties(util.Uint160{}, []byte{}) }, }, @@ -1229,13 +1290,13 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ `{"jsonrpc":"2.0","id":1,"result":"not-a-hex-string"}`: { { name: "getblock_not_a_hex_response", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockByIndex(1) }, }, { name: "getblockheader_not_a_hex_response", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := util.Uint256DecodeStringLE("e93d17a52967f9e69314385482bf86f85260e811b46bf4d4b261a7f4135a623c") if err != nil { panic(err) @@ -1245,7 +1306,7 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ }, { name: "getrawtransaction_not_a_hex_response", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := util.Uint256DecodeStringLE("e93d17a52967f9e69314385482bf86f85260e811b46bf4d4b261a7f4135a623c") if err != nil { panic(err) @@ -1255,7 +1316,7 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ }, { name: "getstoragebyhash_not_a_hex_response", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := util.Uint160DecodeStringLE("03febccf81ac85e3d795bc5cbd4e84e907812aa3") if err != nil { panic(err) @@ -1269,7 +1330,7 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ }, { name: "getstoragebyid_not_a_hex_response", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { key, err := hex.DecodeString("5065746572") if err != nil { panic(err) @@ -1281,13 +1342,13 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ `{"jsonrpc":"2.0","id":1,"result":"01"}`: { { name: "getblock_decodebin_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockByIndex(1) }, }, { name: "getheader_decodebin_err", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := util.Uint256DecodeStringLE("e93d17a52967f9e69314385482bf86f85260e811b46bf4d4b261a7f4135a623c") if err != nil { panic(err) @@ -1297,7 +1358,7 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ }, { name: "getrawtransaction_decodebin_err", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { hash, err := util.Uint256DecodeStringLE("e93d17a52967f9e69314385482bf86f85260e811b46bf4d4b261a7f4135a623c") if err != nil { panic(err) @@ -1309,13 +1370,13 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ `{"jsonrpc":"2.0","id":1,"result":false}`: { { name: "sendrawtransaction_bad_server_answer", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.SendRawTransaction(transaction.New([]byte{byte(opcode.PUSH1)}, 0)) }, }, { name: "submitblock_bad_server_answer", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.SubmitBlock(block.Block{ Header: block.Header{}, Transactions: nil, @@ -1325,7 +1386,7 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ }, { name: "validateaddress_bad_server_answer", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return nil, c.ValidateAddress("AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i") }, }, @@ -1333,116 +1394,116 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ `{"id":1,"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"}}`: { { name: "getapplicationlog_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetApplicationLog(util.Uint256{}, nil) }, }, { name: "getbestblockhash_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBestBlockHash() }, }, { name: "getblock_byindex_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockByIndex(1) }, }, { name: "getblock_byindex_verbose_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockByIndexVerbose(1) }, }, { name: "getblock_byhash_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockByHash(util.Uint256{}) }, }, { name: "getblock_byhash_verbose_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockByHashVerbose(util.Uint256{}) }, }, { name: "getblockhash_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockHash(0) }, }, { name: "getblockheader_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockHeader(util.Uint256{}) }, }, { name: "getblockheader_verbose_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockHeaderVerbose(util.Uint256{}) }, }, { name: "getblocksysfee_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockSysFee(1) }, }, { name: "getconnectioncount_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetConnectionCount() }, }, { name: "getcontractstate_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetContractStateByHash(util.Uint160{}) }, }, { name: "getnep11balances_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetNEP11Balances(util.Uint160{}) }, }, { name: "getnep17balances_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetNEP17Balances(util.Uint160{}) }, }, { name: "getnep11properties_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetNEP11Properties(util.Uint160{}, []byte{}) }, }, { name: "getnep11transfers_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetNEP11Transfers(util.Uint160{}, nil, nil, nil, nil) }, }, { name: "getnep17transfers_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetNEP17Transfers(util.Uint160{}, nil, nil, nil, nil) }, }, { name: "getnep17transfers_invalid_params_error 2", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { var stop uint64 return c.GetNEP17Transfers(util.Uint160{}, nil, &stop, nil, nil) }, }, { name: "getnep17transfers_invalid_params_error 3", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { var start uint64 var limit int return c.GetNEP17Transfers(util.Uint160{}, &start, nil, &limit, nil) @@ -1450,7 +1511,7 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ }, { name: "getnep17transfers_invalid_params_error 4", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { var start, stop uint64 var page int return c.GetNEP17Transfers(util.Uint160{}, &start, &stop, nil, &page) @@ -1458,67 +1519,67 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ }, { name: "getrawtransaction_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetRawTransaction(util.Uint256{}) }, }, { name: "getrawtransaction_verbose_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetRawTransactionVerbose(util.Uint256{}) }, }, { name: "getstoragebyhash_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetStorageByHash(util.Uint160{}, []byte{}) }, }, { name: "getstoragebyid_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetStorageByID(-1, []byte{}) }, }, { name: "gettransactionheight_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetTransactionHeight(util.Uint256{}) }, }, { name: "getunclaimedgas_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetUnclaimedGas("") }, }, { name: "invokefunction_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.InvokeFunction(util.Uint160{}, "", []smartcontract.Parameter{}, nil) }, }, { name: "invokescript_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.InvokeScript([]byte{}, nil) }, }, { name: "sendrawtransaction_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.SendRawTransaction(&transaction.Transaction{}) }, }, { name: "submitblock_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.SubmitBlock(block.Block{}) }, }, { name: "validateaddress_invalid_params_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return nil, c.ValidateAddress("") }, }, @@ -1526,193 +1587,193 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ `{}`: { { name: "getapplicationlog_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetApplicationLog(util.Uint256{}, nil) }, }, { name: "getbestblockhash_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBestBlockHash() }, }, { name: "getblock_byindex_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockByIndex(1) }, }, { name: "getblock_byindex_verbose_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockByIndexVerbose(1) }, }, { name: "getblock_byhash_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockByHash(util.Uint256{}) }, }, { name: "getblock_byhash_verbose_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockByHashVerbose(util.Uint256{}) }, }, { name: "getblockcount_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockCount() }, }, { name: "getblockhash_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockHash(1) }, }, { name: "getblockheader_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockHeader(util.Uint256{}) }, }, { name: "getblockheader_verbose_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockHeaderVerbose(util.Uint256{}) }, }, { name: "getblocksysfee_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetBlockSysFee(1) }, }, { name: "getcommittee_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetCommittee() }, }, { name: "getconnectioncount_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetConnectionCount() }, }, { name: "getcontractstate_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetContractStateByHash(util.Uint160{}) }, }, { name: "getnep11balances_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetNEP11Balances(util.Uint160{}) }, }, { name: "getnep17balances_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetNEP17Balances(util.Uint160{}) }, }, { name: "getnep11transfers_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetNEP11Transfers(util.Uint160{}, nil, nil, nil, nil) }, }, { name: "getnep17transfers_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetNEP17Transfers(util.Uint160{}, nil, nil, nil, nil) }, }, { name: "getpeers_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetPeers() }, }, { name: "getrawmempool_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetRawMemPool() }, }, { name: "getrawtransaction_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetRawTransaction(util.Uint256{}) }, }, { name: "getrawtransaction_verbose_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetRawTransactionVerbose(util.Uint256{}) }, }, { name: "getstoragebyhash_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetStorageByHash(util.Uint160{}, []byte{}) }, }, { name: "getstoragebyid_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetStorageByID(-1, []byte{}) }, }, { name: "gettransactionheight_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetTransactionHeight(util.Uint256{}) }, }, { name: "getunclaimedgas_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetUnclaimedGas("") }, }, { name: "getcandidates_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetCandidates() }, }, { name: "getvalidators_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.GetNextBlockValidators() }, }, { name: "invokefunction_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.InvokeFunction(util.Uint160{}, "", []smartcontract.Parameter{}, nil) }, }, { name: "invokescript_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.InvokeScript([]byte{}, nil) }, }, { name: "sendrawtransaction_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.SendRawTransaction(transaction.New([]byte{byte(opcode.PUSH1)}, 0)) }, }, { name: "submitblock_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return c.SubmitBlock(block.Block{ Header: block.Header{}, Transactions: nil, @@ -1722,7 +1783,7 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ }, { name: "validateaddress_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { + invoke: func(c *Client) (any, error) { return nil, c.ValidateAddress("") }, }, @@ -1741,7 +1802,7 @@ func TestRPCClients(t *testing.T) { }) t.Run("WSClient", func(t *testing.T) { testRPCClient(t, func(ctx context.Context, endpoint string, opts Options) (*Client, error) { - wsc, err := NewWS(ctx, httpURLtoWS(endpoint), opts) + wsc, err := NewWS(ctx, httpURLtoWS(endpoint), WSOptions{Options: opts}) require.NoError(t, err) wsc.getNextRequestID = getTestRequestID require.NoError(t, wsc.Init()) @@ -1951,7 +2012,7 @@ func TestGetNetwork(t *testing.T) { c.getNextRequestID = getTestRequestID // network was not initialised _, err = c.GetNetwork() - require.True(t, errors.Is(err, errNetworkNotInitialized)) + require.ErrorIs(t, err, errNetworkNotInitialized) require.Equal(t, false, c.cache.initDone) }) diff --git a/pkg/rpcclient/unwrap/unwrap_test.go b/pkg/rpcclient/unwrap/unwrap_test.go index b26876a58..9ec9f3cc0 100644 --- a/pkg/rpcclient/unwrap/unwrap_test.go +++ b/pkg/rpcclient/unwrap/unwrap_test.go @@ -15,66 +15,66 @@ import ( ) func TestStdErrors(t *testing.T) { - funcs := []func(r *result.Invoke, err error) (interface{}, error){ - func(r *result.Invoke, err error) (interface{}, error) { + funcs := []func(r *result.Invoke, err error) (any, error){ + func(r *result.Invoke, err error) (any, error) { return BigInt(r, err) }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { return Bool(r, err) }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { return Int64(r, err) }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { return LimitedInt64(r, err, 0, 1) }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { return Bytes(r, err) }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { return UTF8String(r, err) }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { return PrintableASCIIString(r, err) }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { return Uint160(r, err) }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { return Uint256(r, err) }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { return PublicKey(r, err) }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { _, _, err = SessionIterator(r, err) return nil, err }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { return Array(r, err) }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { return ArrayOfBools(r, err) }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { return ArrayOfBigInts(r, err) }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { return ArrayOfBytes(r, err) }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { return ArrayOfUTF8Strings(r, err) }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { return ArrayOfUint160(r, err) }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { return ArrayOfUint256(r, err) }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { return ArrayOfPublicKeys(r, err) }, - func(r *result.Invoke, err error) (interface{}, error) { + func(r *result.Invoke, err error) (any, error) { return Map(r, err) }, } diff --git a/pkg/rpcclient/wsclient.go b/pkg/rpcclient/wsclient.go index ec3ed4cd7..e4f11a0f0 100644 --- a/pkg/rpcclient/wsclient.go +++ b/pkg/rpcclient/wsclient.go @@ -38,7 +38,11 @@ import ( // will make WSClient wait for the channel reader to get the event and while // it waits every other messages (subscription-related or request replies) // will be blocked. This also means that subscription channel must be properly -// drained after unsubscription. +// drained after unsubscription. If CloseNotificationChannelIfFull option is on +// then the receiver channel will be closed immediately in case if a subsequent +// notification can't be sent to it, which means WSClient's operations are +// unblocking in this mode. No unsubscription is performed in this case, so it's +// still the user responsibility to unsubscribe. // // Any received subscription items (blocks/transactions/nofitications) are passed // via pointers for efficiency, but the actual structures MUST NOT be changed, as @@ -47,7 +51,9 @@ import ( // only sent once per channel. The receiver channel will be closed by the WSClient // immediately after MissedEvent is received from the server; no unsubscription // is performed in this case, so it's the user responsibility to unsubscribe. It -// will also be closed on disconnection from server. +// will also be closed on disconnection from server or on situation when it's +// impossible to send a subsequent notification to the subscriber's channel and +// CloseNotificationChannelIfFull option is on. type WSClient struct { Client // Notifications is a channel that is used to send events received from @@ -66,6 +72,7 @@ type WSClient struct { Notifications chan Notification ws *websocket.Conn + wsOpts WSOptions done chan struct{} requests chan *neorpc.Request shutdown chan struct{} @@ -80,22 +87,40 @@ type WSClient struct { // It must be accessed with subscriptionsLock taken. Its keys must be used to deliver // notifications, if channel is not in the receivers list and corresponding subscription // still exists, notification must not be sent. - receivers map[interface{}][]string + receivers map[any][]string respLock sync.RWMutex respChannels map[uint64]chan *neorpc.Response } +// WSOptions defines options for the web-socket RPC client. It contains a +// set of options for the underlying standard RPC client as far as +// WSClient-specific options. See Options documentation for more details. +type WSOptions struct { + Options + // CloseNotificationChannelIfFull allows WSClient to close a subscriber's + // receive channel in case if the channel isn't read properly and no more + // events can be pushed to it. This option, if set, allows to avoid WSClient + // blocking on a subsequent notification dispatch. However, if enabled, the + // corresponding subscription is kept even after receiver's channel closing, + // thus it's still the caller's duty to call Unsubscribe() for this + // subscription. + CloseNotificationChannelIfFull bool +} + // notificationReceiver is an interface aimed to provide WS subscriber functionality // for different types of subscriptions. type notificationReceiver interface { // Comparator provides notification filtering functionality. rpcevent.Comparator // Receiver returns notification receiver channel. - Receiver() interface{} + Receiver() any // TrySend checks whether notification passes receiver filter and sends it - // to the underlying channel if so. - TrySend(ntf Notification) bool + // to the underlying channel if so. It is performed under subscriptions lock + // taken. nonBlocking denotes whether the receiving operation shouldn't block + // the client's operation. It returns whether notification matches the filter + // and whether the receiver channel is overflown. + TrySend(ntf Notification, nonBlocking bool) (bool, bool) // Close closes underlying receiver channel. Close() } @@ -112,7 +137,7 @@ func (r *blockReceiver) EventID() neorpc.EventID { } // Filter implements neorpc.Comparator interface. -func (r *blockReceiver) Filter() interface{} { +func (r *blockReceiver) Filter() any { if r.filter == nil { return nil } @@ -120,17 +145,26 @@ func (r *blockReceiver) Filter() interface{} { } // Receiver implements notificationReceiver interface. -func (r *blockReceiver) Receiver() interface{} { +func (r *blockReceiver) Receiver() any { return r.ch } // TrySend implements notificationReceiver interface. -func (r *blockReceiver) TrySend(ntf Notification) bool { +func (r *blockReceiver) TrySend(ntf Notification, nonBlocking bool) (bool, bool) { if rpcevent.Matches(r, ntf) { - r.ch <- ntf.Value.(*block.Block) - return true + if nonBlocking { + select { + case r.ch <- ntf.Value.(*block.Block): + default: + return true, true + } + } else { + r.ch <- ntf.Value.(*block.Block) + } + + return true, false } - return false + return false, false } // Close implements notificationReceiver interface. @@ -150,7 +184,7 @@ func (r *txReceiver) EventID() neorpc.EventID { } // Filter implements neorpc.Comparator interface. -func (r *txReceiver) Filter() interface{} { +func (r *txReceiver) Filter() any { if r.filter == nil { return nil } @@ -158,17 +192,26 @@ func (r *txReceiver) Filter() interface{} { } // Receiver implements notificationReceiver interface. -func (r *txReceiver) Receiver() interface{} { +func (r *txReceiver) Receiver() any { return r.ch } // TrySend implements notificationReceiver interface. -func (r *txReceiver) TrySend(ntf Notification) bool { +func (r *txReceiver) TrySend(ntf Notification, nonBlocking bool) (bool, bool) { if rpcevent.Matches(r, ntf) { - r.ch <- ntf.Value.(*transaction.Transaction) - return true + if nonBlocking { + select { + case r.ch <- ntf.Value.(*transaction.Transaction): + default: + return true, true + } + } else { + r.ch <- ntf.Value.(*transaction.Transaction) + } + + return true, false } - return false + return false, false } // Close implements notificationReceiver interface. @@ -188,7 +231,7 @@ func (r *executionNotificationReceiver) EventID() neorpc.EventID { } // Filter implements neorpc.Comparator interface. -func (r *executionNotificationReceiver) Filter() interface{} { +func (r *executionNotificationReceiver) Filter() any { if r.filter == nil { return nil } @@ -196,17 +239,26 @@ func (r *executionNotificationReceiver) Filter() interface{} { } // Receiver implements notificationReceiver interface. -func (r *executionNotificationReceiver) Receiver() interface{} { +func (r *executionNotificationReceiver) Receiver() any { return r.ch } // TrySend implements notificationReceiver interface. -func (r *executionNotificationReceiver) TrySend(ntf Notification) bool { +func (r *executionNotificationReceiver) TrySend(ntf Notification, nonBlocking bool) (bool, bool) { if rpcevent.Matches(r, ntf) { - r.ch <- ntf.Value.(*state.ContainedNotificationEvent) - return true + if nonBlocking { + select { + case r.ch <- ntf.Value.(*state.ContainedNotificationEvent): + default: + return true, true + } + } else { + r.ch <- ntf.Value.(*state.ContainedNotificationEvent) + } + + return true, false } - return false + return false, false } // Close implements notificationReceiver interface. @@ -226,7 +278,7 @@ func (r *executionReceiver) EventID() neorpc.EventID { } // Filter implements neorpc.Comparator interface. -func (r *executionReceiver) Filter() interface{} { +func (r *executionReceiver) Filter() any { if r.filter == nil { return nil } @@ -234,17 +286,26 @@ func (r *executionReceiver) Filter() interface{} { } // Receiver implements notificationReceiver interface. -func (r *executionReceiver) Receiver() interface{} { +func (r *executionReceiver) Receiver() any { return r.ch } // TrySend implements notificationReceiver interface. -func (r *executionReceiver) TrySend(ntf Notification) bool { +func (r *executionReceiver) TrySend(ntf Notification, nonBlocking bool) (bool, bool) { if rpcevent.Matches(r, ntf) { - r.ch <- ntf.Value.(*state.AppExecResult) - return true + if nonBlocking { + select { + case r.ch <- ntf.Value.(*state.AppExecResult): + default: + return true, true + } + } else { + r.ch <- ntf.Value.(*state.AppExecResult) + } + + return true, false } - return false + return false, false } // Close implements notificationReceiver interface. @@ -264,7 +325,7 @@ func (r *notaryRequestReceiver) EventID() neorpc.EventID { } // Filter implements neorpc.Comparator interface. -func (r *notaryRequestReceiver) Filter() interface{} { +func (r *notaryRequestReceiver) Filter() any { if r.filter == nil { return nil } @@ -272,17 +333,26 @@ func (r *notaryRequestReceiver) Filter() interface{} { } // Receiver implements notificationReceiver interface. -func (r *notaryRequestReceiver) Receiver() interface{} { +func (r *notaryRequestReceiver) Receiver() any { return r.ch } // TrySend implements notificationReceiver interface. -func (r *notaryRequestReceiver) TrySend(ntf Notification) bool { +func (r *notaryRequestReceiver) TrySend(ntf Notification, nonBlocking bool) (bool, bool) { if rpcevent.Matches(r, ntf) { - r.ch <- ntf.Value.(*result.NotaryRequestEvent) - return true + if nonBlocking { + select { + case r.ch <- ntf.Value.(*result.NotaryRequestEvent): + default: + return true, true + } + } else { + r.ch <- ntf.Value.(*result.NotaryRequestEvent) + } + + return true, false } - return false + return false, false } // Close implements notificationReceiver interface. @@ -296,7 +366,7 @@ func (r *notaryRequestReceiver) Close() { // Deprecated: this receiver must be removed after outdated subscriptions API removal. type naiveReceiver struct { eventID neorpc.EventID - filter interface{} + filter any ch chan<- Notification } @@ -306,22 +376,31 @@ func (r *naiveReceiver) EventID() neorpc.EventID { } // Filter implements neorpc.Comparator interface. -func (r *naiveReceiver) Filter() interface{} { +func (r *naiveReceiver) Filter() any { return r.filter } // Receiver implements notificationReceiver interface. -func (r *naiveReceiver) Receiver() interface{} { +func (r *naiveReceiver) Receiver() any { return r.ch } // TrySend implements notificationReceiver interface. -func (r *naiveReceiver) TrySend(ntf Notification) bool { +func (r *naiveReceiver) TrySend(ntf Notification, nonBlocking bool) (bool, bool) { if rpcevent.Matches(r, ntf) { - r.ch <- ntf - return true + if nonBlocking { + select { + case r.ch <- ntf: + default: + return true, true + } + } else { + r.ch <- ntf + } + + return true, false } - return false + return false, false } // Close implements notificationReceiver interface. @@ -336,7 +415,7 @@ func (r *naiveReceiver) Close() { // *transaction.Transaction or *subscriptions.NotaryRequestEvent based on Type. type Notification struct { Type neorpc.EventID - Value interface{} + Value any } // EventID implements Container interface and returns notification ID. @@ -346,7 +425,7 @@ func (n Notification) EventID() neorpc.EventID { // EventPayload implements Container interface and returns notification // object. -func (n Notification) EventPayload() interface{} { +func (n Notification) EventPayload() any { return n.Value } @@ -375,6 +454,11 @@ const ( // ErrNilNotificationReceiver is returned when notification receiver channel is nil. var ErrNilNotificationReceiver = errors.New("nil notification receiver") +// ErrWSConnLost is a WSClient-specific error that will be returned for any +// requests after disconnection (including intentional ones via +// (*WSClient).Close). +var ErrWSConnLost = errors.New("connection lost") + // errConnClosedByUser is a WSClient error used iff the user calls (*WSClient).Close method by himself. var errConnClosedByUser = errors.New("connection closed by user") @@ -382,7 +466,7 @@ var errConnClosedByUser = errors.New("connection closed by user") // connection). You need to use websocket URL for it like `ws://1.2.3.4/ws`. // You should call Init method to initialize the network magic the client is // operating on. -func NewWS(ctx context.Context, endpoint string, opts Options) (*WSClient, error) { +func NewWS(ctx context.Context, endpoint string, opts WSOptions) (*WSClient, error) { dialer := websocket.Dialer{HandshakeTimeout: opts.DialTimeout} ws, resp, err := dialer.DialContext(ctx, endpoint, nil) if resp != nil && resp.Body != nil { // Can be non-nil even with error returned. @@ -405,16 +489,17 @@ func NewWS(ctx context.Context, endpoint string, opts Options) (*WSClient, error Notifications: make(chan Notification), ws: ws, + wsOpts: opts, shutdown: make(chan struct{}), done: make(chan struct{}), closeCalled: *atomic.NewBool(false), respChannels: make(map[uint64]chan *neorpc.Response), requests: make(chan *neorpc.Request), subscriptions: make(map[string]notificationReceiver), - receivers: make(map[interface{}][]string), + receivers: make(map[any][]string), } - err = initClient(ctx, &wsc.Client, endpoint, opts) + err = initClient(ctx, &wsc.Client, endpoint, opts.Options) if err != nil { return nil, err } @@ -429,7 +514,7 @@ func NewWS(ctx context.Context, endpoint string, opts Options) (*WSClient, error // Close closes connection to the remote side rendering this client instance // unusable. func (c *WSClient) Close() { - if c.closeCalled.CAS(false, true) { + if c.closeCalled.CompareAndSwap(false, true) { c.setCloseErr(errConnClosedByUser) // Closing shutdown channel sends a signal to wsWriter to break out of the // loop. In doing so it does ws.Close() closing the network connection @@ -542,16 +627,30 @@ readloop: c.respLock.Unlock() c.subscriptionsLock.Lock() for rcvrCh, ids := range c.receivers { - rcvr := c.subscriptions[ids[0]] + c.dropSubCh(rcvrCh, ids[0], true) + } + c.subscriptionsLock.Unlock() + c.Client.ctxCancel() +} + +// dropSubCh closes corresponding subscriber's channel and removes it from the +// receivers map. If the channel belongs to a naive subscriber then it will be +// closed manually without call to Close(). The channel is still being kept in +// the subscribers map as technically the server-side subscription still exists +// and the user is responsible for unsubscription. This method must be called +// under subscriptionsLock taken. It's the caller's duty to ensure dropSubCh +// will be called once per channel, otherwise panic will occur. +func (c *WSClient) dropSubCh(rcvrCh any, id string, ignoreCloseNotificationChannelIfFull bool) { + if ignoreCloseNotificationChannelIfFull || c.wsOpts.CloseNotificationChannelIfFull { + rcvr := c.subscriptions[id] _, ok := rcvr.(*naiveReceiver) - if !ok { // naiveReceiver uses c.Notifications that is about to be closed below. - c.subscriptions[ids[0]].Close() + if ok { // naiveReceiver uses c.Notifications that should be handled separately. + close(c.Notifications) + } else { + c.subscriptions[id].Close() } delete(c.receivers, rcvrCh) } - c.subscriptionsLock.Unlock() - close(c.Notifications) - c.Client.ctxCancel() } func (c *WSClient) wsWriter() { @@ -604,15 +703,20 @@ func (c *WSClient) notifySubscribers(ntf Notification) { c.subscriptionsLock.Unlock() return } - c.subscriptionsLock.RLock() - for _, ids := range c.receivers { + c.subscriptionsLock.Lock() + for rcvrCh, ids := range c.receivers { for _, id := range ids { - if c.subscriptions[id].TrySend(ntf) { + ok, dropCh := c.subscriptions[id].TrySend(ntf, c.wsOpts.CloseNotificationChannelIfFull) + if dropCh { + c.dropSubCh(rcvrCh, id, false) + break // strictly single drop per channel + } + if ok { break // strictly one notification per channel } } } - c.subscriptionsLock.RUnlock() + c.subscriptionsLock.Unlock() } func (c *WSClient) unregisterRespChannel(id uint64) { @@ -636,29 +740,29 @@ func (c *WSClient) makeWsRequest(r *neorpc.Request) (*neorpc.Response, error) { select { case <-c.done: c.respLock.Unlock() - return nil, errors.New("connection lost before registering response channel") + return nil, fmt.Errorf("%w: before registering response channel", ErrWSConnLost) default: c.respChannels[r.ID] = ch c.respLock.Unlock() } select { case <-c.done: - return nil, errors.New("connection lost before sending the request") + return nil, fmt.Errorf("%w: before sending the request", ErrWSConnLost) case c.requests <- r: } select { case <-c.done: - return nil, errors.New("connection lost while waiting for the response") + return nil, fmt.Errorf("%w: while waiting for the response", ErrWSConnLost) case resp, ok := <-ch: if !ok { - return nil, errors.New("connection lost while waiting for the response") + return nil, fmt.Errorf("%w: while waiting for the response", ErrWSConnLost) } c.unregisterRespChannel(r.ID) return resp, nil } } -func (c *WSClient) performSubscription(params []interface{}, rcvr notificationReceiver) (string, error) { +func (c *WSClient) performSubscription(params []any, rcvr notificationReceiver) (string, error) { var resp string if err := c.performRequest("subscribe", params, &resp); err != nil { @@ -680,12 +784,12 @@ func (c *WSClient) performSubscription(params []interface{}, rcvr notificationRe // // Deprecated: please, use ReceiveBlocks. This method will be removed in future versions. func (c *WSClient) SubscribeForNewBlocks(primary *int) (string, error) { - var flt interface{} + var flt any if primary != nil { var f = neorpc.BlockFilter{Primary: primary} flt = *f.Copy() } - params := []interface{}{"block_added"} + params := []any{"block_added"} if flt != nil { params = append(params, flt) } @@ -704,7 +808,7 @@ func (c *WSClient) ReceiveBlocks(flt *neorpc.BlockFilter, rcvr chan<- *block.Blo if rcvr == nil { return "", ErrNilNotificationReceiver } - params := []interface{}{"block_added"} + params := []any{"block_added"} if flt != nil { flt = flt.Copy() params = append(params, *flt) @@ -722,12 +826,12 @@ func (c *WSClient) ReceiveBlocks(flt *neorpc.BlockFilter, rcvr chan<- *block.Blo // // Deprecated: please, use ReceiveTransactions. This method will be removed in future versions. func (c *WSClient) SubscribeForNewTransactions(sender *util.Uint160, signer *util.Uint160) (string, error) { - var flt interface{} + var flt any if sender != nil || signer != nil { var f = neorpc.TxFilter{Sender: sender, Signer: signer} flt = *f.Copy() } - params := []interface{}{"transaction_added"} + params := []any{"transaction_added"} if flt != nil { params = append(params, flt) } @@ -746,7 +850,7 @@ func (c *WSClient) ReceiveTransactions(flt *neorpc.TxFilter, rcvr chan<- *transa if rcvr == nil { return "", ErrNilNotificationReceiver } - params := []interface{}{"transaction_added"} + params := []any{"transaction_added"} if flt != nil { flt = flt.Copy() params = append(params, *flt) @@ -765,12 +869,12 @@ func (c *WSClient) ReceiveTransactions(flt *neorpc.TxFilter, rcvr chan<- *transa // // Deprecated: please, use ReceiveExecutionNotifications. This method will be removed in future versions. func (c *WSClient) SubscribeForExecutionNotifications(contract *util.Uint160, name *string) (string, error) { - var flt interface{} + var flt any if contract != nil || name != nil { var f = neorpc.NotificationFilter{Contract: contract, Name: name} flt = *f.Copy() } - params := []interface{}{"notification_from_execution"} + params := []any{"notification_from_execution"} if flt != nil { params = append(params, flt) } @@ -789,7 +893,7 @@ func (c *WSClient) ReceiveExecutionNotifications(flt *neorpc.NotificationFilter, if rcvr == nil { return "", ErrNilNotificationReceiver } - params := []interface{}{"notification_from_execution"} + params := []any{"notification_from_execution"} if flt != nil { flt = flt.Copy() params = append(params, *flt) @@ -808,7 +912,7 @@ func (c *WSClient) ReceiveExecutionNotifications(flt *neorpc.NotificationFilter, // // Deprecated: please, use ReceiveExecutions. This method will be removed in future versions. func (c *WSClient) SubscribeForTransactionExecutions(state *string) (string, error) { - var flt interface{} + var flt any if state != nil { if *state != "HALT" && *state != "FAULT" { return "", errors.New("bad state parameter") @@ -816,7 +920,7 @@ func (c *WSClient) SubscribeForTransactionExecutions(state *string) (string, err var f = neorpc.ExecutionFilter{State: state} flt = *f.Copy() } - params := []interface{}{"transaction_executed"} + params := []any{"transaction_executed"} if flt != nil { params = append(params, flt) } @@ -836,7 +940,7 @@ func (c *WSClient) ReceiveExecutions(flt *neorpc.ExecutionFilter, rcvr chan<- *s if rcvr == nil { return "", ErrNilNotificationReceiver } - params := []interface{}{"transaction_executed"} + params := []any{"transaction_executed"} if flt != nil { if flt.State != nil { if *flt.State != "HALT" && *flt.State != "FAULT" { @@ -860,12 +964,12 @@ func (c *WSClient) ReceiveExecutions(flt *neorpc.ExecutionFilter, rcvr chan<- *s // // Deprecated: please, use ReceiveNotaryRequests. This method will be removed in future versions. func (c *WSClient) SubscribeForNotaryRequests(sender *util.Uint160, mainSigner *util.Uint160) (string, error) { - var flt interface{} + var flt any if sender != nil || mainSigner != nil { var f = neorpc.TxFilter{Sender: sender, Signer: mainSigner} flt = *f.Copy() } - params := []interface{}{"notary_request_event"} + params := []any{"notary_request_event"} if flt != nil { params = append(params, flt) } @@ -886,7 +990,7 @@ func (c *WSClient) ReceiveNotaryRequests(flt *neorpc.TxFilter, rcvr chan<- *resu if rcvr == nil { return "", ErrNilNotificationReceiver } - params := []interface{}{"notary_request_event"} + params := []any{"notary_request_event"} if flt != nil { flt = flt.Copy() params = append(params, *flt) @@ -932,10 +1036,10 @@ func (c *WSClient) UnsubscribeAll() error { err := c.performUnsubscription(id) if err != nil { errFmt := "failed to unsubscribe from feed %d: %v" - errArgs := []interface{}{err} + errArgs := []any{err} if resErr != nil { errFmt = "%w; " + errFmt - errArgs = append([]interface{}{resErr}, errArgs...) + errArgs = append([]any{resErr}, errArgs...) } resErr = fmt.Errorf(errFmt, errArgs...) } @@ -949,7 +1053,7 @@ func (c *WSClient) UnsubscribeAll() error { // may still receive WS notifications. func (c *WSClient) performUnsubscription(id string) error { var resp bool - if err := c.performRequest("unsubscribe", []interface{}{id}, &resp); err != nil { + if err := c.performRequest("unsubscribe", []any{id}, &resp); err != nil { return err } if !resp { diff --git a/pkg/rpcclient/wsclient_test.go b/pkg/rpcclient/wsclient_test.go index c3f30c894..46f542eb3 100644 --- a/pkg/rpcclient/wsclient_test.go +++ b/pkg/rpcclient/wsclient_test.go @@ -31,7 +31,11 @@ import ( func TestWSClientClose(t *testing.T) { srv := initTestServer(t, `{"jsonrpc": "2.0", "id": 1, "result": "55aaff00"}`) - wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{}) + wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), WSOptions{}) + require.NoError(t, err) + wsc.getNextRequestID = getTestRequestID + bCh := make(chan *block.Block) + _, err = wsc.ReceiveBlocks(nil, bCh) require.NoError(t, err) wsc.getNextRequestID = getTestRequestID bCh := make(chan *block.Block) @@ -70,7 +74,7 @@ func TestWSClientSubscription(t *testing.T) { for name, f := range cases { t.Run(name, func(t *testing.T) { srv := initTestServer(t, `{"jsonrpc": "2.0", "id": 1, "result": "55aaff00"}`) - wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{}) + wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), WSOptions{}) require.NoError(t, err) wsc.getNextRequestID = getTestRequestID require.NoError(t, wsc.Init()) @@ -84,7 +88,7 @@ func TestWSClientSubscription(t *testing.T) { for name, f := range cases { t.Run(name, func(t *testing.T) { srv := initTestServer(t, `{"jsonrpc": "2.0", "id": 1, "error":{"code":-32602,"message":"Invalid Params"}}`) - wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{}) + wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), WSOptions{}) require.NoError(t, err) wsc.getNextRequestID = getTestRequestID require.NoError(t, wsc.Init()) @@ -134,7 +138,7 @@ func TestWSClientUnsubscription(t *testing.T) { for name, rc := range cases { t.Run(name, func(t *testing.T) { srv := initTestServer(t, rc.response) - wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{}) + wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), WSOptions{}) require.NoError(t, err) wsc.getNextRequestID = getTestRequestID require.NoError(t, wsc.Init()) @@ -170,7 +174,7 @@ func TestWSClientEvents(t *testing.T) { return } })) - wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{}) + wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), WSOptions{}) require.NoError(t, err) wsc.getNextRequestID = getTestRequestID wsc.cacheLock.Lock() @@ -289,10 +293,92 @@ func TestWSClientEvents(t *testing.T) { require.False(t, ok) } +func TestWSClientNonBlockingEvents(t *testing.T) { + // Use buffered channel as a receiver to check it will be closed by WSClient + // after overflow if CloseNotificationChannelIfFull option is enabled. + const chCap = 3 + bCh := make(chan *block.Block, chCap) + + // Events from RPC server testchain. Require events len to be larger than chCap to reach + // subscriber's chanel overflow. + var events = []string{ + fmt.Sprintf(`{"jsonrpc":"2.0","method":"block_added","params":[%s]}`, b1Verbose), + fmt.Sprintf(`{"jsonrpc":"2.0","method":"block_added","params":[%s]}`, b1Verbose), + fmt.Sprintf(`{"jsonrpc":"2.0","method":"block_added","params":[%s]}`, b1Verbose), + fmt.Sprintf(`{"jsonrpc":"2.0","method":"block_added","params":[%s]}`, b1Verbose), + fmt.Sprintf(`{"jsonrpc":"2.0","method":"block_added","params":[%s]}`, b1Verbose), + } + require.True(t, chCap < len(events)) + + var blocksSent atomic.Bool + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + if req.URL.Path == "/ws" && req.Method == "GET" { + var upgrader = websocket.Upgrader{} + ws, err := upgrader.Upgrade(w, req, nil) + require.NoError(t, err) + for _, event := range events { + err = ws.SetWriteDeadline(time.Now().Add(2 * time.Second)) + require.NoError(t, err) + err = ws.WriteMessage(1, []byte(event)) + if err != nil { + break + } + } + blocksSent.Store(true) + ws.Close() + return + } + })) + wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), WSOptions{CloseNotificationChannelIfFull: true}) + require.NoError(t, err) + wsc.getNextRequestID = getTestRequestID + wsc.cacheLock.Lock() + wsc.cache.initDone = true // Our server mock is restricted, so perform initialisation manually. + wsc.cache.network = netmode.UnitTestNet + wsc.cacheLock.Unlock() + + // Our server mock is restricted, so perform subscriptions manually. + wsc.subscriptionsLock.Lock() + wsc.subscriptions["0"] = &blockReceiver{ch: bCh} + wsc.subscriptions["1"] = &blockReceiver{ch: bCh} + wsc.receivers[chan<- *block.Block(bCh)] = []string{"0", "1"} + wsc.subscriptionsLock.Unlock() + + // Check that events are sent to WSClient. + require.Eventually(t, func() bool { + return blocksSent.Load() + }, time.Second, 100*time.Millisecond) + + // Check that block receiver channel was removed from the receivers list due to overflow. + require.Eventually(t, func() bool { + wsc.subscriptionsLock.RLock() + defer wsc.subscriptionsLock.RUnlock() + return len(wsc.receivers) == 0 + }, 2*time.Second, 200*time.Millisecond) + + // Check that subscriptions are still there and waiting for the call to Unsubscribe() + // to be excluded from the subscriptions map. + wsc.subscriptionsLock.RLock() + require.True(t, len(wsc.subscriptions) == 2) + wsc.subscriptionsLock.RUnlock() + + // Check that receiver was closed after overflow. + for i := 0; i < chCap; i++ { + _, ok := <-bCh + require.True(t, ok) + } + select { + case _, ok := <-bCh: + require.False(t, ok) + default: + t.Fatal("channel wasn't closed by WSClient") + } +} + func TestWSExecutionVMStateCheck(t *testing.T) { // Will answer successfully if request slips through. srv := initTestServer(t, `{"jsonrpc": "2.0", "id": 1, "result": "55aaff00"}`) - wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{}) + wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), WSOptions{}) require.NoError(t, err) wsc.getNextRequestID = getTestRequestID require.NoError(t, wsc.Init()) @@ -527,7 +613,7 @@ func TestWSFilteredSubscriptions(t *testing.T) { ws.Close() } })) - wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{}) + wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), WSOptions{}) require.NoError(t, err) wsc.getNextRequestID = getTestRequestID wsc.cache.network = netmode.UnitTestNet @@ -541,14 +627,14 @@ func TestNewWS(t *testing.T) { srv := initTestServer(t, "") t.Run("good", func(t *testing.T) { - c, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{}) + c, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), WSOptions{}) require.NoError(t, err) c.getNextRequestID = getTestRequestID c.cache.network = netmode.UnitTestNet require.NoError(t, c.Init()) }) t.Run("bad URL", func(t *testing.T) { - _, err := NewWS(context.TODO(), strings.TrimPrefix(srv.URL, "http://"), Options{}) + _, err := NewWS(context.TODO(), strings.TrimPrefix(srv.URL, "http://"), WSOptions{}) require.Error(t, err) }) } @@ -605,7 +691,7 @@ func TestWSConcurrentAccess(t *testing.T) { })) t.Cleanup(srv.Close) - wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{}) + wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), WSOptions{}) require.NoError(t, err) batchCount := 100 completed := atomic.NewInt32(0) @@ -649,7 +735,7 @@ func TestWSConcurrentAccess(t *testing.T) { func TestWSDoubleClose(t *testing.T) { srv := initTestServer(t, "") - c, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{}) + c, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), WSOptions{}) require.NoError(t, err) require.NotPanics(t, func() { @@ -661,7 +747,7 @@ func TestWSDoubleClose(t *testing.T) { func TestWS_RequestAfterClose(t *testing.T) { srv := initTestServer(t, "") - c, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{}) + c, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), WSOptions{}) require.NoError(t, err) c.Close() @@ -670,13 +756,13 @@ func TestWS_RequestAfterClose(t *testing.T) { _, err = c.GetBlockCount() }) require.Error(t, err) - require.True(t, strings.Contains(err.Error(), "connection lost before registering response channel")) + require.ErrorIs(t, err, ErrWSConnLost) } func TestWSClient_ConnClosedError(t *testing.T) { t.Run("standard closing", func(t *testing.T) { srv := initTestServer(t, `{"jsonrpc": "2.0", "id": 1, "result": 123}`) - c, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{}) + c, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), WSOptions{}) require.NoError(t, err) // Check client is working. @@ -692,7 +778,7 @@ func TestWSClient_ConnClosedError(t *testing.T) { t.Run("malformed request", func(t *testing.T) { srv := initTestServer(t, "") - c, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{}) + c, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), WSOptions{}) require.NoError(t, err) defaultMaxBlockSize := 262144 diff --git a/pkg/services/helpers/rpcbroadcaster/broadcaster.go b/pkg/services/helpers/rpcbroadcaster/broadcaster.go index 2738f3b47..33464f0fd 100644 --- a/pkg/services/helpers/rpcbroadcaster/broadcaster.go +++ b/pkg/services/helpers/rpcbroadcaster/broadcaster.go @@ -10,7 +10,7 @@ import ( type RPCBroadcaster struct { Clients map[string]*RPCClient Log *zap.Logger - Responses chan []interface{} + Responses chan []any close chan struct{} finished chan struct{} @@ -24,7 +24,7 @@ func NewRPCBroadcaster(log *zap.Logger, sendTimeout time.Duration) *RPCBroadcast Log: log, close: make(chan struct{}), finished: make(chan struct{}), - Responses: make(chan []interface{}), + Responses: make(chan []any), sendTimeout: sendTimeout, } } @@ -65,7 +65,7 @@ drain: } // SendParams sends a request using all clients if the broadcaster is active. -func (r *RPCBroadcaster) SendParams(params []interface{}) { +func (r *RPCBroadcaster) SendParams(params []any) { select { case <-r.close: case r.Responses <- params: diff --git a/pkg/services/helpers/rpcbroadcaster/client.go b/pkg/services/helpers/rpcbroadcaster/client.go index 43c4e97c2..841336d43 100644 --- a/pkg/services/helpers/rpcbroadcaster/client.go +++ b/pkg/services/helpers/rpcbroadcaster/client.go @@ -14,17 +14,17 @@ type RPCClient struct { addr string close chan struct{} finished chan struct{} - responses chan []interface{} + responses chan []any log *zap.Logger sendTimeout time.Duration method SendMethod } // SendMethod represents an rpc method for sending data to other nodes. -type SendMethod func(*rpcclient.Client, []interface{}) error +type SendMethod func(*rpcclient.Client, []any) error // NewRPCClient returns a new rpc client for the provided address and method. -func (r *RPCBroadcaster) NewRPCClient(addr string, method SendMethod, timeout time.Duration, ch chan []interface{}) *RPCClient { +func (r *RPCBroadcaster) NewRPCClient(addr string, method SendMethod, timeout time.Duration, ch chan []any) *RPCClient { return &RPCClient{ addr: addr, close: r.close, diff --git a/pkg/services/metrics/metrics.go b/pkg/services/metrics/metrics.go index e814e1eff..883c1fabd 100644 --- a/pkg/services/metrics/metrics.go +++ b/pkg/services/metrics/metrics.go @@ -35,7 +35,7 @@ func NewService(name string, httpServers []*http.Server, cfg config.BasicService // Start runs http service with the exposed endpoint on the configured port. func (ms *Service) Start() error { if ms.config.Enabled { - if !ms.started.CAS(false, true) { + if !ms.started.CompareAndSwap(false, true) { ms.log.Info("service already started") return nil } @@ -66,7 +66,7 @@ func (ms *Service) ShutDown() { if !ms.config.Enabled { return } - if !ms.started.CAS(true, false) { + if !ms.started.CompareAndSwap(true, false) { return } for _, srv := range ms.http { diff --git a/pkg/services/notary/core_test.go b/pkg/services/notary/core_test.go index c383c39be..638da3887 100644 --- a/pkg/services/notary/core_test.go +++ b/pkg/services/notary/core_test.go @@ -47,7 +47,7 @@ func getTestNotary(t *testing.T, bc *core.Blockchain, walletPath, pass string, o Chain: bc, Log: zaptest.NewLogger(t), } - mp := mempool.New(10, 1, true) + mp := mempool.New(10, 1, true, nil) ntr, err := notary.NewNotary(cfg, netmode.UnitTestNet, mp, onTx) require.NoError(t, err) @@ -154,7 +154,7 @@ func TestNotary(t *testing.T) { mp1.StopSubscriptions() }) - notaryNodes := []interface{}{acc1.PublicKey().Bytes(), acc2.PrivateKey().PublicKey().Bytes()} + notaryNodes := []any{acc1.PublicKey().Bytes(), acc2.PrivateKey().PublicKey().Bytes()} designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", int64(noderoles.P2PNotary), notaryNodes) @@ -721,9 +721,9 @@ func TestNotary(t *testing.T) { requester1, _ := wallet.NewAccount() requester2, _ := wallet.NewAccount() amount := int64(100_0000_0000) - gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), bc.GetNotaryContractScriptHash(), amount, []interface{}{requester1.ScriptHash(), int64(bc.BlockHeight() + 50)}) + gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), bc.GetNotaryContractScriptHash(), amount, []any{requester1.ScriptHash(), int64(bc.BlockHeight() + 50)}) e.CheckGASBalance(t, notaryHash, big.NewInt(amount)) - gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), bc.GetNotaryContractScriptHash(), amount, []interface{}{requester2.ScriptHash(), int64(bc.BlockHeight() + 50)}) + gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), bc.GetNotaryContractScriptHash(), amount, []any{requester2.ScriptHash(), int64(bc.BlockHeight() + 50)}) e.CheckGASBalance(t, notaryHash, big.NewInt(2*amount)) // create request for 2 standard signatures => main tx should be completed after the second request is added to the pool diff --git a/pkg/services/notary/node_test.go b/pkg/services/notary/node_test.go index b1779cd6c..93c666dc6 100644 --- a/pkg/services/notary/node_test.go +++ b/pkg/services/notary/node_test.go @@ -21,7 +21,7 @@ func getTestNotary(t *testing.T, bc Ledger, walletPath, pass string) (*wallet.Ac Password: pass, }, } - mp := mempool.New(10, 1, true) + mp := mempool.New(10, 1, true, nil) cfg := Config{ MainCfg: mainCfg, Chain: bc, diff --git a/pkg/services/notary/notary.go b/pkg/services/notary/notary.go index e3b6bea96..d2a47e2ae 100644 --- a/pkg/services/notary/notary.go +++ b/pkg/services/notary/notary.go @@ -171,7 +171,7 @@ func (n *Notary) Name() string { // Start runs a Notary module in a separate goroutine. // The Notary only starts once, subsequent calls to Start are no-op. func (n *Notary) Start() { - if !n.started.CAS(false, true) { + if !n.started.CompareAndSwap(false, true) { return } n.Config.Log.Info("starting notary service") @@ -221,7 +221,7 @@ drainLoop: // to Shutdown on the same instance are no-op. The instance that was stopped can // not be started again by calling Start (use a new instance if needed). func (n *Notary) Shutdown() { - if !n.started.CAS(true, false) { + if !n.started.CompareAndSwap(true, false) { return } n.Config.Log.Info("stopping notary service") diff --git a/pkg/services/notary/notary_test.go b/pkg/services/notary/notary_test.go index d3d173e99..9df86c72f 100644 --- a/pkg/services/notary/notary_test.go +++ b/pkg/services/notary/notary_test.go @@ -27,21 +27,21 @@ func TestWallet(t *testing.T) { } t.Run("unexisting wallet", func(t *testing.T) { cfg.MainCfg.UnlockWallet.Path = "./testdata/does_not_exists.json" - _, err := NewNotary(cfg, netmode.UnitTestNet, mempool.New(1, 1, true), nil) + _, err := NewNotary(cfg, netmode.UnitTestNet, mempool.New(1, 1, true, nil), nil) require.Error(t, err) }) t.Run("bad password", func(t *testing.T) { cfg.MainCfg.UnlockWallet.Path = "./testdata/notary1.json" cfg.MainCfg.UnlockWallet.Password = "invalid" - _, err := NewNotary(cfg, netmode.UnitTestNet, mempool.New(1, 1, true), nil) + _, err := NewNotary(cfg, netmode.UnitTestNet, mempool.New(1, 1, true, nil), nil) require.Error(t, err) }) t.Run("good", func(t *testing.T) { cfg.MainCfg.UnlockWallet.Path = "./testdata/notary1.json" cfg.MainCfg.UnlockWallet.Password = "one" - _, err := NewNotary(cfg, netmode.UnitTestNet, mempool.New(1, 1, true), nil) + _, err := NewNotary(cfg, netmode.UnitTestNet, mempool.New(1, 1, true, nil), nil) require.NoError(t, err) }) } diff --git a/pkg/services/oracle/broadcaster/oracle.go b/pkg/services/oracle/broadcaster/oracle.go index 0655580b6..b5e5cd2aa 100644 --- a/pkg/services/oracle/broadcaster/oracle.go +++ b/pkg/services/oracle/broadcaster/oracle.go @@ -34,7 +34,7 @@ func New(cfg config.OracleConfiguration, log *zap.Logger) *OracleBroadcaster { } for i := range cfg.Nodes { r.Clients[cfg.Nodes[i]] = r.NewRPCClient(cfg.Nodes[i], (*rpcclient.Client).SubmitRawOracleResponse, - cfg.ResponseTimeout, make(chan []interface{}, defaultChanCapacity)) + cfg.ResponseTimeout, make(chan []any, defaultChanCapacity)) } return r } @@ -44,7 +44,7 @@ func (r *OracleBroadcaster) SendResponse(priv *keys.PrivateKey, resp *transactio pub := priv.PublicKey() data := GetMessage(pub.Bytes(), resp.ID, txSig) msgSig := priv.Sign(data) - params := []interface{}{ + params := []any{ base64.StdEncoding.EncodeToString(pub.Bytes()), resp.ID, base64.StdEncoding.EncodeToString(txSig), diff --git a/pkg/services/oracle/filter.go b/pkg/services/oracle/filter.go index 5c13030a8..2c92f3d0d 100644 --- a/pkg/services/oracle/filter.go +++ b/pkg/services/oracle/filter.go @@ -19,7 +19,7 @@ func filter(value []byte, path string) ([]byte, error) { d := json.NewDecoder(buf) d.UseOrderedObject() - var v interface{} + var v any if err := d.Decode(&v); err != nil { return nil, err } diff --git a/pkg/services/oracle/jsonpath/jsonpath.go b/pkg/services/oracle/jsonpath/jsonpath.go index 18b1294ac..7ccf41bf7 100644 --- a/pkg/services/oracle/jsonpath/jsonpath.go +++ b/pkg/services/oracle/jsonpath/jsonpath.go @@ -40,9 +40,9 @@ const ( // Get returns substructures of value selected by path. // The result is always non-nil unless the path is invalid. -func Get(path string, value interface{}) ([]interface{}, bool) { +func Get(path string, value any) ([]any, bool) { if path == "" { - return []interface{}{value}, true + return []any{value}, true } p := pathParser{ @@ -55,7 +55,7 @@ func Get(path string, value interface{}) ([]interface{}, bool) { return nil, false } - objs := []interface{}{value} + objs := []any{value} for p.i < len(p.s) { var ok bool @@ -72,7 +72,7 @@ func Get(path string, value interface{}) ([]interface{}, bool) { } if objs == nil { - objs = []interface{}{} + objs = []any{} } return objs, true } @@ -173,7 +173,7 @@ func (p *pathParser) parseNumber() (string, int, bool) { // processDot handles `.` operator. // It either descends 1 level down or performs recursive descent. -func (p *pathParser) processDot(objs []interface{}) ([]interface{}, bool) { +func (p *pathParser) processDot(objs []any) ([]any, bool) { typ, value := p.nextToken() switch typ { case pathAsterisk: @@ -189,16 +189,16 @@ func (p *pathParser) processDot(objs []interface{}) ([]interface{}, bool) { // descend descends 1 level down. // It flattens arrays and returns map values for maps. -func (p *pathParser) descend(objs []interface{}) ([]interface{}, bool) { +func (p *pathParser) descend(objs []any) ([]any, bool) { if p.depth <= 0 { return nil, false } p.depth-- - var values []interface{} + var values []any for i := range objs { switch obj := objs[i].(type) { - case []interface{}: + case []any: if maxObjects < len(values)+len(obj) { return nil, false } @@ -217,13 +217,13 @@ func (p *pathParser) descend(objs []interface{}) ([]interface{}, bool) { } // descendRecursive performs recursive descent. -func (p *pathParser) descendRecursive(objs []interface{}) ([]interface{}, bool) { +func (p *pathParser) descendRecursive(objs []any) ([]any, bool) { typ, val := p.nextToken() if typ != pathIdentifier { return nil, false } - var values []interface{} + var values []any for len(objs) > 0 { newObjs, _ := p.descendByIdentAux(objs, false, val) @@ -238,11 +238,11 @@ func (p *pathParser) descendRecursive(objs []interface{}) ([]interface{}, bool) } // descendByIdent performs map's field access by name. -func (p *pathParser) descendByIdent(objs []interface{}, names ...string) ([]interface{}, bool) { +func (p *pathParser) descendByIdent(objs []any, names ...string) ([]any, bool) { return p.descendByIdentAux(objs, true, names...) } -func (p *pathParser) descendByIdentAux(objs []interface{}, checkDepth bool, names ...string) ([]interface{}, bool) { +func (p *pathParser) descendByIdentAux(objs []any, checkDepth bool, names ...string) ([]any, bool) { if checkDepth { if p.depth <= 0 { return nil, false @@ -250,7 +250,7 @@ func (p *pathParser) descendByIdentAux(objs []interface{}, checkDepth bool, name p.depth-- } - var values []interface{} + var values []any for i := range objs { obj, ok := objs[i].(json.OrderedObject) if !ok { @@ -273,15 +273,15 @@ func (p *pathParser) descendByIdentAux(objs []interface{}, checkDepth bool, name } // descendByIndex performs array access by index. -func (p *pathParser) descendByIndex(objs []interface{}, indices ...int) ([]interface{}, bool) { +func (p *pathParser) descendByIndex(objs []any, indices ...int) ([]any, bool) { if p.depth <= 0 { return nil, false } p.depth-- - var values []interface{} + var values []any for i := range objs { - obj, ok := objs[i].([]interface{}) + obj, ok := objs[i].([]any) if !ok { continue } @@ -304,7 +304,7 @@ func (p *pathParser) descendByIndex(objs []interface{}, indices ...int) ([]inter // processLeftBracket processes index expressions which can be either // array/map access, array sub-slice or union of indices. -func (p *pathParser) processLeftBracket(objs []interface{}) ([]interface{}, bool) { +func (p *pathParser) processLeftBracket(objs []any) ([]any, bool) { typ, value := p.nextToken() switch typ { case pathAsterisk: @@ -360,7 +360,7 @@ func (p *pathParser) processLeftBracket(objs []interface{}) ([]interface{}, bool // processUnion processes union of multiple indices. // firstTyp is assumed to be either pathNumber or pathString. -func (p *pathParser) processUnion(objs []interface{}, firstTyp pathTokenType, firstVal string) ([]interface{}, bool) { +func (p *pathParser) processUnion(objs []any, firstTyp pathTokenType, firstVal string) ([]any, bool) { items := []string{firstVal} for { typ, val := p.nextToken() @@ -403,7 +403,7 @@ func (p *pathParser) processUnion(objs []interface{}, firstTyp pathTokenType, fi } // processSlice processes a slice with the specified start index. -func (p *pathParser) processSlice(objs []interface{}, start int) ([]interface{}, bool) { +func (p *pathParser) processSlice(objs []any, start int) ([]any, bool) { typ, val := p.nextToken() switch typ { case pathNumber: @@ -426,15 +426,15 @@ func (p *pathParser) processSlice(objs []interface{}, start int) ([]interface{}, } // descendByRange is similar to descend but skips maps and returns sub-slices for arrays. -func (p *pathParser) descendByRange(objs []interface{}, start, end int) ([]interface{}, bool) { +func (p *pathParser) descendByRange(objs []any, start, end int) ([]any, bool) { if p.depth <= 0 { return nil, false } p.depth-- - var values []interface{} + var values []any for i := range objs { - arr, ok := objs[i].([]interface{}) + arr, ok := objs[i].([]any) if !ok { continue } diff --git a/pkg/services/oracle/jsonpath/jsonpath_test.go b/pkg/services/oracle/jsonpath/jsonpath_test.go index eaeacbe2d..e537fb1db 100644 --- a/pkg/services/oracle/jsonpath/jsonpath_test.go +++ b/pkg/services/oracle/jsonpath/jsonpath_test.go @@ -16,8 +16,8 @@ type pathTestCase struct { result string } -func unmarshalGet(t *testing.T, js string, path string) ([]interface{}, bool) { - var v interface{} +func unmarshalGet(t *testing.T, js string, path string) ([]any, bool) { + var v any buf := bytes.NewBuffer([]byte(js)) d := json.NewDecoder(buf) d.UseOrderedObject() diff --git a/pkg/services/oracle/neofs/neofs.go b/pkg/services/oracle/neofs/neofs.go index e7d67fc51..d7a701534 100644 --- a/pkg/services/oracle/neofs/neofs.go +++ b/pkg/services/oracle/neofs/neofs.go @@ -1,19 +1,22 @@ package neofs import ( + "bytes" "context" "errors" "fmt" + "io" "net/url" "strconv" "strings" - "unicode/utf8" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neofs-sdk-go/client" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" + neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" "github.com/nspcc-dev/neofs-sdk-go/object" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" ) const ( @@ -41,37 +44,64 @@ var ( // Get returns a neofs object from the provided url. // URI scheme is "neofs://". // If Command is not provided, full object is requested. -func Get(ctx context.Context, priv *keys.PrivateKey, u *url.URL, addr string) ([]byte, error) { +func Get(ctx context.Context, priv *keys.PrivateKey, u *url.URL, addr string) (io.ReadCloser, error) { objectAddr, ps, err := parseNeoFSURL(u) if err != nil { return nil, err } - c, err := client.New( - client.WithDefaultPrivateKey(&priv.PrivateKey), - client.WithURIAddress(addr, nil), - client.WithNeoFSErrorParsing(), + var ( + prmi client.PrmInit + c *client.Client ) + prmi.SetDefaultSigner(neofsecdsa.Signer(priv.PrivateKey)) + c, err = client.New(prmi) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create client: %w", err) + } + + var ( + res = clientCloseWrapper{c: c} + prmd client.PrmDial + ) + prmd.SetServerURI(addr) + prmd.SetContext(ctx) + err = c.Dial(prmd) //nolint:contextcheck // contextcheck: Function `Dial->Balance->SendUnary->Init->setNeoFSAPIServer` should pass the context parameter + if err != nil { + return res, err } switch { case len(ps) == 0 || ps[0] == "": // Get request - return getPayload(ctx, c, objectAddr) + res.ReadCloser, err = getPayload(ctx, c, objectAddr) case ps[0] == rangeCmd: - return getRange(ctx, c, objectAddr, ps[1:]...) + res.ReadCloser, err = getRange(ctx, c, objectAddr, ps[1:]...) case ps[0] == headerCmd: - return getHeader(ctx, c, objectAddr) + res.ReadCloser, err = getHeader(ctx, c, objectAddr) case ps[0] == hashCmd: - return getHash(ctx, c, objectAddr, ps[1:]...) + res.ReadCloser, err = getHash(ctx, c, objectAddr, ps[1:]...) default: - return nil, ErrInvalidCommand + err = ErrInvalidCommand } + return res, err +} + +type clientCloseWrapper struct { + io.ReadCloser + c *client.Client +} + +func (w clientCloseWrapper) Close() error { + var res error + if w.ReadCloser != nil { + res = w.ReadCloser.Close() + } + w.c.Close() + return res } // parseNeoFSURL returns parsed neofs address. -func parseNeoFSURL(u *url.URL) (*object.Address, []string, error) { +func parseNeoFSURL(u *url.URL) (*oid.Address, []string, error) { if u.Scheme != URIScheme { return nil, nil, ErrInvalidScheme } @@ -81,31 +111,26 @@ func parseNeoFSURL(u *url.URL) (*object.Address, []string, error) { return nil, nil, ErrMissingObject } - containerID := cid.New() - if err := containerID.Parse(ps[0]); err != nil { + var containerID cid.ID + if err := containerID.DecodeString(ps[0]); err != nil { return nil, nil, fmt.Errorf("%w: %v", ErrInvalidContainer, err) //nolint:errorlint // errorlint: non-wrapping format verb for fmt.Errorf. Use `%w` to format errors } - objectID := object.NewID() - if err := objectID.Parse(ps[1]); err != nil { + var objectID oid.ID + if err := objectID.DecodeString(ps[1]); err != nil { return nil, nil, fmt.Errorf("%w: %v", ErrInvalidObject, err) //nolint:errorlint // errorlint: non-wrapping format verb for fmt.Errorf. Use `%w` to format errors } - - objectAddr := object.NewAddress() - objectAddr.SetContainerID(containerID) - objectAddr.SetObjectID(objectID) - return objectAddr, ps[2:], nil + var objAddr = new(oid.Address) + objAddr.SetContainer(containerID) + objAddr.SetObject(objectID) + return objAddr, ps[2:], nil } -func getPayload(ctx context.Context, c *client.Client, addr *object.Address) ([]byte, error) { - res, err := c.GetObject(ctx, new(client.GetObjectParams).WithAddress(addr)) - if err != nil { - return nil, err - } - return checkUTF8(res.Object().Payload()) +func getPayload(ctx context.Context, c *client.Client, addr *oid.Address) (io.ReadCloser, error) { + return c.ObjectGetInit(ctx, addr.Container(), addr.Object(), client.PrmObjectGet{}) } -func getRange(ctx context.Context, c *client.Client, addr *object.Address, ps ...string) ([]byte, error) { +func getRange(ctx context.Context, c *client.Client, addr *oid.Address, ps ...string) (io.ReadCloser, error) { if len(ps) == 0 { return nil, ErrInvalidRange } @@ -113,39 +138,57 @@ func getRange(ctx context.Context, c *client.Client, addr *object.Address, ps .. if err != nil { return nil, err } - res, err := c.ObjectPayloadRangeData(ctx, new(client.RangeDataParams).WithAddress(addr).WithRange(r)) + + return c.ObjectRangeInit(ctx, addr.Container(), addr.Object(), r.GetOffset(), r.GetLength(), client.PrmObjectRange{}) +} + +func getObjHeader(ctx context.Context, c *client.Client, addr *oid.Address) (*object.Object, error) { + res, err := c.ObjectHead(ctx, addr.Container(), addr.Object(), client.PrmObjectHead{}) if err != nil { return nil, err } - return checkUTF8(res.Data()) + var obj = object.New() + if !res.ReadHeader(obj) { + return nil, errors.New("missing header in the reply") + } + return obj, nil } -func getHeader(ctx context.Context, c *client.Client, addr *object.Address) ([]byte, error) { - res, err := c.HeadObject(ctx, new(client.ObjectHeaderParams).WithAddress(addr)) +func getHeader(ctx context.Context, c *client.Client, addr *oid.Address) (io.ReadCloser, error) { + obj, err := getObjHeader(ctx, c, addr) if err != nil { return nil, err } - return res.Object().MarshalHeaderJSON() + res, err := obj.MarshalHeaderJSON() + if err != nil { + return nil, err + } + return io.NopCloser(bytes.NewReader(res)), nil } -func getHash(ctx context.Context, c *client.Client, addr *object.Address, ps ...string) ([]byte, error) { +func getHash(ctx context.Context, c *client.Client, addr *oid.Address, ps ...string) (io.ReadCloser, error) { if len(ps) == 0 || ps[0] == "" { // hash of the full payload - res, err := c.HeadObject(ctx, new(client.ObjectHeaderParams).WithAddress(addr)) + obj, err := getObjHeader(ctx, c, addr) if err != nil { return nil, err } - return res.Object().PayloadChecksum().Sum(), nil + sum, flag := obj.PayloadChecksum() + if !flag { + return nil, errors.New("missing checksum in the reply") + } + return io.NopCloser(bytes.NewReader(sum.Value())), nil } r, err := parseRange(ps[0]) if err != nil { return nil, err } - res, err := c.HashObjectPayloadRanges(ctx, - new(client.RangeChecksumParams).WithAddress(addr).WithRangeList(r)) + var hashPrm client.PrmObjectHash + hashPrm.SetRangeList(r.GetOffset(), r.GetLength()) + + hashes, err := c.ObjectHash(ctx, addr.Container(), addr.Object(), hashPrm) if err != nil { return nil, err } - hashes := res.Hashes() if len(hashes) == 0 { return nil, fmt.Errorf("%w: empty response", ErrInvalidRange) } @@ -153,7 +196,11 @@ func getHash(ctx context.Context, c *client.Client, addr *object.Address, ps ... if err != nil { return nil, fmt.Errorf("decode Uint256: %w", err) } - return u256.MarshalJSON() + res, err := u256.MarshalJSON() + if err != nil { + return nil, err + } + return io.NopCloser(bytes.NewReader(res)), nil } func parseRange(s string) (*object.Range, error) { @@ -175,10 +222,3 @@ func parseRange(s string) (*object.Range, error) { r.SetLength(length) return r, nil } - -func checkUTF8(v []byte) ([]byte, error) { - if !utf8.Valid(v) { - return nil, errors.New("invalid UTF-8") - } - return v, nil -} diff --git a/pkg/services/oracle/neofs/neofs_test.go b/pkg/services/oracle/neofs/neofs_test.go index 1ad79920d..aa3308a32 100644 --- a/pkg/services/oracle/neofs/neofs_test.go +++ b/pkg/services/oracle/neofs/neofs_test.go @@ -1,12 +1,10 @@ package neofs import ( - "errors" "net/url" "testing" - cid "github.com/nspcc-dev/neofs-sdk-go/container/id" - "github.com/nspcc-dev/neofs-sdk-go/object" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" "github.com/stretchr/testify/require" ) @@ -37,17 +35,11 @@ func TestParseRange(t *testing.T) { func TestParseNeoFSURL(t *testing.T) { cStr := "C3swfg8MiMJ9bXbeFG6dWJTCoHp9hAEZkHezvbSwK1Cc" - containerID := cid.New() - require.NoError(t, containerID.Parse(cStr)) - oStr := "3nQH1L8u3eM9jt2mZCs6MyjzdjerdSzBkXCYYj4M4Znk" - oid := object.NewID() - require.NoError(t, oid.Parse(oStr)) + var objectAddr oid.Address + require.NoError(t, objectAddr.DecodeString(cStr+"/"+oStr)) validPrefix := "neofs:" + cStr + "/" + oStr - objectAddr := object.NewAddress() - objectAddr.SetContainerID(containerID) - objectAddr.SetObjectID(oid) testCases := []struct { url string @@ -68,11 +60,11 @@ func TestParseNeoFSURL(t *testing.T) { require.NoError(t, err) oa, ps, err := parseNeoFSURL(u) if tc.err != nil { - require.True(t, errors.Is(err, tc.err), "got: %#v", err) + require.ErrorIs(t, err, tc.err) return } require.NoError(t, err) - require.Equal(t, objectAddr, oa) + require.Equal(t, objectAddr, *oa) require.Equal(t, len(tc.params), len(ps)) if len(ps) != 0 { require.Equal(t, tc.params, ps) @@ -80,13 +72,3 @@ func TestParseNeoFSURL(t *testing.T) { }) } } - -func Test_checkUTF8(t *testing.T) { - _, err := checkUTF8([]byte{0xFF}) - require.Error(t, err) - - a := []byte{1, 2, 3} - b, err := checkUTF8(a) - require.NoError(t, err) - require.Equal(t, a, b) -} diff --git a/pkg/services/oracle/network_test.go b/pkg/services/oracle/network_test.go index de132508a..b3ba2a96a 100644 --- a/pkg/services/oracle/network_test.go +++ b/pkg/services/oracle/network_test.go @@ -1,7 +1,6 @@ package oracle import ( - "errors" "net" "strings" "testing" @@ -41,8 +40,7 @@ func TestDefaultClient_RestrictedRedirectErr(t *testing.T) { for _, c := range testCases { t.Run(c, func(t *testing.T) { _, err := cl.Get(c) //nolint:bodyclose // It errors out and it's a test. - require.Error(t, err) - require.True(t, errors.Is(err, ErrRestrictedRedirect), err) + require.ErrorIs(t, err, ErrRestrictedRedirect) require.True(t, strings.Contains(err.Error(), "IP is not global unicast"), err) }) } diff --git a/pkg/services/oracle/oracle_test.go b/pkg/services/oracle/oracle_test.go index 9cc43bcee..acc0f6509 100644 --- a/pkg/services/oracle/oracle_test.go +++ b/pkg/services/oracle/oracle_test.go @@ -41,7 +41,7 @@ var pathToInternalContracts = filepath.Join("..", "..", "..", "internal", "contr func putOracleRequest(t *testing.T, oracleValidatorInvoker *neotest.ContractInvoker, url string, filter *string, cb string, userData []byte, gas int64) util.Uint256 { - var filtItem interface{} + var filtItem any if filter != nil { filtItem = *filter } @@ -153,7 +153,7 @@ func TestOracle(t *testing.T) { oracleNodes := keys.PublicKeys{acc1.PublicKey(), acc2.PrivateKey().PublicKey()} // Must be set in native contract for tx verification. designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", - int64(roles.Oracle), []interface{}{oracleNodes[0].Bytes(), oracleNodes[1].Bytes()}) + int64(roles.Oracle), []any{oracleNodes[0].Bytes(), oracleNodes[1].Bytes()}) orc1.UpdateOracleNodes(oracleNodes.Copy()) orc2.UpdateOracleNodes(oracleNodes.Copy()) @@ -357,7 +357,7 @@ func TestOracleFull(t *testing.T) { }) designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", - int64(roles.Oracle), []interface{}{acc.PublicKey().Bytes()}) + int64(roles.Oracle), []any{acc.PublicKey().Bytes()}) cs := contracts.GetOracleContractState(t, pathToInternalContracts, validator.ScriptHash(), 0) e.DeployContract(t, &neotest.Contract{ @@ -391,7 +391,7 @@ func TestNotYetRunningOracle(t *testing.T) { t.Cleanup(bc.Close) designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", - int64(roles.Oracle), []interface{}{acc.PublicKey().Bytes()}) + int64(roles.Oracle), []any{acc.PublicKey().Bytes()}) var req state.OracleRequest var reqs = make(map[uint64]*state.OracleRequest) diff --git a/pkg/services/oracle/request.go b/pkg/services/oracle/request.go index fddc9d771..6267f55d0 100644 --- a/pkg/services/oracle/request.go +++ b/pkg/services/oracle/request.go @@ -146,16 +146,7 @@ func (o *Oracle) processRequest(priv *keys.PrivateKey, req request) error { break } - resp.Result, err = readResponse(r.Body, transaction.MaxOracleResultSize) - if err != nil { - if errors.Is(err, ErrResponseTooLarge) { - resp.Code = transaction.ResponseTooLarge - } else { - resp.Code = transaction.Error - } - o.Log.Warn("failed to read data for oracle request", zap.String("url", req.Req.URL), zap.Error(err)) - break - } + resp.Result, resp.Code = o.readResponse(r.Body, req.Req.URL) case http.StatusForbidden: resp.Code = transaction.Forbidden case http.StatusNotFound: @@ -169,11 +160,17 @@ func (o *Oracle) processRequest(priv *keys.PrivateKey, req request) error { ctx, cancel := context.WithTimeout(context.Background(), o.MainCfg.NeoFS.Timeout) defer cancel() index := (int(req.ID) + incTx.attempts) % len(o.MainCfg.NeoFS.Nodes) - resp.Result, err = neofs.Get(ctx, priv, u, o.MainCfg.NeoFS.Nodes[index]) + rc, err := neofs.Get(ctx, priv, u, o.MainCfg.NeoFS.Nodes[index]) if err != nil { - o.Log.Warn("oracle request failed", zap.String("url", req.Req.URL), zap.Error(err)) resp.Code = transaction.Error + o.Log.Warn("failed to perform oracle request", zap.String("url", req.Req.URL), zap.Error(err)) + if rc != nil { + rc.Close() // intentionally skip the closing error, make it unified with Oracle `https` protocol. + } + break } + resp.Result, resp.Code = o.readResponse(rc, req.Req.URL) + rc.Close() // intentionally skip the closing error, make it unified with Oracle `https` protocol. default: resp.Code = transaction.ProtocolNotSupported o.Log.Warn("unknown oracle request scheme", zap.String("url", req.Req.URL)) diff --git a/pkg/services/oracle/response.go b/pkg/services/oracle/response.go index f2d3667c0..06062941e 100644 --- a/pkg/services/oracle/response.go +++ b/pkg/services/oracle/response.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" gio "io" + "unicode/utf8" "github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/interop" @@ -67,16 +68,37 @@ func (o *Oracle) AddResponse(pub *keys.PublicKey, reqID uint64, txSig []byte) { // ErrResponseTooLarge is returned when a response exceeds the max allowed size. var ErrResponseTooLarge = errors.New("too big response") -func readResponse(rc gio.ReadCloser, limit int) ([]byte, error) { +func (o *Oracle) readResponse(rc gio.Reader, url string) ([]byte, transaction.OracleResponseCode) { + const limit = transaction.MaxOracleResultSize buf := make([]byte, limit+1) n, err := gio.ReadFull(rc, buf) if errors.Is(err, gio.ErrUnexpectedEOF) && n <= limit { - return buf[:n], nil + res, err := checkUTF8(buf[:n]) + return o.handleResponseError(res, err, url) } if err == nil || n > limit { - return nil, ErrResponseTooLarge + return o.handleResponseError(nil, ErrResponseTooLarge, url) } - return nil, err + + return o.handleResponseError(nil, err, url) +} + +func (o *Oracle) handleResponseError(data []byte, err error, url string) ([]byte, transaction.OracleResponseCode) { + if err != nil { + o.Log.Warn("failed to read data for oracle request", zap.String("url", url), zap.Error(err)) + if errors.Is(err, ErrResponseTooLarge) { + return nil, transaction.ResponseTooLarge + } + return nil, transaction.Error + } + return data, transaction.Success +} + +func checkUTF8(v []byte) ([]byte, error) { + if !utf8.Valid(v) { + return nil, errors.New("invalid UTF-8") + } + return v, nil } // CreateResponseTx creates an unsigned oracle response transaction. diff --git a/pkg/services/rpcsrv/client_test.go b/pkg/services/rpcsrv/client_test.go index d74b83afb..73f36154f 100644 --- a/pkg/services/rpcsrv/client_test.go +++ b/pkg/services/rpcsrv/client_test.go @@ -24,12 +24,15 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/fee" + "github.com/nspcc-dev/neo-go/pkg/core/native" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" "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/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/neorpc" "github.com/nspcc-dev/neo-go/pkg/neorpc/result" @@ -258,11 +261,10 @@ func TestClientManagementContract(t *testing.T) { cs2, err := c.GetContractStateByHash(gas.Hash) require.NoError(t, err) require.Equal(t, cs2, cs1) - /* C# compat cs1, err = manReader.GetContractByID(-6) require.NoError(t, err) require.Equal(t, cs2, cs1) - */ + ret, err := manReader.HasMethod(gas.Hash, "transfer", 4) require.NoError(t, err) require.True(t, ret) @@ -369,9 +371,9 @@ func TestClientNEOContract(t *testing.T) { require.Equal(t, int64(1000_0000_0000), regP) acc0 := testchain.PrivateKey(0).PublicKey().GetScriptHash() - uncl, err := neoR.UnclaimedGas(acc0, 100) + uncl, err := neoR.UnclaimedGas(acc0, chain.BlockHeight()+1) require.NoError(t, err) - require.Equal(t, big.NewInt(48000), uncl) + require.Equal(t, big.NewInt(10000), uncl) accState, err := neoR.GetAccountState(acc0) require.NoError(t, err) @@ -1765,6 +1767,33 @@ func TestClient_GetNotaryServiceFeePerKey(t *testing.T) { require.Equal(t, defaultNotaryServiceFeePerKey, actual) } +func TestClient_States(t *testing.T) { + chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t) + defer chain.Close() + defer rpcSrv.Shutdown() + + c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) + require.NoError(t, err) + require.NoError(t, c.Init()) + + stateheight, err := c.GetStateHeight() + assert.NoError(t, err) + assert.Equal(t, chain.BlockHeight(), stateheight.Local) + + stateroot, err := c.GetStateRootByHeight(stateheight.Local) + assert.NoError(t, err) + + t.Run("proof", func(t *testing.T) { + policy, err := chain.GetNativeContractScriptHash(nativenames.Policy) + assert.NoError(t, err) + proof, err := c.GetProof(stateroot.Root, policy, []byte{19}) // storagePrice key in policy contract + assert.NoError(t, err) + value, err := c.VerifyProof(stateroot.Root, proof) + assert.NoError(t, err) + assert.Equal(t, big.NewInt(native.DefaultStoragePrice), bigint.FromBytes(value)) + }) +} + func TestClientOracle(t *testing.T) { chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t) defer chain.Close() @@ -2044,7 +2073,7 @@ func mkSubsClient(t *testing.T, rpcSrv *Server, httpSrv *httptest.Server, local icl, err = rpcclient.NewInternal(context.Background(), rpcSrv.RegisterLocal) } else { url := "ws" + strings.TrimPrefix(httpSrv.URL, "http") + "/ws" - c, err = rpcclient.NewWS(context.Background(), url, rpcclient.Options{}) + c, err = rpcclient.NewWS(context.Background(), url, rpcclient.WSOptions{}) } require.NoError(t, err) if local { @@ -2210,7 +2239,7 @@ func TestWSClientHandshakeError(t *testing.T) { defer rpcSrv.Shutdown() url := "ws" + strings.TrimPrefix(httpSrv.URL, "http") + "/ws" - _, err := rpcclient.NewWS(context.Background(), url, rpcclient.Options{}) + _, err := rpcclient.NewWS(context.Background(), url, rpcclient.WSOptions{}) require.ErrorContains(t, err, "websocket users limit reached") } @@ -2269,7 +2298,7 @@ func testSubClientWaitWithMissedEvent(t *testing.T, local bool) { overNotification := neorpc.Notification{ JSONRPC: neorpc.JSONRPCVersion, Event: neorpc.MissedEventID, - Payload: make([]interface{}, 0), + Payload: make([]any, 0), } overEvent, err := json.Marshal(overNotification) require.NoError(t, err) diff --git a/pkg/services/rpcsrv/error.go b/pkg/services/rpcsrv/error.go index 819b40b8a..89fb64538 100644 --- a/pkg/services/rpcsrv/error.go +++ b/pkg/services/rpcsrv/error.go @@ -17,7 +17,7 @@ type abstractResult interface { type abstract struct { neorpc.Header Error *neorpc.Error `json:"error,omitempty"` - Result interface{} `json:"result,omitempty"` + Result any `json:"result,omitempty"` } // RunForErrors implements abstractResult interface. diff --git a/pkg/services/rpcsrv/params/param.go b/pkg/services/rpcsrv/params/param.go index db392d58d..e0a56db3f 100644 --- a/pkg/services/rpcsrv/params/param.go +++ b/pkg/services/rpcsrv/params/param.go @@ -25,7 +25,7 @@ type ( // the client. Param struct { json.RawMessage - cache interface{} + cache any } // FuncParam represents a function argument parameter used in the @@ -193,7 +193,7 @@ func (p *Param) GetIntStrict() (int, error) { return 0, errNotAnInt } -func (p *Param) fillIntCache() (interface{}, error) { +func (p *Param) fillIntCache() (any, error) { if p.cache != nil { return p.cache, nil } diff --git a/pkg/services/rpcsrv/params/params.go b/pkg/services/rpcsrv/params/params.go index 23e5fe189..805625856 100644 --- a/pkg/services/rpcsrv/params/params.go +++ b/pkg/services/rpcsrv/params/params.go @@ -12,7 +12,7 @@ type ( // FromAny allows to create Params for a slice of abstract values (by // JSON-marshaling them). -func FromAny(arr []interface{}) (Params, error) { +func FromAny(arr []any) (Params, error) { var res Params for i := range arr { b, err := json.Marshal(arr[i]) diff --git a/pkg/services/rpcsrv/params/params_test.go b/pkg/services/rpcsrv/params/params_test.go index 8fd066951..415203182 100644 --- a/pkg/services/rpcsrv/params/params_test.go +++ b/pkg/services/rpcsrv/params/params_test.go @@ -10,7 +10,7 @@ import ( func TestParamsFromAny(t *testing.T) { str := "jajaja" - ps, err := FromAny([]interface{}{str, smartcontract.Parameter{Type: smartcontract.StringType, Value: str}}) + ps, err := FromAny([]any{str, smartcontract.Parameter{Type: smartcontract.StringType, Value: str}}) require.NoError(t, err) require.Equal(t, 2, len(ps)) @@ -26,6 +26,6 @@ func TestParamsFromAny(t *testing.T) { require.Equal(t, resStr, str) // Invalid item. - _, err = FromAny([]interface{}{smartcontract.Parameter{Type: smartcontract.IntegerType, Value: str}}) + _, err = FromAny([]any{smartcontract.Parameter{Type: smartcontract.IntegerType, Value: str}}) require.Error(t, err) } diff --git a/pkg/services/rpcsrv/prometheus.go b/pkg/services/rpcsrv/prometheus.go index b90f1ae94..4e24a4e9d 100644 --- a/pkg/services/rpcsrv/prometheus.go +++ b/pkg/services/rpcsrv/prometheus.go @@ -1,7 +1,6 @@ package rpcsrv import ( - "fmt" "strings" "time" @@ -10,8 +9,7 @@ import ( // Metrics used in monitoring service. var ( - rpcCounter = map[string]prometheus.Counter{} - rpcTimes = map[string]prometheus.Histogram{} + rpcTimes = map[string]prometheus.Histogram{} ) func addReqTimeMetric(name string, t time.Duration) { @@ -19,22 +17,9 @@ func addReqTimeMetric(name string, t time.Duration) { if ok { hist.Observe(t.Seconds()) } - ctr, ok := rpcCounter[name] - if ok { - ctr.Inc() - } } func regCounter(call string) { - ctr := prometheus.NewCounter( - prometheus.CounterOpts{ - Help: fmt.Sprintf("Number of calls to %s rpc endpoint (obsolete, to be removed)", call), - Name: fmt.Sprintf("%s_called", call), - Namespace: "neogo", - }, - ) - prometheus.MustRegister(ctr) - rpcCounter[call] = ctr rpcTimes[call] = prometheus.NewHistogram( prometheus.HistogramOpts{ Help: "RPC " + call + " call handling time", diff --git a/pkg/services/rpcsrv/server.go b/pkg/services/rpcsrv/server.go index 3602cad85..5fcbf656c 100644 --- a/pkg/services/rpcsrv/server.go +++ b/pkg/services/rpcsrv/server.go @@ -196,7 +196,7 @@ const ( defaultSessionPoolSize = 20 ) -var rpcHandlers = map[string]func(*Server, params.Params) (interface{}, *neorpc.Error){ +var rpcHandlers = map[string]func(*Server, params.Params) (any, *neorpc.Error){ "calculatenetworkfee": (*Server).calculateNetworkFee, "findstates": (*Server).findStates, "getapplicationlog": (*Server).getApplicationLog, @@ -245,7 +245,7 @@ var rpcHandlers = map[string]func(*Server, params.Params) (interface{}, *neorpc. "verifyproof": (*Server).verifyProof, } -var rpcWsHandlers = map[string]func(*Server, params.Params, *subscriber) (interface{}, *neorpc.Error){ +var rpcWsHandlers = map[string]func(*Server, params.Params, *subscriber) (any, *neorpc.Error){ "subscribe": (*Server).subscribe, "unsubscribe": (*Server).unsubscribe, } @@ -342,7 +342,7 @@ func (s *Server) Start() { s.log.Info("RPC server is not enabled") return } - if !s.started.CAS(false, true) { + if !s.started.CompareAndSwap(false, true) { s.log.Info("RPC server already started") return } @@ -397,7 +397,7 @@ func (s *Server) Start() { // that was stopped can not be started again by calling Start (use a new // instance if needed). func (s *Server) Shutdown() { - if !s.started.CAS(true, false) { + if !s.started.CompareAndSwap(true, false) { return } // Signal to websocket writer routines and handleSubEvents. @@ -538,7 +538,7 @@ func (s *Server) handleRequest(req *params.Request, sub *subscriber) abstractRes // handleInternal is an experimental interface to handle client requests directly. func (s *Server) handleInternal(req *neorpc.Request, sub *subscriber) (*neorpc.Response, error) { var ( - res interface{} + res any rpcRes = &neorpc.Response{ HeaderAndError: neorpc.HeaderAndError{ Header: neorpc.Header{ @@ -581,7 +581,7 @@ func (s *Server) handleInternal(req *neorpc.Request, sub *subscriber) (*neorpc.R } func (s *Server) handleIn(req *params.In, sub *subscriber) abstract { - var res interface{} + var res any var resErr *neorpc.Error if req.JSONRPC != neorpc.JSONRPCVersion { return s.packResponse(req, nil, neorpc.NewInvalidParamsError(fmt.Sprintf("problem parsing JSON: invalid version, expected 2.0 got '%s'", req.JSONRPC))) @@ -725,19 +725,19 @@ func (s *Server) dropSubscriber(subscr *subscriber) { s.subsCounterLock.Unlock() } -func (s *Server) getBestBlockHash(_ params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getBestBlockHash(_ params.Params) (any, *neorpc.Error) { return "0x" + s.chain.CurrentBlockHash().StringLE(), nil } -func (s *Server) getBlockCount(_ params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getBlockCount(_ params.Params) (any, *neorpc.Error) { return s.chain.BlockHeight() + 1, nil } -func (s *Server) getBlockHeaderCount(_ params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getBlockHeaderCount(_ params.Params) (any, *neorpc.Error) { return s.chain.HeaderHeight() + 1, nil } -func (s *Server) getConnectionCount(_ params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getConnectionCount(_ params.Params) (any, *neorpc.Error) { return s.coreServer.PeerCount(), nil } @@ -773,7 +773,7 @@ func (s *Server) fillBlockMetadata(obj io.Serializable, h *block.Header) result. return res } -func (s *Server) getBlock(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getBlock(reqParams params.Params) (any, *neorpc.Error) { param := reqParams.Value(0) hash, respErr := s.blockHashFromParam(param) if respErr != nil { @@ -797,7 +797,7 @@ func (s *Server) getBlock(reqParams params.Params) (interface{}, *neorpc.Error) return writer.Bytes(), nil } -func (s *Server) getBlockHash(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getBlockHash(reqParams params.Params) (any, *neorpc.Error) { num, err := s.blockHeightFromParam(reqParams.Value(0)) if err != nil { return nil, neorpc.ErrInvalidParams @@ -806,7 +806,7 @@ func (s *Server) getBlockHash(reqParams params.Params) (interface{}, *neorpc.Err return s.chain.GetHeaderHash(num), nil } -func (s *Server) getVersion(_ params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getVersion(_ params.Params) (any, *neorpc.Error) { port, err := s.coreServer.Port(nil) // any port will suite if err != nil { return nil, neorpc.NewInternalServerError(fmt.Sprintf("cannot fetch tcp port: %s", err)) @@ -836,7 +836,7 @@ func (s *Server) getVersion(_ params.Params) (interface{}, *neorpc.Error) { }, nil } -func (s *Server) getPeers(_ params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getPeers(_ params.Params) (any, *neorpc.Error) { peers := result.NewGetPeers() peers.AddUnconnected(s.coreServer.UnconnectedPeers()) peers.AddConnected(s.coreServer.ConnectedPeers()) @@ -844,7 +844,7 @@ func (s *Server) getPeers(_ params.Params) (interface{}, *neorpc.Error) { return peers, nil } -func (s *Server) getRawMempool(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getRawMempool(reqParams params.Params) (any, *neorpc.Error) { verbose, _ := reqParams.Value(0).GetBoolean() mp := s.chain.GetMemPool() hashList := make([]util.Uint256, 0) @@ -861,7 +861,7 @@ func (s *Server) getRawMempool(reqParams params.Params) (interface{}, *neorpc.Er }, nil } -func (s *Server) validateAddress(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) validateAddress(reqParams params.Params) (any, *neorpc.Error) { param, err := reqParams.Value(0).GetString() if err != nil { return nil, neorpc.ErrInvalidParams @@ -874,7 +874,7 @@ func (s *Server) validateAddress(reqParams params.Params) (interface{}, *neorpc. } // calculateNetworkFee calculates network fee for the transaction. -func (s *Server) calculateNetworkFee(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) calculateNetworkFee(reqParams params.Params) (any, *neorpc.Error) { if len(reqParams) < 1 { return 0, neorpc.ErrInvalidParams } @@ -942,7 +942,7 @@ func (s *Server) calculateNetworkFee(reqParams params.Params) (interface{}, *neo } // getApplicationLog returns the contract log based on the specified txid or blockid. -func (s *Server) getApplicationLog(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getApplicationLog(reqParams params.Params) (any, *neorpc.Error) { hash, err := reqParams.Value(0).GetUint256() if err != nil { return nil, neorpc.ErrInvalidParams @@ -968,7 +968,7 @@ func (s *Server) getApplicationLog(reqParams params.Params) (interface{}, *neorp } func (s *Server) getNEP11Tokens(h util.Uint160, acc util.Uint160, bw *io.BufBinWriter) ([]stackitem.Item, string, int, error) { - items, finalize, err := s.invokeReadOnlyMulti(bw, h, []string{"tokensOf", "symbol", "decimals"}, [][]interface{}{{acc}, nil, nil}) + items, finalize, err := s.invokeReadOnlyMulti(bw, h, []string{"tokensOf", "symbol", "decimals"}, [][]any{{acc}, nil, nil}) if err != nil { return nil, "", 0, err } @@ -991,7 +991,7 @@ func (s *Server) getNEP11Tokens(h util.Uint160, acc util.Uint160, bw *io.BufBinW return vals, sym, int(dec.Int64()), nil } -func (s *Server) getNEP11Balances(ps params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getNEP11Balances(ps params.Params) (any, *neorpc.Error) { u, err := ps.Value(0).GetUint160FromAddressOrHex() if err != nil { return nil, neorpc.ErrInvalidParams @@ -1080,7 +1080,7 @@ func (s *Server) invokeNEP11Properties(h util.Uint160, id []byte, bw *io.BufBinW return item.Value().([]stackitem.MapElement), nil } -func (s *Server) getNEP11Properties(ps params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getNEP11Properties(ps params.Params) (any, *neorpc.Error) { asset, err := ps.Value(0).GetUint160FromAddressOrHex() if err != nil { return nil, neorpc.ErrInvalidParams @@ -1093,13 +1093,13 @@ func (s *Server) getNEP11Properties(ps params.Params) (interface{}, *neorpc.Erro if err != nil { return nil, neorpc.NewRPCError("Failed to get NEP-11 properties", err.Error()) } - res := make(map[string]interface{}) + res := make(map[string]any) for _, kv := range props { key, err := kv.Key.TryBytes() if err != nil { continue } - var val interface{} + var val any if result.KnownNEP11Properties[string(key)] || kv.Value.Type() != stackitem.AnyT { v, err := kv.Value.TryBytes() if err != nil { @@ -1116,7 +1116,7 @@ func (s *Server) getNEP11Properties(ps params.Params) (interface{}, *neorpc.Erro return res, nil } -func (s *Server) getNEP17Balances(ps params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getNEP17Balances(ps params.Params) (any, *neorpc.Error) { u, err := ps.Value(0).GetUint160FromAddressOrHex() if err != nil { return nil, neorpc.ErrInvalidParams @@ -1164,15 +1164,15 @@ func (s *Server) getNEP17Balances(ps params.Params) (interface{}, *neorpc.Error) return bs, nil } -func (s *Server) invokeReadOnly(bw *io.BufBinWriter, h util.Uint160, method string, params ...interface{}) (stackitem.Item, func(), error) { - r, f, err := s.invokeReadOnlyMulti(bw, h, []string{method}, [][]interface{}{params}) +func (s *Server) invokeReadOnly(bw *io.BufBinWriter, h util.Uint160, method string, params ...any) (stackitem.Item, func(), error) { + r, f, err := s.invokeReadOnlyMulti(bw, h, []string{method}, [][]any{params}) if err != nil { return nil, nil, err } return r[0], f, nil } -func (s *Server) invokeReadOnlyMulti(bw *io.BufBinWriter, h util.Uint160, methods []string, params [][]interface{}) ([]stackitem.Item, func(), error) { +func (s *Server) invokeReadOnlyMulti(bw *io.BufBinWriter, h util.Uint160, methods []string, params [][]any) ([]stackitem.Item, func(), error) { if bw == nil { bw = io.NewBufBinWriter() } else { @@ -1209,7 +1209,7 @@ func (s *Server) invokeReadOnlyMulti(bw *io.BufBinWriter, h util.Uint160, method } func (s *Server) getNEP17TokenBalance(h util.Uint160, acc util.Uint160, bw *io.BufBinWriter) (*big.Int, string, int, error) { - items, finalize, err := s.invokeReadOnlyMulti(bw, h, []string{"balanceOf", "symbol", "decimals"}, [][]interface{}{{acc}, nil, nil}) + items, finalize, err := s.invokeReadOnlyMulti(bw, h, []string{"balanceOf", "symbol", "decimals"}, [][]any{{acc}, nil, nil}) if err != nil { return nil, "", 0, err } @@ -1295,15 +1295,15 @@ func getTimestampsAndLimit(ps params.Params, index int) (uint64, uint64, int, in return start, end, limit, page, nil } -func (s *Server) getNEP11Transfers(ps params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getNEP11Transfers(ps params.Params) (any, *neorpc.Error) { return s.getTokenTransfers(ps, true) } -func (s *Server) getNEP17Transfers(ps params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getNEP17Transfers(ps params.Params) (any, *neorpc.Error) { return s.getTokenTransfers(ps, false) } -func (s *Server) getTokenTransfers(ps params.Params, isNEP11 bool) (interface{}, *neorpc.Error) { +func (s *Server) getTokenTransfers(ps params.Params, isNEP11 bool) (any, *neorpc.Error) { u, err := ps.Value(0).GetUint160FromAddressOrHex() if err != nil { return nil, neorpc.ErrInvalidParams @@ -1316,8 +1316,8 @@ func (s *Server) getTokenTransfers(ps params.Params, isNEP11 bool) (interface{}, bs := &tokenTransfers{ Address: address.Uint160ToString(u), - Received: []interface{}{}, - Sent: []interface{}{}, + Received: []any{}, + Sent: []any{}, } cache := make(map[int32]util.Uint160) var resCount, frameCount int @@ -1482,7 +1482,7 @@ func makeStorageKey(id int32, key []byte) []byte { var errKeepOnlyLatestState = errors.New("'KeepOnlyLatestState' setting is enabled") -func (s *Server) getProof(ps params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getProof(ps params.Params) (any, *neorpc.Error) { if s.chain.GetConfig().Ledger.KeepOnlyLatestState { return nil, neorpc.NewInvalidRequestError(fmt.Sprintf("'getproof' is not supported: %s", errKeepOnlyLatestState)) } @@ -1513,7 +1513,7 @@ func (s *Server) getProof(ps params.Params) (interface{}, *neorpc.Error) { }, nil } -func (s *Server) verifyProof(ps params.Params) (interface{}, *neorpc.Error) { +func (s *Server) verifyProof(ps params.Params) (any, *neorpc.Error) { if s.chain.GetConfig().Ledger.KeepOnlyLatestState { return nil, neorpc.NewInvalidRequestError(fmt.Sprintf("'verifyproof' is not supported: %s", errKeepOnlyLatestState)) } @@ -1537,7 +1537,7 @@ func (s *Server) verifyProof(ps params.Params) (interface{}, *neorpc.Error) { return vp, nil } -func (s *Server) getState(ps params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getState(ps params.Params) (any, *neorpc.Error) { root, err := ps.Value(0).GetUint256() if err != nil { return nil, neorpc.WrapErrorWithData(neorpc.ErrInvalidParams, "invalid stateroot") @@ -1571,7 +1571,7 @@ func (s *Server) getState(ps params.Params) (interface{}, *neorpc.Error) { return res, nil } -func (s *Server) findStates(ps params.Params) (interface{}, *neorpc.Error) { +func (s *Server) findStates(ps params.Params) (any, *neorpc.Error) { root, err := ps.Value(0).GetUint256() if err != nil { return nil, neorpc.WrapErrorWithData(neorpc.ErrInvalidParams, "invalid stateroot") @@ -1679,7 +1679,7 @@ func (s *Server) getHistoricalContractState(root util.Uint256, csHash util.Uint1 return contract, nil } -func (s *Server) getStateHeight(_ params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getStateHeight(_ params.Params) (any, *neorpc.Error) { var height = s.chain.BlockHeight() var stateHeight = s.chain.GetStateModule().CurrentValidatedHeight() if s.chain.GetConfig().StateRootInHeader { @@ -1691,7 +1691,7 @@ func (s *Server) getStateHeight(_ params.Params) (interface{}, *neorpc.Error) { }, nil } -func (s *Server) getStateRoot(ps params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getStateRoot(ps params.Params) (any, *neorpc.Error) { p := ps.Value(0) if p == nil { return nil, neorpc.NewInvalidParamsError("missing stateroot identifier") @@ -1717,7 +1717,7 @@ func (s *Server) getStateRoot(ps params.Params) (interface{}, *neorpc.Error) { return rt, nil } -func (s *Server) getStorage(ps params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getStorage(ps params.Params) (any, *neorpc.Error) { id, rErr := s.contractIDFromParam(ps.Value(0)) if rErr == neorpc.ErrUnknown { return nil, nil @@ -1739,7 +1739,7 @@ func (s *Server) getStorage(ps params.Params) (interface{}, *neorpc.Error) { return []byte(item), nil } -func (s *Server) getrawtransaction(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getrawtransaction(reqParams params.Params) (any, *neorpc.Error) { txHash, err := reqParams.Value(0).GetUint256() if err != nil { return nil, neorpc.ErrInvalidParams @@ -1778,7 +1778,7 @@ func (s *Server) getrawtransaction(reqParams params.Params) (interface{}, *neorp return tx.Bytes(), nil } -func (s *Server) getTransactionHeight(ps params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getTransactionHeight(ps params.Params) (any, *neorpc.Error) { h, err := ps.Value(0).GetUint256() if err != nil { return nil, neorpc.ErrInvalidParams @@ -1794,7 +1794,7 @@ func (s *Server) getTransactionHeight(ps params.Params) (interface{}, *neorpc.Er // getContractState returns contract state (contract information, according to the contract script hash, // contract id or native contract name). -func (s *Server) getContractState(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getContractState(reqParams params.Params) (any, *neorpc.Error) { scriptHash, err := s.contractScriptHashFromParam(reqParams.Value(0)) if err != nil { return nil, err @@ -1806,12 +1806,12 @@ func (s *Server) getContractState(reqParams params.Params) (interface{}, *neorpc return cs, nil } -func (s *Server) getNativeContracts(_ params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getNativeContracts(_ params.Params) (any, *neorpc.Error) { return s.chain.GetNatives(), nil } // getBlockSysFee returns the system fees of the block, based on the specified index. -func (s *Server) getBlockSysFee(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getBlockSysFee(reqParams params.Params) (any, *neorpc.Error) { num, err := s.blockHeightFromParam(reqParams.Value(0)) if err != nil { return 0, neorpc.NewRPCError("Invalid height", "invalid block identifier") @@ -1832,7 +1832,7 @@ func (s *Server) getBlockSysFee(reqParams params.Params) (interface{}, *neorpc.E } // getBlockHeader returns the corresponding block header information according to the specified script hash. -func (s *Server) getBlockHeader(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getBlockHeader(reqParams params.Params) (any, *neorpc.Error) { param := reqParams.Value(0) hash, respErr := s.blockHashFromParam(param) if respErr != nil { @@ -1862,7 +1862,7 @@ func (s *Server) getBlockHeader(reqParams params.Params) (interface{}, *neorpc.E } // getUnclaimedGas returns unclaimed GAS amount of the specified address. -func (s *Server) getUnclaimedGas(ps params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getUnclaimedGas(ps params.Params) (any, *neorpc.Error) { u, err := ps.Value(0).GetUint160FromAddressOrHex() if err != nil { return nil, neorpc.ErrInvalidParams @@ -1885,7 +1885,7 @@ func (s *Server) getUnclaimedGas(ps params.Params) (interface{}, *neorpc.Error) } // getCandidates returns the current list of candidates with their active/inactive voting status. -func (s *Server) getCandidates(_ params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getCandidates(_ params.Params) (any, *neorpc.Error) { var validators keys.PublicKeys validators, err := s.chain.GetNextBlockValidators() @@ -1908,7 +1908,7 @@ func (s *Server) getCandidates(_ params.Params) (interface{}, *neorpc.Error) { } // getNextBlockValidators returns validators for the next block with voting status. -func (s *Server) getNextBlockValidators(_ params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getNextBlockValidators(_ params.Params) (any, *neorpc.Error) { var validators keys.PublicKeys validators, err := s.chain.GetNextBlockValidators() @@ -1933,7 +1933,7 @@ func (s *Server) getNextBlockValidators(_ params.Params) (interface{}, *neorpc.E } // getCommittee returns the current list of NEO committee members. -func (s *Server) getCommittee(_ params.Params) (interface{}, *neorpc.Error) { +func (s *Server) getCommittee(_ params.Params) (any, *neorpc.Error) { keys, err := s.chain.GetCommittee() if err != nil { return nil, neorpc.NewInternalServerError(fmt.Sprintf("can't get committee members: %s", err)) @@ -1942,7 +1942,7 @@ func (s *Server) getCommittee(_ params.Params) (interface{}, *neorpc.Error) { } // invokeFunction implements the `invokeFunction` RPC call. -func (s *Server) invokeFunction(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) invokeFunction(reqParams params.Params) (any, *neorpc.Error) { tx, verbose, respErr := s.getInvokeFunctionParams(reqParams) if respErr != nil { return nil, respErr @@ -1951,7 +1951,7 @@ func (s *Server) invokeFunction(reqParams params.Params) (interface{}, *neorpc.E } // invokeFunctionHistoric implements the `invokeFunctionHistoric` RPC call. -func (s *Server) invokeFunctionHistoric(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) invokeFunctionHistoric(reqParams params.Params) (any, *neorpc.Error) { nextH, respErr := s.getHistoricParams(reqParams) if respErr != nil { return nil, respErr @@ -2009,7 +2009,7 @@ func (s *Server) getInvokeFunctionParams(reqParams params.Params) (*transaction. } // invokescript implements the `invokescript` RPC call. -func (s *Server) invokescript(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) invokescript(reqParams params.Params) (any, *neorpc.Error) { tx, verbose, respErr := s.getInvokeScriptParams(reqParams) if respErr != nil { return nil, respErr @@ -2018,7 +2018,7 @@ func (s *Server) invokescript(reqParams params.Params) (interface{}, *neorpc.Err } // invokescripthistoric implements the `invokescripthistoric` RPC call. -func (s *Server) invokescripthistoric(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) invokescripthistoric(reqParams params.Params) (any, *neorpc.Error) { nextH, respErr := s.getHistoricParams(reqParams) if respErr != nil { return nil, respErr @@ -2063,7 +2063,7 @@ func (s *Server) getInvokeScriptParams(reqParams params.Params) (*transaction.Tr } // invokeContractVerify implements the `invokecontractverify` RPC call. -func (s *Server) invokeContractVerify(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) invokeContractVerify(reqParams params.Params) (any, *neorpc.Error) { scriptHash, tx, invocationScript, respErr := s.getInvokeContractVerifyParams(reqParams) if respErr != nil { return nil, respErr @@ -2072,7 +2072,7 @@ func (s *Server) invokeContractVerify(reqParams params.Params) (interface{}, *ne } // invokeContractVerifyHistoric implements the `invokecontractverifyhistoric` RPC call. -func (s *Server) invokeContractVerifyHistoric(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) invokeContractVerifyHistoric(reqParams params.Params) (any, *neorpc.Error) { nextH, respErr := s.getHistoricParams(reqParams) if respErr != nil { return nil, respErr @@ -2316,7 +2316,7 @@ func (s *Server) registerOrDumpIterator(item stackitem.Item) (stackitem.Item, uu return stackitem.NewInterop(resIterator), iterID } -func (s *Server) traverseIterator(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) traverseIterator(reqParams params.Params) (any, *neorpc.Error) { if !s.config.SessionEnabled { return nil, neorpc.NewInvalidRequestError("sessions are disabled") } @@ -2373,7 +2373,7 @@ func (s *Server) traverseIterator(reqParams params.Params) (interface{}, *neorpc return result, nil } -func (s *Server) terminateSession(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) terminateSession(reqParams params.Params) (any, *neorpc.Error) { if !s.config.SessionEnabled { return nil, neorpc.NewInvalidRequestError("sessions are disabled") } @@ -2400,7 +2400,7 @@ func (s *Server) terminateSession(reqParams params.Params) (interface{}, *neorpc } // submitBlock broadcasts a raw block over the Neo network. -func (s *Server) submitBlock(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) submitBlock(reqParams params.Params) (any, *neorpc.Error) { blockBytes, err := reqParams.Value(0).GetBytesBase64() if err != nil { return nil, neorpc.NewInvalidParamsError(fmt.Sprintf("missing parameter or not a base64: %s", err)) @@ -2426,7 +2426,7 @@ func (s *Server) submitBlock(reqParams params.Params) (interface{}, *neorpc.Erro } // submitNotaryRequest broadcasts P2PNotaryRequest over the Neo network. -func (s *Server) submitNotaryRequest(ps params.Params) (interface{}, *neorpc.Error) { +func (s *Server) submitNotaryRequest(ps params.Params) (any, *neorpc.Error) { if !s.chain.P2PSigExtensionsEnabled() { return nil, neorpc.NewRPCError("P2PSignatureExtensions are disabled", "") } @@ -2443,7 +2443,7 @@ func (s *Server) submitNotaryRequest(ps params.Params) (interface{}, *neorpc.Err } // getRelayResult returns successful relay result or an error. -func getRelayResult(err error, hash util.Uint256) (interface{}, *neorpc.Error) { +func getRelayResult(err error, hash util.Uint256) (any, *neorpc.Error) { switch { case err == nil: return result.RelayResult{ @@ -2460,7 +2460,7 @@ func getRelayResult(err error, hash util.Uint256) (interface{}, *neorpc.Error) { } } -func (s *Server) submitOracleResponse(ps params.Params) (interface{}, *neorpc.Error) { +func (s *Server) submitOracleResponse(ps params.Params) (any, *neorpc.Error) { oracle := s.oracle.Load().(*OracleHandler) if oracle == nil || *oracle == nil { return nil, neorpc.NewRPCError("Oracle is not enabled", "") @@ -2493,7 +2493,7 @@ func (s *Server) submitOracleResponse(ps params.Params) (interface{}, *neorpc.Er return json.RawMessage([]byte("{}")), nil } -func (s *Server) sendrawtransaction(reqParams params.Params) (interface{}, *neorpc.Error) { +func (s *Server) sendrawtransaction(reqParams params.Params) (any, *neorpc.Error) { if len(reqParams) < 1 { return nil, neorpc.NewInvalidParamsError("not enough parameters") } @@ -2509,7 +2509,7 @@ func (s *Server) sendrawtransaction(reqParams params.Params) (interface{}, *neor } // subscribe handles subscription requests from websocket clients. -func (s *Server) subscribe(reqParams params.Params, sub *subscriber) (interface{}, *neorpc.Error) { +func (s *Server) subscribe(reqParams params.Params, sub *subscriber) (any, *neorpc.Error) { streamName, err := reqParams.Value(0).GetString() if err != nil { return nil, neorpc.ErrInvalidParams @@ -2522,7 +2522,7 @@ func (s *Server) subscribe(reqParams params.Params, sub *subscriber) (interface{ return nil, neorpc.WrapErrorWithData(neorpc.ErrInvalidParams, "P2PSigExtensions are disabled") } // Optional filter. - var filter interface{} + var filter any if p := reqParams.Value(1); p != nil { param := *p jd := json.NewDecoder(bytes.NewReader(param.RawMessage)) @@ -2615,7 +2615,7 @@ func (s *Server) subscribeToChannel(event neorpc.EventID) { } // unsubscribe handles unsubscription requests from websocket clients. -func (s *Server) unsubscribe(reqParams params.Params, sub *subscriber) (interface{}, *neorpc.Error) { +func (s *Server) unsubscribe(reqParams params.Params, sub *subscriber) (any, *neorpc.Error) { id, err := reqParams.Value(0).GetInt() if err != nil || id < 0 { return nil, neorpc.ErrInvalidParams @@ -2675,7 +2675,7 @@ func (s *Server) handleSubEvents() { var overflowEvent = neorpc.Notification{ JSONRPC: neorpc.JSONRPCVersion, Event: neorpc.MissedEventID, - Payload: make([]interface{}, 0), + Payload: make([]any, 0), } b, err := json.Marshal(overflowEvent) if err != nil { @@ -2691,7 +2691,7 @@ chloop: for { var resp = neorpc.Notification{ JSONRPC: neorpc.JSONRPCVersion, - Payload: make([]interface{}, 1), + Payload: make([]any, 1), } var msg *websocket.PreparedMessage select { @@ -2806,7 +2806,7 @@ func (s *Server) blockHeightFromParam(param *params.Param) (uint32, *neorpc.Erro return uint32(num), nil } -func (s *Server) packResponse(r *params.In, result interface{}, respErr *neorpc.Error) abstract { +func (s *Server) packResponse(r *params.In, result any, respErr *neorpc.Error) abstract { resp := abstract{ Header: neorpc.Header{ JSONRPC: r.JSONRPC, @@ -2890,7 +2890,7 @@ func (s *Server) writeHTTPServerResponse(r *params.Request, w http.ResponseWrite // validateAddress verifies that the address is a correct Neo address // see https://docs.neo.org/en-us/node/cli/2.9.4/api/validateaddress.html -func validateAddress(addr interface{}) bool { +func validateAddress(addr any) bool { if addr, ok := addr.(string); ok { _, err := address.StringToUint160(addr) return err == nil diff --git a/pkg/services/rpcsrv/server_test.go b/pkg/services/rpcsrv/server_test.go index 352a454c0..e59055b8a 100644 --- a/pkg/services/rpcsrv/server_test.go +++ b/pkg/services/rpcsrv/server_test.go @@ -63,8 +63,8 @@ type rpcTestCase struct { name string params string fail bool - result func(e *executor) interface{} - check func(t *testing.T, e *executor, result interface{}) + result func(e *executor) any + check func(t *testing.T, e *executor, result any) } const genesisBlockHash = "0f8fb4e17d2ab9f3097af75ca7fd16064160fb8043db94909e00dd4e257b9dc4" @@ -83,7 +83,7 @@ const ( faultedTxHashLE = "82279bfe9bada282ca0f8cb8e0bb124b921af36f00c69a518320322c6f4fef60" faultedTxBlock uint32 = 23 invokescriptContractAVM = "VwIADBQBDAMOBQYMDQIODw0DDgcJAAAAAErZMCQE2zBwaEH4J+yMqiYEEUAMFA0PAwIJAAIBAwcDBAUCAQAOBgwJStkwJATbMHFpQfgn7IyqJgQSQBNA" - block20StateRootLE = "811a287a0235cfb3c7def100ae7029335a10e8b90d0ca59c460955c0546a0414" + block20StateRootLE = "ae445869283f8d7e0debc3f455014c73cde21b9802db99e80248da9f393bce14" ) var ( @@ -98,8 +98,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: `["` + deploymentTxHash + `"]`, - result: func(e *executor) interface{} { return &result.ApplicationLog{} }, - check: func(t *testing.T, e *executor, acc interface{}) { + result: func(e *executor) any { return &result.ApplicationLog{} }, + check: func(t *testing.T, e *executor, acc any) { res, ok := acc.(*result.ApplicationLog) require.True(t, ok) expectedTxHash, err := util.Uint256DecodeStringLE(deploymentTxHash) @@ -113,8 +113,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, genesis block", params: `["` + genesisBlockHash + `"]`, - result: func(e *executor) interface{} { return &result.ApplicationLog{} }, - check: func(t *testing.T, e *executor, acc interface{}) { + result: func(e *executor) any { return &result.ApplicationLog{} }, + check: func(t *testing.T, e *executor, acc any) { res, ok := acc.(*result.ApplicationLog) require.True(t, ok) assert.Equal(t, genesisBlockHash, res.Container.StringLE()) @@ -127,8 +127,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, genesis block, postPersist", params: `["` + genesisBlockHash + `", "PostPersist"]`, - result: func(e *executor) interface{} { return &result.ApplicationLog{} }, - check: func(t *testing.T, e *executor, acc interface{}) { + result: func(e *executor) any { return &result.ApplicationLog{} }, + check: func(t *testing.T, e *executor, acc any) { res, ok := acc.(*result.ApplicationLog) require.True(t, ok) assert.Equal(t, genesisBlockHash, res.Container.StringLE()) @@ -140,8 +140,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, genesis block, onPersist", params: `["` + genesisBlockHash + `", "OnPersist"]`, - result: func(e *executor) interface{} { return &result.ApplicationLog{} }, - check: func(t *testing.T, e *executor, acc interface{}) { + result: func(e *executor) any { return &result.ApplicationLog{} }, + check: func(t *testing.T, e *executor, acc any) { res, ok := acc.(*result.ApplicationLog) require.True(t, ok) assert.Equal(t, genesisBlockHash, res.Container.StringLE()) @@ -175,8 +175,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, by hash", params: fmt.Sprintf(`["%s"]`, testContractHash), - result: func(e *executor) interface{} { return &state.Contract{} }, - check: func(t *testing.T, e *executor, cs interface{}) { + result: func(e *executor) any { return &state.Contract{} }, + check: func(t *testing.T, e *executor, cs any) { res, ok := cs.(*state.Contract) require.True(t, ok) assert.Equal(t, testContractHash, res.Hash.StringLE()) @@ -185,8 +185,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, by id", params: `[1]`, - result: func(e *executor) interface{} { return &state.Contract{} }, - check: func(t *testing.T, e *executor, cs interface{}) { + result: func(e *executor) any { return &state.Contract{} }, + check: func(t *testing.T, e *executor, cs any) { res, ok := cs.(*state.Contract) require.True(t, ok) assert.Equal(t, int32(1), res.ID) @@ -195,8 +195,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, native by id", params: `[-3]`, - result: func(e *executor) interface{} { return &state.Contract{} }, - check: func(t *testing.T, e *executor, cs interface{}) { + result: func(e *executor) any { return &state.Contract{} }, + check: func(t *testing.T, e *executor, cs any) { res, ok := cs.(*state.Contract) require.True(t, ok) assert.Equal(t, int32(-3), res.ID) @@ -205,8 +205,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, native by name", params: `["PolicyContract"]`, - result: func(e *executor) interface{} { return &state.Contract{} }, - check: func(t *testing.T, e *executor, cs interface{}) { + result: func(e *executor) any { return &state.Contract{} }, + check: func(t *testing.T, e *executor, cs any) { res, ok := cs.(*state.Contract) require.True(t, ok) assert.Equal(t, int32(-7), res.ID) @@ -252,13 +252,13 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: `["` + testchain.PrivateKeyByID(0).GetScriptHash().StringLE() + `"]`, - result: func(e *executor) interface{} { return &result.NEP11Balances{} }, + result: func(e *executor) any { return &result.NEP11Balances{} }, check: checkNep11Balances, }, { name: "positive_address", params: `["` + address.Uint160ToString(testchain.PrivateKeyByID(0).GetScriptHash()) + `"]`, - result: func(e *executor) interface{} { return &result.NEP11Balances{} }, + result: func(e *executor) any { return &result.NEP11Balances{} }, check: checkNep11Balances, }, }, @@ -286,8 +286,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: `["` + nnsContractHash + `", "6e656f2e636f6d"]`, - result: func(e *executor) interface{} { - return &map[string]interface{}{ + result: func(e *executor) any { + return &map[string]any{ "name": "neo.com", "expiration": "lhbLRl0B", "admin": nil, @@ -319,7 +319,7 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: `["` + testchain.PrivateKeyByID(0).Address() + `", 0]`, - result: func(e *executor) interface{} { return &result.NEP11Transfers{} }, + result: func(e *executor) any { return &result.NEP11Transfers{} }, check: checkNep11Transfers, }, }, @@ -337,13 +337,13 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: `["` + testchain.PrivateKeyByID(0).GetScriptHash().StringLE() + `"]`, - result: func(e *executor) interface{} { return &result.NEP17Balances{} }, + result: func(e *executor) any { return &result.NEP17Balances{} }, check: checkNep17Balances, }, { name: "positive_address", params: `["` + address.Uint160ToString(testchain.PrivateKeyByID(0).GetScriptHash()) + `"]`, - result: func(e *executor) interface{} { return &result.NEP17Balances{} }, + result: func(e *executor) any { return &result.NEP17Balances{} }, check: checkNep17Balances, }, }, @@ -396,13 +396,13 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: `["` + testchain.PrivateKeyByID(0).Address() + `", 0]`, - result: func(e *executor) interface{} { return &result.NEP17Transfers{} }, + result: func(e *executor) any { return &result.NEP17Transfers{} }, check: checkNep17Transfers, }, { name: "positive_hash", params: `["` + testchain.PrivateKeyByID(0).GetScriptHash().StringLE() + `", 0]`, - result: func(e *executor) interface{} { return &result.NEP17Transfers{} }, + result: func(e *executor) any { return &result.NEP17Transfers{} }, check: checkNep17Transfers, }, }, @@ -496,8 +496,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: `[]`, - result: func(_ *executor) interface{} { return new(result.StateHeight) }, - check: func(t *testing.T, e *executor, res interface{}) { + result: func(_ *executor) any { return new(result.StateHeight) }, + check: func(t *testing.T, e *executor, res any) { sh, ok := res.(*result.StateHeight) require.True(t, ok) @@ -522,7 +522,7 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: fmt.Sprintf(`["%s", "dGVzdGtleQ=="]`, testContractHash), - result: func(e *executor) interface{} { + result: func(e *executor) any { v := base64.StdEncoding.EncodeToString([]byte("newtestvalue")) return &v }, @@ -530,7 +530,7 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "missing key", params: fmt.Sprintf(`["%s", "dGU="]`, testContractHash), - result: func(e *executor) interface{} { + result: func(e *executor) any { v := "" return &v }, @@ -559,7 +559,7 @@ var rpcTestCases = map[string][]rpcTestCase{ "getbestblockhash": { { params: "[]", - result: func(e *executor) interface{} { + result: func(e *executor) any { v := "0x" + e.chain.CurrentBlockHash().StringLE() return &v }, @@ -569,8 +569,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: "[3, 1]", - result: func(_ *executor) interface{} { return &result.Block{} }, - check: func(t *testing.T, e *executor, blockRes interface{}) { + result: func(_ *executor) any { return &result.Block{} }, + check: func(t *testing.T, e *executor, blockRes any) { res, ok := blockRes.(*result.Block) require.True(t, ok) @@ -615,7 +615,7 @@ var rpcTestCases = map[string][]rpcTestCase{ "getblockcount": { { params: "[]", - result: func(e *executor) interface{} { + result: func(e *executor) any { v := int(e.chain.BlockHeight() + 1) return &v }, @@ -624,7 +624,7 @@ var rpcTestCases = map[string][]rpcTestCase{ "getblockhash": { { params: "[1]", - result: func(e *executor) interface{} { + result: func(e *executor) any { // We don't have `t` here for proper handling, but // error here would lead to panic down below. block, _ := e.chain.GetBlock(e.chain.GetHeaderHash(1)) @@ -668,7 +668,7 @@ var rpcTestCases = map[string][]rpcTestCase{ "getblockheadercount": { { params: "[]", - result: func(e *executor) interface{} { + result: func(e *executor) any { v := int(e.chain.HeaderHeight() + 1) return &v }, @@ -678,7 +678,7 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: "[1]", - result: func(e *executor) interface{} { + result: func(e *executor) any { block, _ := e.chain.GetBlock(e.chain.GetHeaderHash(1)) var expectedBlockSysFee int64 @@ -707,7 +707,7 @@ var rpcTestCases = map[string][]rpcTestCase{ "getcommittee": { { params: "[]", - result: func(e *executor) interface{} { + result: func(e *executor) any { expected, _ := e.chain.GetCommittee() sort.Sort(expected) return &expected @@ -717,7 +717,7 @@ var rpcTestCases = map[string][]rpcTestCase{ "getconnectioncount": { { params: "[]", - result: func(*executor) interface{} { + result: func(*executor) any { v := 0 return &v }, @@ -726,10 +726,10 @@ var rpcTestCases = map[string][]rpcTestCase{ "getnativecontracts": { { params: "[]", - result: func(e *executor) interface{} { + result: func(e *executor) any { return new([]state.NativeContract) }, - check: func(t *testing.T, e *executor, res interface{}) { + check: func(t *testing.T, e *executor, res any) { lst := res.(*[]state.NativeContract) for i := range *lst { cs := e.chain.GetContractState((*lst)[i].Hash) @@ -743,7 +743,7 @@ var rpcTestCases = map[string][]rpcTestCase{ "getpeers": { { params: "[]", - result: func(*executor) interface{} { + result: func(*executor) any { return &result.GetPeers{ Unconnected: []result.Peer{}, Connected: []result.Peer{}, @@ -773,11 +773,11 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: `["` + deploymentTxHash + `"]`, - result: func(e *executor) interface{} { + result: func(e *executor) any { h := 0 return &h }, - check: func(t *testing.T, e *executor, resp interface{}) { + check: func(t *testing.T, e *executor, resp any) { h, ok := resp.(*int) require.True(t, ok) assert.Equal(t, 2, *h) @@ -813,10 +813,10 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: `["` + testchain.MultisigAddress() + `"]`, - result: func(*executor) interface{} { + result: func(*executor) any { return &result.UnclaimedGas{} }, - check: func(t *testing.T, e *executor, resp interface{}) { + check: func(t *testing.T, e *executor, resp any) { actual, ok := resp.(*result.UnclaimedGas) require.True(t, ok) expected := result.UnclaimedGas{ @@ -830,7 +830,7 @@ var rpcTestCases = map[string][]rpcTestCase{ "getcandidates": { { params: "[]", - result: func(*executor) interface{} { + result: func(*executor) any { return &[]result.Candidate{} }, }, @@ -838,11 +838,11 @@ var rpcTestCases = map[string][]rpcTestCase{ "getnextblockvalidators": { { params: "[]", - result: func(*executor) interface{} { + result: func(*executor) any { return &[]result.Validator{} }, /* preview3 doesn't return any validators until there is a vote - check: func(t *testing.T, e *executor, validators interface{}) { + check: func(t *testing.T, e *executor, validators any) { var expected []result.Validator sBValidators := e.chain.GetStandByValidators() for _, sbValidator := range sBValidators { @@ -864,8 +864,8 @@ var rpcTestCases = map[string][]rpcTestCase{ "getversion": { { params: "[]", - result: func(*executor) interface{} { return &result.Version{} }, - check: func(t *testing.T, e *executor, ver interface{}) { + result: func(*executor) any { return &result.Version{} }, + check: func(t *testing.T, e *executor, ver any) { resp, ok := ver.(*result.Version) require.True(t, ok) require.Equal(t, "/NEO-GO:0.98.6-test/", resp.UserAgent) @@ -892,8 +892,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: `["50befd26fdf6e4d957c11e078b24ebce6291456f", "test", []]`, - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.NotNil(t, res.Script) @@ -904,7 +904,7 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, with notifications", params: `["` + nnsContractHash + `", "transfer", [{"type":"Hash160", "value":"0x0bcd2978634d961c24f5aea0802297ff128724d6"},{"type":"String", "value":"neo.com"},{"type":"Any", "value":null}],["0xb248508f4ef7088e10c48f14d04be3272ca29eee"]]`, - result: func(e *executor) interface{} { + result: func(e *executor) any { script := append([]byte{0x0b, 0x0c, 0x07, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x0c, 0x14, 0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0xb, 0x13, 0xc0, 0x1f, 0xc, 0x8, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0xc, 0x14}, nnsHash.BytesBE()...) script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52) return &result.Invoke{ @@ -928,8 +928,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, with storage changes", params: `["0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5", "transfer", [{"type":"Hash160", "value":"0xb248508f4ef7088e10c48f14d04be3272ca29eee"},{"type":"Hash160", "value":"0x0bcd2978634d961c24f5aea0802297ff128724d6"},{"type":"Integer", "value":1},{"type":"Any", "value":null}],["0xb248508f4ef7088e10c48f14d04be3272ca29eee"],true]`, - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.NotNil(t, res.Script) @@ -943,11 +943,11 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { State: "Added", Key: []byte{0xfb, 0xff, 0xff, 0xff, 0x14, 0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0xb}, - Value: []byte{0x41, 0x03, 0x21, 0x01, 0x01, 0x21, 0x01, 0x18, 0}, + Value: []byte{0x41, 0x04, 0x21, 0x01, 0x01, 0x21, 0x01, 0x18, 0x00, 0x21, 0x00}, }, { State: "Changed", Key: []byte{0xfb, 0xff, 0xff, 0xff, 0x14, 0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x8, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2}, - Value: []byte{0x41, 0x03, 0x21, 0x04, 0x2f, 0xd9, 0xf5, 0x05, 0x21, 0x01, 0x18, 0}, + Value: []byte{0x41, 0x04, 0x21, 0x04, 0x2f, 0xd9, 0xf5, 0x05, 0x21, 0x01, 0x18, 0x00, 0x21, 0x00}, }, { State: "Changed", Key: []byte{0xfa, 0xff, 0xff, 0xff, 0x14, 0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x8, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2}, @@ -960,7 +960,7 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, verbose", params: `["` + nnsContractHash + `", "resolve", [{"type":"String", "value":"neo.com"},{"type":"Integer","value":1}], [], true]`, - result: func(e *executor) interface{} { + result: func(e *executor) any { script := append([]byte{0x11, 0xc, 0x7, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0xc0, 0x1f, 0xc, 0x7, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0xc, 0x14}, nnsHash.BytesBE()...) script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52) stdHash, _ := e.chain.GetNativeContractScriptHash(nativenames.StdLib) @@ -1027,8 +1027,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, by index", params: `[20, "50befd26fdf6e4d957c11e078b24ebce6291456f", "test", []]`, - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.NotNil(t, res.Script) @@ -1039,8 +1039,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, by stateroot", params: `["` + block20StateRootLE + `", "50befd26fdf6e4d957c11e078b24ebce6291456f", "test", []]`, - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.NotNil(t, res.Script) @@ -1051,7 +1051,7 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, with notifications", params: `[20, "` + nnsContractHash + `", "transfer", [{"type":"Hash160", "value":"0x0bcd2978634d961c24f5aea0802297ff128724d6"},{"type":"String", "value":"neo.com"},{"type":"Any", "value":null}],["0xb248508f4ef7088e10c48f14d04be3272ca29eee"]]`, - result: func(e *executor) interface{} { + result: func(e *executor) any { script := append([]byte{0x0b, 0x0c, 0x07, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x0c, 0x14, 0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0xb, 0x13, 0xc0, 0x1f, 0xc, 0x8, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0xc, 0x14}, nnsHash.BytesBE()...) script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52) return &result.Invoke{ @@ -1075,7 +1075,7 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, verbose", params: `[20, "` + nnsContractHash + `", "resolve", [{"type":"String", "value":"neo.com"},{"type":"Integer","value":1}], [], true]`, - result: func(e *executor) interface{} { + result: func(e *executor) any { script := append([]byte{0x11, 0xc, 0x7, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0xc0, 0x1f, 0xc, 0x7, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0xc, 0x14}, nnsHash.BytesBE()...) script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52) stdHash, _ := e.chain.GetNativeContractScriptHash(nativenames.StdLib) @@ -1157,8 +1157,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: `["UcVrDUhlbGxvLCB3b3JsZCFoD05lby5SdW50aW1lLkxvZ2FsdWY="]`, - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.NotEqual(t, "", res.Script) @@ -1169,7 +1169,7 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive,verbose", params: `["UcVrDUhlbGxvLCB3b3JsZCFoD05lby5SdW50aW1lLkxvZ2FsdWY=",[],true]`, - result: func(e *executor) interface{} { + result: func(e *executor) any { script := []byte{0x51, 0xc5, 0x6b, 0xd, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x68, 0xf, 0x4e, 0x65, 0x6f, 0x2e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x61, 0x6c, 0x75, 0x66} return &result.Invoke{ State: "FAULT", @@ -1191,8 +1191,8 @@ var rpcTestCases = map[string][]rpcTestCase{ name: "positive, good witness", // script is base64-encoded `invokescript_contract.avm` representation, hashes are hex-encoded LE bytes of hashes used in the contract with `0x` prefix params: fmt.Sprintf(`["%s",["0x0000000009070e030d0f0e020d0c06050e030c01","0x090c060e00010205040307030102000902030f0d"]]`, invokescriptContractAVM), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.Equal(t, "HALT", res.State) @@ -1203,8 +1203,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, bad witness of second hash", params: fmt.Sprintf(`["%s",["0x0000000009070e030d0f0e020d0c06050e030c01"]]`, invokescriptContractAVM), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.Equal(t, "HALT", res.State) @@ -1215,8 +1215,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, no good hashes", params: fmt.Sprintf(`["%s"]`, invokescriptContractAVM), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.Equal(t, "HALT", res.State) @@ -1227,8 +1227,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, bad hashes witness", params: fmt.Sprintf(`["%s",["0x0000000009070e030d0f0e020d0c06050e030c02"]]`, invokescriptContractAVM), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.Equal(t, "HALT", res.State) @@ -1256,8 +1256,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, by index", params: `[20,"UcVrDUhlbGxvLCB3b3JsZCFoD05lby5SdW50aW1lLkxvZ2FsdWY="]`, - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.NotEqual(t, "", res.Script) @@ -1268,8 +1268,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, by stateroot", params: `["` + block20StateRootLE + `","UcVrDUhlbGxvLCB3b3JsZCFoD05lby5SdW50aW1lLkxvZ2FsdWY="]`, - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.NotEqual(t, "", res.Script) @@ -1280,7 +1280,7 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive,verbose", params: `[20, "UcVrDUhlbGxvLCB3b3JsZCFoD05lby5SdW50aW1lLkxvZ2FsdWY=",[],true]`, - result: func(e *executor) interface{} { + result: func(e *executor) any { script := []byte{0x51, 0xc5, 0x6b, 0xd, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x68, 0xf, 0x4e, 0x65, 0x6f, 0x2e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x61, 0x6c, 0x75, 0x66} return &result.Invoke{ State: "FAULT", @@ -1302,8 +1302,8 @@ var rpcTestCases = map[string][]rpcTestCase{ name: "positive, good witness", // script is base64-encoded `invokescript_contract.avm` representation, hashes are hex-encoded LE bytes of hashes used in the contract with `0x` prefix params: fmt.Sprintf(`[20,"%s",["0x0000000009070e030d0f0e020d0c06050e030c01","0x090c060e00010205040307030102000902030f0d"]]`, invokescriptContractAVM), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.Equal(t, "HALT", res.State) @@ -1314,8 +1314,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, bad witness of second hash", params: fmt.Sprintf(`[20,"%s",["0x0000000009070e030d0f0e020d0c06050e030c01"]]`, invokescriptContractAVM), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.Equal(t, "HALT", res.State) @@ -1326,8 +1326,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, no good hashes", params: fmt.Sprintf(`[20,"%s"]`, invokescriptContractAVM), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.Equal(t, "HALT", res.State) @@ -1338,8 +1338,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, bad hashes witness", params: fmt.Sprintf(`[20,"%s",["0x0000000009070e030d0f0e020d0c06050e030c02"]]`, invokescriptContractAVM), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.Equal(t, "HALT", res.State) @@ -1382,8 +1382,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: fmt.Sprintf(`["%s", [], [{"account":"%s"}]]`, verifyContractHash, testchain.PrivateKeyByID(0).PublicKey().GetScriptHash().StringLE()), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.Nil(t, res.Script) // empty witness invocation script (pushes args of `verify` on stack, but this `verify` don't have args) @@ -1395,8 +1395,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, no signers", params: fmt.Sprintf(`["%s", []]`, verifyContractHash), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.Nil(t, res.Script) @@ -1408,8 +1408,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, no arguments", params: fmt.Sprintf(`["%s"]`, verifyContractHash), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.Nil(t, res.Script) @@ -1421,8 +1421,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, with signers and scripts", params: fmt.Sprintf(`["%s", [], [{"account":"%s", "invocation":"MQo=", "verification": ""}]]`, verifyContractHash, testchain.PrivateKeyByID(0).PublicKey().GetScriptHash().StringLE()), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.Nil(t, res.Script) @@ -1434,8 +1434,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, with arguments, result=true", params: fmt.Sprintf(`["%s", [{"type": "String", "value": "good_string"}, {"type": "Integer", "value": "4"}, {"type":"Boolean", "value": false}]]`, verifyWithArgsContractHash), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) expectedInvScript := io.NewBufBinWriter() @@ -1452,8 +1452,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, with arguments, result=false", params: fmt.Sprintf(`["%s", [{"type": "String", "value": "invalid_string"}, {"type": "Integer", "value": "4"}, {"type":"Boolean", "value": false}]]`, verifyWithArgsContractHash), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) expectedInvScript := io.NewBufBinWriter() @@ -1487,8 +1487,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, by index", params: fmt.Sprintf(`[20,"%s", [], [{"account":"%s"}]]`, verifyContractHash, testchain.PrivateKeyByID(0).PublicKey().GetScriptHash().StringLE()), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.Nil(t, res.Script) // empty witness invocation script (pushes args of `verify` on stack, but this `verify` don't have args) @@ -1500,8 +1500,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, by stateroot", params: fmt.Sprintf(`["`+block20StateRootLE+`","%s", [], [{"account":"%s"}]]`, verifyContractHash, testchain.PrivateKeyByID(0).PublicKey().GetScriptHash().StringLE()), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.Nil(t, res.Script) // empty witness invocation script (pushes args of `verify` on stack, but this `verify` don't have args) @@ -1513,8 +1513,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, no signers", params: fmt.Sprintf(`[20,"%s", []]`, verifyContractHash), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.Nil(t, res.Script) @@ -1526,8 +1526,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, no arguments", params: fmt.Sprintf(`[20,"%s"]`, verifyContractHash), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.Nil(t, res.Script) @@ -1539,8 +1539,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, with signers and scripts", params: fmt.Sprintf(`[20,"%s", [], [{"account":"%s", "invocation":"MQo=", "verification": ""}]]`, verifyContractHash, testchain.PrivateKeyByID(0).PublicKey().GetScriptHash().StringLE()), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) assert.Nil(t, res.Script) @@ -1552,8 +1552,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, with arguments, result=true", params: fmt.Sprintf(`[20,"%s", [{"type": "String", "value": "good_string"}, {"type": "Integer", "value": "4"}, {"type":"Boolean", "value": false}]]`, verifyWithArgsContractHash), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) expectedInvScript := io.NewBufBinWriter() @@ -1570,8 +1570,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive, with arguments, result=false", params: fmt.Sprintf(`[20, "%s", [{"type": "String", "value": "invalid_string"}, {"type": "Integer", "value": "4"}, {"type":"Boolean", "value": false}]]`, verifyWithArgsContractHash), - result: func(e *executor) interface{} { return &result.Invoke{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.Invoke{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.Invoke) require.True(t, ok) expectedInvScript := io.NewBufBinWriter() @@ -1610,8 +1610,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: `["AB0AAACWP5gAAAAAAEDaEgAAAAAAGAAAAAHunqIsJ+NL0BSPxBCOCPdOj1BIsoAAXgsDAOh2SBcAAAAMFBEmW7QXJQBBvgTo+iQOOPV8HlabDBTunqIsJ+NL0BSPxBCOCPdOj1BIshTAHwwIdHJhbnNmZXIMFPVj6kC8KD1NDgXEjqMFs/Kgc0DvQWJ9W1IBQgxAJ6norhWoZxp+Hj1JFhi+Z3qI9DUkLSbfsbaLSaJIqxTfdmPbNFDVK1G+oa+LWmpRp/bj9+QZM7yC+S6HXUI7rigMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwkFW57Mn"]`, - result: func(e *executor) interface{} { return &result.RelayResult{} }, - check: func(t *testing.T, e *executor, inv interface{}) { + result: func(e *executor) any { return &result.RelayResult{} }, + check: func(t *testing.T, e *executor, inv any) { res, ok := inv.(*result.RelayResult) require.True(t, ok) expectedHash := "c11861dec1dd0f188608b725095041fcfc90abe51eea044993f122f22472753e" @@ -1674,8 +1674,8 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: `["Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2"]`, - result: func(*executor) interface{} { return &result.ValidateAddress{} }, - check: func(t *testing.T, e *executor, va interface{}) { + result: func(*executor) any { return &result.ValidateAddress{} }, + check: func(t *testing.T, e *executor, va any) { res, ok := va.(*result.ValidateAddress) require.True(t, ok) assert.Equal(t, "Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2", res.Address) @@ -1685,7 +1685,7 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "negative", params: "[1]", - result: func(*executor) interface{} { + result: func(*executor) any { return &result.ValidateAddress{ Address: float64(1), IsValid: false, @@ -2267,7 +2267,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] testHeaderHash := chain.GetHeaderHash(1).StringLE() hdr := e.getHeader(testHeaderHash) - runCase := func(t *testing.T, rpc string, expected, actual interface{}) { + runCase := func(t *testing.T, rpc string, expected, actual any) { body := doRPCCall(rpc, httpSrv.URL, t) data := checkErrGetResult(t, body, false) require.NoError(t, json.Unmarshal(data, actual)) @@ -2617,7 +2617,7 @@ func encodeBlock(t *testing.T, b *block.Block) string { return base64.StdEncoding.EncodeToString(w.Bytes()) } -func (tc rpcTestCase) getResultPair(e *executor) (expected interface{}, res interface{}) { +func (tc rpcTestCase) getResultPair(e *executor) (expected any, res any) { expected = tc.result(e) resVal := reflect.New(reflect.TypeOf(expected).Elem()) res = resVal.Interface() @@ -2683,7 +2683,7 @@ func doRPCCallOverHTTP(rpcCall string, url string, t *testing.T) []byte { return bytes.TrimSpace(body) } -func checkNep11Balances(t *testing.T, e *executor, acc interface{}) { +func checkNep11Balances(t *testing.T, e *executor, acc any) { res, ok := acc.(*result.NEP11Balances) require.True(t, ok) @@ -2721,7 +2721,7 @@ func checkNep11Balances(t *testing.T, e *executor, acc interface{}) { require.ElementsMatch(t, expected.Balances, res.Balances) } -func checkNep17Balances(t *testing.T, e *executor, acc interface{}) { +func checkNep17Balances(t *testing.T, e *executor, acc any) { res, ok := acc.(*result.NEP17Balances) require.True(t, ok) rubles, err := util.Uint160DecodeStringLE(testContractHash) @@ -2757,11 +2757,11 @@ func checkNep17Balances(t *testing.T, e *executor, acc interface{}) { require.ElementsMatch(t, expected.Balances, res.Balances) } -func checkNep11Transfers(t *testing.T, e *executor, acc interface{}) { +func checkNep11Transfers(t *testing.T, e *executor, acc any) { checkNep11TransfersAux(t, e, acc, []int{0}, []int{0, 1, 2}) } -func checkNep11TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rcvd []int) { +func checkNep11TransfersAux(t *testing.T, e *executor, acc any, sent, rcvd []int) { res, ok := acc.(*result.NEP11Transfers) require.True(t, ok) @@ -2859,11 +2859,11 @@ func checkNep11TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rc require.Equal(t, arr, res.Received) } -func checkNep17Transfers(t *testing.T, e *executor, acc interface{}) { +func checkNep17Transfers(t *testing.T, e *executor, acc any) { checkNep17TransfersAux(t, e, acc, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}, []int{0, 1, 2, 3, 4, 5, 6, 7, 8}) } -func checkNep17TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rcvd []int) { +func checkNep17TransfersAux(t *testing.T, e *executor, acc any, sent, rcvd []int) { res, ok := acc.(*result.NEP17Transfers) require.True(t, ok) rublesHash, err := util.Uint160DecodeStringLE(testContractHash) diff --git a/pkg/services/rpcsrv/subscription.go b/pkg/services/rpcsrv/subscription.go index 511aed2ef..e8f2336cc 100644 --- a/pkg/services/rpcsrv/subscription.go +++ b/pkg/services/rpcsrv/subscription.go @@ -27,7 +27,7 @@ type ( // feed stores subscriber's desired event ID with filter. feed struct { event neorpc.EventID - filter interface{} + filter any } ) @@ -37,7 +37,7 @@ func (f feed) EventID() neorpc.EventID { } // Filter implements neorpc.EventComparator interface and returns notification filter. -func (f feed) Filter() interface{} { +func (f feed) Filter() any { return f.filter } diff --git a/pkg/services/rpcsrv/subscription_test.go b/pkg/services/rpcsrv/subscription_test.go index 64cedcd2d..52ff0c6fb 100644 --- a/pkg/services/rpcsrv/subscription_test.go +++ b/pkg/services/rpcsrv/subscription_test.go @@ -156,7 +156,7 @@ func TestSubscriptions(t *testing.T) { for _, id := range subIDs { callUnsubscribe(t, c, respMsgs, id) } - finishedFlag.CAS(false, true) + finishedFlag.CompareAndSwap(false, true) c.Close() } @@ -171,7 +171,7 @@ func TestFilteredSubscriptions(t *testing.T) { "tx matching sender": { params: `["transaction_added", {"sender":"` + goodSender.StringLE() + `"}]`, check: func(t *testing.T, resp *neorpc.Notification) { - rmap := resp.Payload[0].(map[string]interface{}) + rmap := resp.Payload[0].(map[string]any) require.Equal(t, neorpc.TransactionEventID, resp.Event) sender := rmap["sender"].(string) require.Equal(t, address.Uint160ToString(goodSender), sender) @@ -180,10 +180,10 @@ func TestFilteredSubscriptions(t *testing.T) { "tx matching signer": { params: `["transaction_added", {"signer":"` + goodSender.StringLE() + `"}]`, check: func(t *testing.T, resp *neorpc.Notification) { - rmap := resp.Payload[0].(map[string]interface{}) + rmap := resp.Payload[0].(map[string]any) require.Equal(t, neorpc.TransactionEventID, resp.Event) - signers := rmap["signers"].([]interface{}) - signer0 := signers[0].(map[string]interface{}) + signers := rmap["signers"].([]any) + signer0 := signers[0].(map[string]any) signer0acc := signer0["account"].(string) require.Equal(t, "0x"+goodSender.StringLE(), signer0acc) }, @@ -191,12 +191,12 @@ func TestFilteredSubscriptions(t *testing.T) { "tx matching sender and signer": { params: `["transaction_added", {"sender":"` + goodSender.StringLE() + `", "signer":"` + goodSender.StringLE() + `"}]`, check: func(t *testing.T, resp *neorpc.Notification) { - rmap := resp.Payload[0].(map[string]interface{}) + rmap := resp.Payload[0].(map[string]any) require.Equal(t, neorpc.TransactionEventID, resp.Event) sender := rmap["sender"].(string) require.Equal(t, address.Uint160ToString(goodSender), sender) - signers := rmap["signers"].([]interface{}) - signer0 := signers[0].(map[string]interface{}) + signers := rmap["signers"].([]any) + signer0 := signers[0].(map[string]any) signer0acc := signer0["account"].(string) require.Equal(t, "0x"+goodSender.StringLE(), signer0acc) }, @@ -204,7 +204,7 @@ func TestFilteredSubscriptions(t *testing.T) { "notification matching contract hash": { params: `["notification_from_execution", {"contract":"` + testContractHash + `"}]`, check: func(t *testing.T, resp *neorpc.Notification) { - rmap := resp.Payload[0].(map[string]interface{}) + rmap := resp.Payload[0].(map[string]any) require.Equal(t, neorpc.NotificationEventID, resp.Event) c := rmap["contract"].(string) require.Equal(t, "0x"+testContractHash, c) @@ -213,7 +213,7 @@ func TestFilteredSubscriptions(t *testing.T) { "notification matching name": { params: `["notification_from_execution", {"name":"my_pretty_notification"}]`, check: func(t *testing.T, resp *neorpc.Notification) { - rmap := resp.Payload[0].(map[string]interface{}) + rmap := resp.Payload[0].(map[string]any) require.Equal(t, neorpc.NotificationEventID, resp.Event) n := rmap["name"].(string) require.Equal(t, "my_pretty_notification", n) @@ -222,7 +222,7 @@ func TestFilteredSubscriptions(t *testing.T) { "notification matching contract hash and name": { params: `["notification_from_execution", {"contract":"` + testContractHash + `", "name":"my_pretty_notification"}]`, check: func(t *testing.T, resp *neorpc.Notification) { - rmap := resp.Payload[0].(map[string]interface{}) + rmap := resp.Payload[0].(map[string]any) require.Equal(t, neorpc.NotificationEventID, resp.Event) c := rmap["contract"].(string) require.Equal(t, "0x"+testContractHash, c) @@ -233,7 +233,7 @@ func TestFilteredSubscriptions(t *testing.T) { "execution matching state": { params: `["transaction_executed", {"state":"HALT"}]`, check: func(t *testing.T, resp *neorpc.Notification) { - rmap := resp.Payload[0].(map[string]interface{}) + rmap := resp.Payload[0].(map[string]any) require.Equal(t, neorpc.ExecutionEventID, resp.Event) st := rmap["vmstate"].(string) require.Equal(t, "HALT", st) @@ -242,7 +242,7 @@ func TestFilteredSubscriptions(t *testing.T) { "execution matching container": { params: `["transaction_executed", {"container":"` + deploymentTxHash + `"}]`, check: func(t *testing.T, resp *neorpc.Notification) { - rmap := resp.Payload[0].(map[string]interface{}) + rmap := resp.Payload[0].(map[string]any) require.Equal(t, neorpc.ExecutionEventID, resp.Event) tx := rmap["container"].(string) require.Equal(t, "0x"+deploymentTxHash, tx) @@ -251,7 +251,7 @@ func TestFilteredSubscriptions(t *testing.T) { "execution matching state and container": { params: `["transaction_executed", {"state":"HALT", "container":"` + deploymentTxHash + `"}]`, check: func(t *testing.T, resp *neorpc.Notification) { - rmap := resp.Payload[0].(map[string]interface{}) + rmap := resp.Payload[0].(map[string]any) require.Equal(t, neorpc.ExecutionEventID, resp.Event) tx := rmap["container"].(string) require.Equal(t, "0x"+deploymentTxHash, tx) @@ -299,7 +299,7 @@ func TestFilteredSubscriptions(t *testing.T) { for { resp := getNotification(t, respMsgs) - rmap := resp.Payload[0].(map[string]interface{}) + rmap := resp.Payload[0].(map[string]any) if resp.Event == neorpc.BlockEventID { index := rmap["index"].(float64) if uint32(index) == lastBlock { @@ -312,7 +312,7 @@ func TestFilteredSubscriptions(t *testing.T) { callUnsubscribe(t, c, respMsgs, subID) callUnsubscribe(t, c, respMsgs, blockSubID) - finishedFlag.CAS(false, true) + finishedFlag.CompareAndSwap(false, true) c.Close() }) } @@ -331,25 +331,25 @@ func TestFilteredNotaryRequestSubscriptions(t *testing.T) { "matching sender": { params: `["notary_request_event", {"sender":"` + goodSender.StringLE() + `"}]`, check: func(t *testing.T, resp *neorpc.Notification) { - rmap := resp.Payload[0].(map[string]interface{}) + rmap := resp.Payload[0].(map[string]any) require.Equal(t, neorpc.NotaryRequestEventID, resp.Event) require.Equal(t, "added", rmap["type"].(string)) - req := rmap["notaryrequest"].(map[string]interface{}) - fbTx := req["fallbacktx"].(map[string]interface{}) - sender := fbTx["signers"].([]interface{})[1].(map[string]interface{})["account"].(string) + req := rmap["notaryrequest"].(map[string]any) + fbTx := req["fallbacktx"].(map[string]any) + sender := fbTx["signers"].([]any)[1].(map[string]any)["account"].(string) require.Equal(t, "0x"+goodSender.StringLE(), sender) }, }, "matching signer": { params: `["notary_request_event", {"signer":"` + goodSender.StringLE() + `"}]`, check: func(t *testing.T, resp *neorpc.Notification) { - rmap := resp.Payload[0].(map[string]interface{}) + rmap := resp.Payload[0].(map[string]any) require.Equal(t, neorpc.NotaryRequestEventID, resp.Event) require.Equal(t, "added", rmap["type"].(string)) - req := rmap["notaryrequest"].(map[string]interface{}) - mainTx := req["maintx"].(map[string]interface{}) - signers := mainTx["signers"].([]interface{}) - signer0 := signers[0].(map[string]interface{}) + req := rmap["notaryrequest"].(map[string]any) + mainTx := req["maintx"].(map[string]any) + signers := mainTx["signers"].([]any) + signer0 := signers[0].(map[string]any) signer0acc := signer0["account"].(string) require.Equal(t, "0x"+goodSender.StringLE(), signer0acc) }, @@ -357,16 +357,16 @@ func TestFilteredNotaryRequestSubscriptions(t *testing.T) { "matching sender and signer": { params: `["notary_request_event", {"sender":"` + goodSender.StringLE() + `", "signer":"` + goodSender.StringLE() + `"}]`, check: func(t *testing.T, resp *neorpc.Notification) { - rmap := resp.Payload[0].(map[string]interface{}) + rmap := resp.Payload[0].(map[string]any) require.Equal(t, neorpc.NotaryRequestEventID, resp.Event) require.Equal(t, "added", rmap["type"].(string)) - req := rmap["notaryrequest"].(map[string]interface{}) - mainTx := req["maintx"].(map[string]interface{}) - fbTx := req["fallbacktx"].(map[string]interface{}) - sender := fbTx["signers"].([]interface{})[1].(map[string]interface{})["account"].(string) + req := rmap["notaryrequest"].(map[string]any) + mainTx := req["maintx"].(map[string]any) + fbTx := req["fallbacktx"].(map[string]any) + sender := fbTx["signers"].([]any)[1].(map[string]any)["account"].(string) require.Equal(t, "0x"+goodSender.StringLE(), sender) - signers := mainTx["signers"].([]interface{}) - signer0 := signers[0].(map[string]interface{}) + signers := mainTx["signers"].([]any) + signer0 := signers[0].(map[string]any) signer0acc := signer0["account"].(string) require.Equal(t, "0x"+goodSender.StringLE(), signer0acc) }, @@ -408,7 +408,7 @@ func TestFilteredNotaryRequestSubscriptions(t *testing.T) { callUnsubscribe(t, c, respMsgs, subID) }) } - finishedFlag.CAS(false, true) + finishedFlag.CompareAndSwap(false, true) c.Close() } @@ -443,12 +443,12 @@ func TestFilteredBlockSubscriptions(t *testing.T) { } require.Equal(t, neorpc.BlockEventID, resp.Event) - rmap := resp.Payload[0].(map[string]interface{}) + rmap := resp.Payload[0].(map[string]any) primary := rmap["primary"].(float64) require.Equal(t, 3, int(primary)) } callUnsubscribe(t, c, respMsgs, blockSubID) - finishedFlag.CAS(false, true) + finishedFlag.CompareAndSwap(false, true) c.Close() } @@ -477,7 +477,7 @@ func TestMaxSubscriptions(t *testing.T) { } } - finishedFlag.CAS(false, true) + finishedFlag.CompareAndSwap(false, true) c.Close() } @@ -519,7 +519,7 @@ func TestBadSubUnsub(t *testing.T) { t.Run("subscribe", testF(t, subCases)) t.Run("unsubscribe", testF(t, unsubCases)) - finishedFlag.CAS(false, true) + finishedFlag.CompareAndSwap(false, true) c.Close() } @@ -614,6 +614,6 @@ func TestSubscriptionOverflow(t *testing.T) { // `Missed` is the last event and there is nothing afterwards. require.Equal(t, 0, len(respMsgs)) - finishedFlag.CAS(false, true) + finishedFlag.CompareAndSwap(false, true) c.Close() } diff --git a/pkg/services/rpcsrv/tokens.go b/pkg/services/rpcsrv/tokens.go index 4844a85d4..38530fa95 100644 --- a/pkg/services/rpcsrv/tokens.go +++ b/pkg/services/rpcsrv/tokens.go @@ -6,9 +6,9 @@ import ( // tokenTransfers is a generic type used to represent NEP-11 and NEP-17 transfers. type tokenTransfers struct { - Sent []interface{} `json:"sent"` - Received []interface{} `json:"received"` - Address string `json:"address"` + Sent []any `json:"sent"` + Received []any `json:"received"` + Address string `json:"address"` } // nep17TransferToNEP11 adds an ID to the provided NEP-17 transfer and returns a new diff --git a/pkg/services/stateroot/service_test.go b/pkg/services/stateroot/service_test.go index d71c2f101..f4e2eb5fd 100644 --- a/pkg/services/stateroot/service_test.go +++ b/pkg/services/stateroot/service_test.go @@ -2,7 +2,6 @@ package stateroot_test import ( "crypto/elliptic" - "errors" "path/filepath" "sort" "testing" @@ -85,7 +84,7 @@ func TestStateRoot(t *testing.T) { gasValidatorInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas)) h, pubs, accs := newMajorityMultisigWithGAS(t, 2) - validatorNodes := []interface{}{pubs[0].Bytes(), pubs[1].Bytes()} + validatorNodes := []any{pubs[0].Bytes(), pubs[1].Bytes()} designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", int64(roles.StateValidator), validatorNodes) updateIndex := bc.BlockHeight() @@ -133,7 +132,7 @@ func TestStateRoot(t *testing.T) { require.NoError(t, err) data := testSignStateRoot(t, r, pubs, accInv) err = srv.OnPayload(&payload.Extensible{Data: data}) - require.True(t, errors.Is(err, core.ErrWitnessHashMismatch), "got: %v", err) + require.ErrorIs(t, err, core.ErrWitnessHashMismatch) require.EqualValues(t, 0, bc.GetStateModule().CurrentValidatedHeight()) }) @@ -166,7 +165,7 @@ func TestStateRootInitNonZeroHeight(t *testing.T) { designationSuperInvoker := e.NewInvoker(e.NativeHash(t, nativenames.Designation), validator, committee) gasValidatorInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas)) - validatorNodes := []interface{}{pubs[0].Bytes(), pubs[1].Bytes()} + validatorNodes := []any{pubs[0].Bytes(), pubs[1].Bytes()} designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", int64(roles.StateValidator), validatorNodes) gasValidatorInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), h, 1_0000_0000, nil) @@ -244,7 +243,7 @@ func TestStateRootFull(t *testing.T) { srv.Start() t.Cleanup(srv.Shutdown) - validatorNodes := []interface{}{pubs[0].Bytes(), pubs[1].Bytes()} + validatorNodes := []any{pubs[0].Bytes(), pubs[1].Bytes()} designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", int64(roles.StateValidator), validatorNodes) gasValidatorInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), h, 1_0000_0000, nil) diff --git a/pkg/services/stateroot/validators.go b/pkg/services/stateroot/validators.go index 4e266cb1c..a62e75892 100644 --- a/pkg/services/stateroot/validators.go +++ b/pkg/services/stateroot/validators.go @@ -25,7 +25,7 @@ func (s *service) Name() string { // Start runs service instance in a separate goroutine. // The service only starts once, subsequent calls to Start are no-op. func (s *service) Start() { - if !s.started.CAS(false, true) { + if !s.started.CompareAndSwap(false, true) { return } s.log.Info("starting state validation service") @@ -68,7 +68,7 @@ drainloop: // to Shutdown on the same instance are no-op. The instance that was stopped can // not be started again by calling Start (use a new instance if needed). func (s *service) Shutdown() { - if !s.started.CAS(true, false) { + if !s.started.CompareAndSwap(true, false) { return } s.log.Info("stopping state validation service") diff --git a/pkg/smartcontract/binding/generate.go b/pkg/smartcontract/binding/generate.go index 62733b7ac..7e6716908 100644 --- a/pkg/smartcontract/binding/generate.go +++ b/pkg/smartcontract/binding/generate.go @@ -48,14 +48,25 @@ const Hash = "{{ .Hash }}" type ( // Config contains parameter for the generated binding. Config struct { - Package string `yaml:"package,omitempty"` - Manifest *manifest.Manifest `yaml:"-"` - Hash util.Uint160 `yaml:"hash,omitempty"` - Overrides map[string]Override `yaml:"overrides,omitempty"` - CallFlags map[string]callflag.CallFlag `yaml:"callflags,omitempty"` - NamedTypes map[string]ExtendedType `yaml:"namedtypes,omitempty"` - Types map[string]ExtendedType `yaml:"types,omitempty"` - Output io.Writer `yaml:"-"` + Package string `yaml:"package,omitempty"` + Manifest *manifest.Manifest `yaml:"-"` + // Hash denotes the contract hash and is allowed to be empty for RPC bindings + // generation (if not provided by the user). + Hash util.Uint160 `yaml:"hash,omitempty"` + Overrides map[string]Override `yaml:"overrides,omitempty"` + CallFlags map[string]callflag.CallFlag `yaml:"callflags,omitempty"` + // NamedTypes contains exported structured types that have some name (even + // if the original structure doesn't) and a number of internal fields. The + // map key is in the form of `namespace.name`, the value is fully-qualified + // and possibly nested description of the type structure. + NamedTypes map[string]ExtendedType `yaml:"namedtypes,omitempty"` + // Types contains type structure description for various types used in + // smartcontract. The map key has one of the following forms: + // - `methodName` for method return value; + // - `mathodName.paramName` for method's parameter value. + // - `eventName.paramName` for event's parameter value. + Types map[string]ExtendedType `yaml:"types,omitempty"` + Output io.Writer `yaml:"-"` } ExtendedType struct { @@ -63,7 +74,7 @@ type ( Name string `yaml:"name,omitempty"` // Structure name, omitted for arrays, interfaces and maps. Interface string `yaml:"interface,omitempty"` // Interface type name, "iterator" only for now. Key smartcontract.ParamType `yaml:"key,omitempty"` // Key type (only simple types can be used for keys) for maps. - Value *ExtendedType `yaml:"value,omitempty"` // Value type for iterators and arrays. + Value *ExtendedType `yaml:"value,omitempty"` // Value type for iterators, arrays and maps. Fields []FieldExtendedType `yaml:"fields,omitempty"` // Ordered type data for structure fields. } @@ -126,7 +137,7 @@ func scTypeToGo(name string, typ smartcontract.ParamType, cfg *Config) (string, switch typ { case smartcontract.AnyType: - return "interface{}", "" + return "any", "" case smartcontract.BoolType: return "bool", "" case smartcontract.IntegerType: @@ -144,11 +155,11 @@ func scTypeToGo(name string, typ smartcontract.ParamType, cfg *Config) (string, case smartcontract.SignatureType: return "interop.Signature", "github.com/nspcc-dev/neo-go/pkg/interop" case smartcontract.ArrayType: - return "[]interface{}", "" + return "[]any", "" case smartcontract.MapType: - return "map[string]interface{}", "" + return "map[string]any", "" case smartcontract.InteropInterfaceType: - return "interface{}", "" + return "any", "" case smartcontract.VoidType: return "", "" default: @@ -160,9 +171,11 @@ func scTypeToGo(name string, typ smartcontract.ParamType, cfg *Config) (string, // and type conversion function. It assumes manifest to be present in the // configuration and assumes it to be correct (passing IsValid check). func TemplateFromManifest(cfg Config, scTypeConverter func(string, smartcontract.ParamType, *Config) (string, string)) ContractTmpl { - hStr := "" - for _, b := range cfg.Hash.BytesBE() { - hStr += fmt.Sprintf("\\x%02x", b) + var hStr string + if !cfg.Hash.Equals(util.Uint160{}) { + for _, b := range cfg.Hash.BytesBE() { + hStr += fmt.Sprintf("\\x%02x", b) + } } ctr := ContractTmpl{ @@ -256,3 +269,34 @@ func TemplateFromManifest(cfg Config, scTypeConverter func(string, smartcontract func upperFirst(s string) string { return strings.ToUpper(s[0:1]) + s[1:] } + +// Equals compares two extended types field-by-field and returns true if they are +// equal. +func (e *ExtendedType) Equals(other *ExtendedType) bool { + if e == nil && other == nil { + return true + } + if e != nil && other == nil || + e == nil && other != nil { + return false + } + if !((e.Base == other.Base || (e.Base == smartcontract.ByteArrayType || e.Base == smartcontract.StringType) && + (other.Base == smartcontract.ByteArrayType || other.Base == smartcontract.StringType)) && + e.Name == other.Name && + e.Interface == other.Interface && + e.Key == other.Key) { + return false + } + if len(e.Fields) != len(other.Fields) { + return false + } + for i := range e.Fields { + if e.Fields[i].Field != other.Fields[i].Field { + return false + } + if !e.Fields[i].ExtendedType.Equals(&other.Fields[i].ExtendedType) { + return false + } + } + return (e.Value == nil && other.Value == nil) || (e.Value != nil && other.Value != nil && e.Value.Equals(other.Value)) +} diff --git a/pkg/smartcontract/binding/generate_test.go b/pkg/smartcontract/binding/generate_test.go new file mode 100644 index 000000000..64522d28c --- /dev/null +++ b/pkg/smartcontract/binding/generate_test.go @@ -0,0 +1,229 @@ +package binding + +import ( + "testing" + + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/stretchr/testify/assert" +) + +func TestExtendedType_Equals(t *testing.T) { + crazyT := ExtendedType{ + Base: smartcontract.StringType, + Name: "qwertyu", + Interface: "qwerty", + Key: smartcontract.BoolType, + Value: &ExtendedType{ + Base: smartcontract.IntegerType, + }, + Fields: []FieldExtendedType{ + { + Field: "qwe", + ExtendedType: ExtendedType{ + Base: smartcontract.IntegerType, + Name: "qwer", + Interface: "qw", + Key: smartcontract.ArrayType, + Fields: []FieldExtendedType{ + { + Field: "as", + }, + }, + }, + }, + { + Field: "asf", + ExtendedType: ExtendedType{ + Base: smartcontract.BoolType, + }, + }, + { + Field: "sffg", + ExtendedType: ExtendedType{ + Base: smartcontract.AnyType, + }, + }, + }, + } + tcs := map[string]struct { + a *ExtendedType + b *ExtendedType + expectedRes bool + }{ + "both nil": { + a: nil, + b: nil, + expectedRes: true, + }, + "a is nil": { + a: nil, + b: &ExtendedType{}, + expectedRes: false, + }, + "b is nil": { + a: &ExtendedType{}, + b: nil, + expectedRes: false, + }, + "base mismatch": { + a: &ExtendedType{ + Base: smartcontract.StringType, + }, + b: &ExtendedType{ + Base: smartcontract.IntegerType, + }, + expectedRes: false, + }, + "name mismatch": { + a: &ExtendedType{ + Base: smartcontract.ArrayType, + Name: "q", + }, + b: &ExtendedType{ + Base: smartcontract.ArrayType, + Name: "w", + }, + expectedRes: false, + }, + "number of fields mismatch": { + a: &ExtendedType{ + Base: smartcontract.ArrayType, + Name: "q", + Fields: []FieldExtendedType{ + { + Field: "IntField", + ExtendedType: ExtendedType{Base: smartcontract.IntegerType}, + }, + }, + }, + b: &ExtendedType{ + Base: smartcontract.ArrayType, + Name: "w", + Fields: []FieldExtendedType{ + { + Field: "IntField", + ExtendedType: ExtendedType{Base: smartcontract.IntegerType}, + }, + { + Field: "BoolField", + ExtendedType: ExtendedType{Base: smartcontract.BoolType}, + }, + }, + }, + expectedRes: false, + }, + "field names mismatch": { + a: &ExtendedType{ + Base: smartcontract.ArrayType, + Fields: []FieldExtendedType{ + { + Field: "IntField", + ExtendedType: ExtendedType{Base: smartcontract.IntegerType}, + }, + }, + }, + b: &ExtendedType{ + Base: smartcontract.ArrayType, + Fields: []FieldExtendedType{ + { + Field: "BoolField", + ExtendedType: ExtendedType{Base: smartcontract.BoolType}, + }, + }, + }, + expectedRes: false, + }, + "field types mismatch": { + a: &ExtendedType{ + Base: smartcontract.ArrayType, + Fields: []FieldExtendedType{ + { + Field: "Field", + ExtendedType: ExtendedType{Base: smartcontract.IntegerType}, + }, + }, + }, + b: &ExtendedType{ + Base: smartcontract.ArrayType, + Fields: []FieldExtendedType{ + { + Field: "Field", + ExtendedType: ExtendedType{Base: smartcontract.BoolType}, + }, + }, + }, + expectedRes: false, + }, + "interface mismatch": { + a: &ExtendedType{Interface: "iterator"}, + b: &ExtendedType{Interface: "unknown"}, + expectedRes: false, + }, + "value is nil": { + a: &ExtendedType{ + Base: smartcontract.StringType, + }, + b: &ExtendedType{ + Base: smartcontract.StringType, + }, + expectedRes: true, + }, + "a value is not nil": { + a: &ExtendedType{ + Base: smartcontract.ArrayType, + Value: &ExtendedType{}, + }, + b: &ExtendedType{ + Base: smartcontract.ArrayType, + }, + expectedRes: false, + }, + "b value is not nil": { + a: &ExtendedType{ + Base: smartcontract.ArrayType, + }, + b: &ExtendedType{ + Base: smartcontract.ArrayType, + Value: &ExtendedType{}, + }, + expectedRes: false, + }, + "byte array tolerance for a": { + a: &ExtendedType{ + Base: smartcontract.StringType, + }, + b: &ExtendedType{ + Base: smartcontract.ByteArrayType, + }, + expectedRes: true, + }, + "byte array tolerance for b": { + a: &ExtendedType{ + Base: smartcontract.ByteArrayType, + }, + b: &ExtendedType{ + Base: smartcontract.StringType, + }, + expectedRes: true, + }, + "key mismatch": { + a: &ExtendedType{ + Key: smartcontract.StringType, + }, + b: &ExtendedType{ + Key: smartcontract.IntegerType, + }, + expectedRes: false, + }, + "good nested": { + a: &crazyT, + b: &crazyT, + expectedRes: true, + }, + } + for name, tc := range tcs { + t.Run(name, func(t *testing.T) { + assert.Equal(t, tc.expectedRes, tc.a.Equals(tc.b)) + }) + } +} diff --git a/pkg/smartcontract/binding/override.go b/pkg/smartcontract/binding/override.go index 366bd562e..ed861a026 100644 --- a/pkg/smartcontract/binding/override.go +++ b/pkg/smartcontract/binding/override.go @@ -48,7 +48,7 @@ func NewOverrideFromString(s string) Override { } // UnmarshalYAML implements the YAML Unmarshaler interface. -func (o *Override) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (o *Override) UnmarshalYAML(unmarshal func(any) error) error { var s string err := unmarshal(&s) @@ -61,7 +61,7 @@ func (o *Override) UnmarshalYAML(unmarshal func(interface{}) error) error { } // MarshalYAML implements the YAML marshaler interface. -func (o Override) MarshalYAML() (interface{}, error) { +func (o Override) MarshalYAML() (any, error) { if o.Package == "" { return o.TypeName, nil } diff --git a/pkg/smartcontract/builder.go b/pkg/smartcontract/builder.go index 369be1b64..016dd5dd5 100644 --- a/pkg/smartcontract/builder.go +++ b/pkg/smartcontract/builder.go @@ -41,10 +41,11 @@ func NewBuilder() *Builder { // InvokeMethod is the most generic contract method invoker, the code it produces // packs all of the arguments given into an array and calls some method of the -// contract. The correctness of this invocation (number and type of parameters) is -// out of scope of this method, as well as return value, if contract's method returns -// something this value just remains on the execution stack. -func (b *Builder) InvokeMethod(contract util.Uint160, method string, params ...interface{}) { +// contract. It accepts as parameters everything that emit.Array accepts. The +// correctness of this invocation (number and type of parameters) is out of scope +// of this method, as well as return value, if contract's method returns something +// this value just remains on the execution stack. +func (b *Builder) InvokeMethod(contract util.Uint160, method string, params ...any) { emit.AppCall(b.bw.BinWriter, contract, method, callflag.All, params...) } @@ -61,7 +62,7 @@ func (b *Builder) Assert() { // that as well as NEO's 'vote'. The ASSERT then allow to simplify transaction // status checking, if action is successful then transaction is successful as // well, if it went wrong than whole transaction fails (ends with vmstate.FAULT). -func (b *Builder) InvokeWithAssert(contract util.Uint160, method string, params ...interface{}) { +func (b *Builder) InvokeWithAssert(contract util.Uint160, method string, params ...any) { b.InvokeMethod(contract, method, params...) b.Assert() } diff --git a/pkg/smartcontract/callflag/call_flags.go b/pkg/smartcontract/callflag/call_flags.go index 224077c4b..03eebd0aa 100644 --- a/pkg/smartcontract/callflag/call_flags.go +++ b/pkg/smartcontract/callflag/call_flags.go @@ -119,12 +119,12 @@ func (f *CallFlag) UnmarshalJSON(data []byte) error { } // MarshalYAML implements the YAML marshaler interface. -func (f CallFlag) MarshalYAML() (interface{}, error) { +func (f CallFlag) MarshalYAML() (any, error) { return f.String(), nil } // UnmarshalYAML implements the YAML unmarshaler interface. -func (f *CallFlag) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (f *CallFlag) UnmarshalYAML(unmarshal func(any) error) error { var s string err := unmarshal(&s) diff --git a/pkg/smartcontract/doc_test.go b/pkg/smartcontract/doc_test.go index 39c76dbfb..fe54126be 100644 --- a/pkg/smartcontract/doc_test.go +++ b/pkg/smartcontract/doc_test.go @@ -34,17 +34,17 @@ func ExampleBuilder() { // Actor has an Invoker inside, so we can perform test invocation using the script. res, _ := a.Run(script) if res.State != "HALT" || len(res.Stack) != 2 { - // The script failed completely or didn't return proper number of return values. + panic("failed") // The script failed completely or didn't return proper number of return values. } transferResult, _ := res.Stack[0].TryBool() voteResult, _ := res.Stack[1].TryBool() if !transferResult { - // Transfer failed. + panic("transfer failed") } if !voteResult { - // Vote failed. + panic("vote failed") } b.Reset() // Copy the old script above if you need it! diff --git a/pkg/smartcontract/entry.go b/pkg/smartcontract/entry.go index c6f6b211a..99caed055 100644 --- a/pkg/smartcontract/entry.go +++ b/pkg/smartcontract/entry.go @@ -18,7 +18,7 @@ import ( // processed this way (and this number can't exceed VM limits), the result of the // script is an array containing extracted value elements. This script can be useful // for interactions with RPC server that have iterator sessions disabled. -func CreateCallAndUnwrapIteratorScript(contract util.Uint160, operation string, maxIteratorResultItems int, params ...interface{}) ([]byte, error) { +func CreateCallAndUnwrapIteratorScript(contract util.Uint160, operation string, maxIteratorResultItems int, params ...any) ([]byte, error) { script := io.NewBufBinWriter() emit.Int(script.BinWriter, int64(maxIteratorResultItems)) emit.AppCall(script.BinWriter, contract, operation, callflag.All, params...) // The System.Contract.Call itself, it will push Iterator on estack. @@ -71,7 +71,7 @@ func CreateCallAndUnwrapIteratorScript(contract util.Uint160, operation string, // CreateCallScript returns a script that calls contract's method with // the specified parameters. Whatever this method returns remains on the stack. // See also (*Builder).InvokeMethod. -func CreateCallScript(contract util.Uint160, method string, params ...interface{}) ([]byte, error) { +func CreateCallScript(contract util.Uint160, method string, params ...any) ([]byte, error) { b := NewBuilder() b.InvokeMethod(contract, method, params...) return b.Script() @@ -80,7 +80,7 @@ func CreateCallScript(contract util.Uint160, method string, params ...interface{ // CreateCallWithAssertScript returns a script that calls contract's method with // the specified parameters expecting a Boolean value to be return that then is // used for ASSERT. See also (*Builder).InvokeWithAssert. -func CreateCallWithAssertScript(contract util.Uint160, method string, params ...interface{}) ([]byte, error) { +func CreateCallWithAssertScript(contract util.Uint160, method string, params ...any) ([]byte, error) { b := NewBuilder() b.InvokeWithAssert(contract, method, params...) return b.Script() diff --git a/pkg/smartcontract/manifest/manifest.go b/pkg/smartcontract/manifest/manifest.go index ba391a657..c4e88e9b0 100644 --- a/pkg/smartcontract/manifest/manifest.go +++ b/pkg/smartcontract/manifest/manifest.go @@ -182,7 +182,7 @@ func extraToStackItem(rawExtra []byte) stackitem.Item { // Prevent accidental precision loss. d.UseNumber() - var obj interface{} + var obj any // The error can't really occur because `json.RawMessage` is already a valid json. _ = d.Decode(&obj) diff --git a/pkg/smartcontract/manifest/permission.go b/pkg/smartcontract/manifest/permission.go index 0859871db..260c73b14 100644 --- a/pkg/smartcontract/manifest/permission.go +++ b/pkg/smartcontract/manifest/permission.go @@ -27,7 +27,7 @@ const ( // PermissionDesc is a permission descriptor. type PermissionDesc struct { Type PermissionType - Value interface{} + Value any } // Permission describes which contracts may be invoked and which methods are called. @@ -45,13 +45,13 @@ type permissionAux struct { } // NewPermission returns a new permission of the given type. -func NewPermission(typ PermissionType, args ...interface{}) *Permission { +func NewPermission(typ PermissionType, args ...any) *Permission { return &Permission{ Contract: *newPermissionDesc(typ, args...), } } -func newPermissionDesc(typ PermissionType, args ...interface{}) *PermissionDesc { +func newPermissionDesc(typ PermissionType, args ...any) *PermissionDesc { desc := &PermissionDesc{Type: typ} switch typ { case PermissionWildcard: diff --git a/pkg/smartcontract/manifest/permission_test.go b/pkg/smartcontract/manifest/permission_test.go index 1016bb542..16fa923cc 100644 --- a/pkg/smartcontract/manifest/permission_test.go +++ b/pkg/smartcontract/manifest/permission_test.go @@ -144,7 +144,7 @@ func TestPermissionDesc_MarshalJSON(t *testing.T) { }) } -func testMarshalUnmarshal(t *testing.T, expected, actual interface{}) { +func testMarshalUnmarshal(t *testing.T, expected, actual any) { data, err := json.Marshal(expected) require.NoError(t, err) require.NoError(t, json.Unmarshal(data, actual)) diff --git a/pkg/smartcontract/manifest/standard/comply_test.go b/pkg/smartcontract/manifest/standard/comply_test.go index 360a7c1de..c867411f6 100644 --- a/pkg/smartcontract/manifest/standard/comply_test.go +++ b/pkg/smartcontract/manifest/standard/comply_test.go @@ -1,7 +1,6 @@ package standard import ( - "errors" "testing" "github.com/nspcc-dev/neo-go/pkg/smartcontract" @@ -39,14 +38,14 @@ func TestComplyMissingMethod(t *testing.T) { m := fooMethodBarEvent() m.ABI.GetMethod("foo", -1).Name = "notafoo" err := Comply(m, &Standard{Manifest: *fooMethodBarEvent()}) - require.True(t, errors.Is(err, ErrMethodMissing)) + require.ErrorIs(t, err, ErrMethodMissing) } func TestComplyInvalidReturnType(t *testing.T) { m := fooMethodBarEvent() m.ABI.GetMethod("foo", -1).ReturnType = smartcontract.VoidType err := Comply(m, &Standard{Manifest: *fooMethodBarEvent()}) - require.True(t, errors.Is(err, ErrInvalidReturnType)) + require.ErrorIs(t, err, ErrInvalidReturnType) } func TestComplyMethodParameterCount(t *testing.T) { @@ -55,14 +54,14 @@ func TestComplyMethodParameterCount(t *testing.T) { f := m.ABI.GetMethod("foo", -1) f.Parameters = append(f.Parameters, manifest.Parameter{Type: smartcontract.BoolType}) err := Comply(m, &Standard{Manifest: *fooMethodBarEvent()}) - require.True(t, errors.Is(err, ErrMethodMissing)) + require.ErrorIs(t, err, ErrMethodMissing) }) t.Run("Event", func(t *testing.T) { m := fooMethodBarEvent() ev := m.ABI.GetEvent("bar") ev.Parameters = ev.Parameters[:0] err := Comply(m, &Standard{Manifest: *fooMethodBarEvent()}) - require.True(t, errors.Is(err, ErrInvalidParameterCount)) + require.ErrorIs(t, err, ErrInvalidParameterCount) }) } @@ -71,13 +70,13 @@ func TestComplyParameterType(t *testing.T) { m := fooMethodBarEvent() m.ABI.GetMethod("foo", -1).Parameters[0].Type = smartcontract.InteropInterfaceType err := Comply(m, &Standard{Manifest: *fooMethodBarEvent()}) - require.True(t, errors.Is(err, ErrInvalidParameterType)) + require.ErrorIs(t, err, ErrInvalidParameterType) }) t.Run("Event", func(t *testing.T) { m := fooMethodBarEvent() m.ABI.GetEvent("bar").Parameters[0].Type = smartcontract.InteropInterfaceType err := Comply(m, &Standard{Manifest: *fooMethodBarEvent()}) - require.True(t, errors.Is(err, ErrInvalidParameterType)) + require.ErrorIs(t, err, ErrInvalidParameterType) }) } @@ -87,7 +86,7 @@ func TestComplyParameterName(t *testing.T) { m.ABI.GetMethod("foo", -1).Parameters[0].Name = "hehe" s := &Standard{Manifest: *fooMethodBarEvent()} err := Comply(m, s) - require.True(t, errors.Is(err, ErrInvalidParameterName)) + require.ErrorIs(t, err, ErrInvalidParameterName) require.NoError(t, ComplyABI(m, s)) }) t.Run("Event", func(t *testing.T) { @@ -95,7 +94,7 @@ func TestComplyParameterName(t *testing.T) { m.ABI.GetEvent("bar").Parameters[0].Name = "hehe" s := &Standard{Manifest: *fooMethodBarEvent()} err := Comply(m, s) - require.True(t, errors.Is(err, ErrInvalidParameterName)) + require.ErrorIs(t, err, ErrInvalidParameterName) require.NoError(t, ComplyABI(m, s)) }) } @@ -104,14 +103,14 @@ func TestMissingEvent(t *testing.T) { m := fooMethodBarEvent() m.ABI.GetEvent("bar").Name = "notabar" err := Comply(m, &Standard{Manifest: *fooMethodBarEvent()}) - require.True(t, errors.Is(err, ErrEventMissing)) + require.ErrorIs(t, err, ErrEventMissing) } func TestSafeFlag(t *testing.T) { m := fooMethodBarEvent() m.ABI.GetMethod("foo", -1).Safe = false err := Comply(m, &Standard{Manifest: *fooMethodBarEvent()}) - require.True(t, errors.Is(err, ErrSafeMethodMismatch)) + require.ErrorIs(t, err, ErrSafeMethodMismatch) } func TestComplyValid(t *testing.T) { diff --git a/pkg/smartcontract/nef/method_token_test.go b/pkg/smartcontract/nef/method_token_test.go index 05072723d..b293ff3d3 100644 --- a/pkg/smartcontract/nef/method_token_test.go +++ b/pkg/smartcontract/nef/method_token_test.go @@ -1,7 +1,6 @@ package nef import ( - "errors" "strings" "testing" @@ -37,7 +36,7 @@ func TestMethodToken_Serializable(t *testing.T) { data, err := testserdes.EncodeBinary(tok) require.NoError(t, err) err = testserdes.DecodeBinary(data, new(MethodToken)) - require.True(t, errors.Is(err, errInvalidMethodName)) + require.ErrorIs(t, err, errInvalidMethodName) }) t.Run("invalid call flag", func(t *testing.T) { tok := getToken() @@ -45,6 +44,6 @@ func TestMethodToken_Serializable(t *testing.T) { data, err := testserdes.EncodeBinary(tok) require.NoError(t, err) err = testserdes.DecodeBinary(data, new(MethodToken)) - require.True(t, errors.Is(err, errInvalidCallFlag)) + require.ErrorIs(t, err, errInvalidCallFlag) }) } diff --git a/pkg/smartcontract/nef/nef_test.go b/pkg/smartcontract/nef/nef_test.go index f265d2ad7..133513276 100644 --- a/pkg/smartcontract/nef/nef_test.go +++ b/pkg/smartcontract/nef/nef_test.go @@ -3,7 +3,6 @@ package nef import ( "encoding/base64" "encoding/json" - "errors" "strconv" "testing" @@ -87,12 +86,12 @@ func TestEncodeDecodeBinary(t *testing.T) { sz := io.GetVarSize(&expected.Header) bytes[sz] = 1 err = testserdes.DecodeBinary(bytes, new(File)) - require.True(t, errors.Is(err, errInvalidReserved), "got: %v", err) + require.ErrorIs(t, err, errInvalidReserved) bytes[sz] = 0 bytes[sz+3] = 1 err = testserdes.DecodeBinary(bytes, new(File)) - require.True(t, errors.Is(err, errInvalidReserved), "got: %v", err) + require.ErrorIs(t, err, errInvalidReserved) }) } diff --git a/pkg/smartcontract/param_type.go b/pkg/smartcontract/param_type.go index c6d640ae5..403a48649 100644 --- a/pkg/smartcontract/param_type.go +++ b/pkg/smartcontract/param_type.go @@ -123,12 +123,12 @@ func (pt *ParamType) UnmarshalJSON(data []byte) error { } // MarshalYAML implements the YAML Marshaler interface. -func (pt ParamType) MarshalYAML() (interface{}, error) { +func (pt ParamType) MarshalYAML() (any, error) { return pt.String(), nil } // UnmarshalYAML implements the YAML Unmarshaler interface. -func (pt *ParamType) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (pt *ParamType) UnmarshalYAML(unmarshal func(any) error) error { var name string err := unmarshal(&name) @@ -275,7 +275,7 @@ func ParseParamType(typ string) (ParamType, error) { } // adjustValToType is a value type-checker and converter. -func adjustValToType(typ ParamType, val string) (interface{}, error) { +func adjustValToType(typ ParamType, val string) (any, error) { switch typ { case SignatureType: b, err := hex.DecodeString(val) diff --git a/pkg/smartcontract/param_type_test.go b/pkg/smartcontract/param_type_test.go index 88ac51fd6..9d3e51d2f 100644 --- a/pkg/smartcontract/param_type_test.go +++ b/pkg/smartcontract/param_type_test.go @@ -171,7 +171,7 @@ func TestAdjustValToType(t *testing.T) { var inouts = []struct { typ ParamType val string - out interface{} + out any err bool }{{ typ: SignatureType, diff --git a/pkg/smartcontract/parameter.go b/pkg/smartcontract/parameter.go index c591f5f0d..758b86bd1 100644 --- a/pkg/smartcontract/parameter.go +++ b/pkg/smartcontract/parameter.go @@ -22,7 +22,7 @@ type Parameter struct { // Type of the parameter. Type ParamType `json:"type"` // The actual value of the parameter. - Value interface{} `json:"value"` + Value any `json:"value"` } // ParameterPair represents a key-value pair, a slice of which is stored in @@ -259,7 +259,7 @@ func NewParameterFromString(in string) (*Parameter, error) { // the value if needed. It does not copy the value if it can avoid doing so. All // regular integers, util.*, keys.PublicKey*, string and bool types are supported, // slice of byte slices is accepted and converted as well. -func NewParameterFromValue(value interface{}) (Parameter, error) { +func NewParameterFromValue(value any) (Parameter, error) { var result = Parameter{ Value: value, } @@ -353,7 +353,7 @@ func NewParameterFromValue(value interface{}) (Parameter, error) { } result.Type = ArrayType result.Value = arr - case []interface{}: + case []any: arr, err := NewParametersFromValues(v...) if err != nil { return result, err @@ -371,7 +371,7 @@ func NewParameterFromValue(value interface{}) (Parameter, error) { // NewParametersFromValues is similar to NewParameterFromValue except that it // works with multiple values and returns a simple slice of Parameter. -func NewParametersFromValues(values ...interface{}) ([]Parameter, error) { +func NewParametersFromValues(values ...any) ([]Parameter, error) { res := make([]Parameter, 0, len(values)) for i := range values { elem, err := NewParameterFromValue(values[i]) @@ -386,12 +386,12 @@ func NewParametersFromValues(values ...interface{}) ([]Parameter, error) { // ExpandParameterToEmitable converts a parameter to a type which can be handled as // an array item by emit.Array. It correlates with the way an RPC server handles // FuncParams for invoke* calls inside the request.ExpandArrayIntoScript function. -func ExpandParameterToEmitable(param Parameter) (interface{}, error) { +func ExpandParameterToEmitable(param Parameter) (any, error) { var err error switch t := param.Type; t { case ArrayType: arr := param.Value.([]Parameter) - res := make([]interface{}, len(arr)) + res := make([]any, len(arr)) for i := range arr { res[i], err = ExpandParameterToEmitable(arr[i]) if err != nil { diff --git a/pkg/smartcontract/parameter_test.go b/pkg/smartcontract/parameter_test.go index 2ec7a0108..c93179a2a 100644 --- a/pkg/smartcontract/parameter_test.go +++ b/pkg/smartcontract/parameter_test.go @@ -452,7 +452,7 @@ func TestExpandParameterToEmitableToStackitem(t *testing.T) { pk, _ := keys.NewPrivateKey() testCases := []struct { In Parameter - Expected interface{} + Expected any ExpectedStackitem stackitem.Item }{ { @@ -520,7 +520,7 @@ func TestExpandParameterToEmitableToStackitem(t *testing.T) { }, }, }}, - Expected: []interface{}{big.NewInt(123), []byte{1, 2, 3}, []interface{}{true}}, + Expected: []any{big.NewInt(123), []byte{1, 2, 3}, []any{true}}, ExpectedStackitem: stackitem.NewArray([]stackitem.Item{ stackitem.NewBigInteger(big.NewInt(123)), stackitem.NewByteArray([]byte{1, 2, 3}), @@ -558,9 +558,9 @@ func TestParameterFromValue(t *testing.T) { pk1, _ := keys.NewPrivateKey() pk2, _ := keys.NewPrivateKey() items := []struct { - value interface{} + value any expType ParamType - expVal interface{} + expVal any }{ { value: []byte{1, 2, 3}, @@ -728,7 +728,7 @@ func TestParameterFromValue(t *testing.T) { }}, }, { - value: []interface{}{-42, "random", []byte{1, 2, 3}}, + value: []any{-42, "random", []byte{1, 2, 3}}, expType: ArrayType, expVal: []Parameter{{ Type: IntegerType, @@ -753,7 +753,7 @@ func TestParameterFromValue(t *testing.T) { } _, err := NewParameterFromValue(make(map[string]int)) require.Error(t, err) - _, err = NewParameterFromValue([]interface{}{1, 2, make(map[string]int)}) + _, err = NewParameterFromValue([]any{1, 2, make(map[string]int)}) require.Error(t, err) } diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index b8f53d91a..697682b63 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -5,15 +5,30 @@ import ( "sort" "strings" "text/template" + "unicode" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/binding" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest/standard" + "github.com/nspcc-dev/neo-go/pkg/util" ) -const srcTmpl = ` -{{- define "SAFEMETHOD" -}} +// The set of constants containing parts of RPC binding template. Each block of code +// including template definition and var/type/method definitions contain new line at the +// start and ends with a new line. On adding new block of code to the template, please, +// ensure that this block has new line at the start and in the end of the block. +const ( + eventDefinition = `{{ define "EVENT" }} +// {{.Name}} represents "{{.ManifestName}}" event emitted by the contract. +type {{.Name}} struct { + {{- range $index, $arg := .Parameters}} + {{.Name}} {{.Type}} + {{- end}} +} +{{ end }}` + + safemethodDefinition = `{{ define "SAFEMETHOD" }} // {{.Name}} {{.Comment}} func (c *ContractReader) {{.Name}}({{range $index, $arg := .Arguments -}} {{- if ne $index 0}}, {{end}} @@ -25,42 +40,40 @@ func (c *ContractReader) {{.Name}}({{range $index, $arg := .Arguments -}} } return {{addIndent (etTypeConverter .ExtendedReturn "item") "\t"}} } ( {{- end -}} {{if .ItemTo -}} itemTo{{ .ItemTo }}( {{- end -}} - unwrap.{{.Unwrapper}}(c.invoker.Call(Hash, "{{ .NameABI }}" + unwrap.{{.Unwrapper}}(c.invoker.Call(c.hash, "{{ .NameABI }}" {{- range $arg := .Arguments -}}, {{.Name}}{{end -}} )) {{- if or .ItemTo (eq .Unwrapper "Item") -}} ) {{- end}} {{- else -}} (*result.Invoke, error) { - c.invoker.Call(Hash, "{{ .NameABI }}" + c.invoker.Call(c.hash, "{{ .NameABI }}" {{- range $arg := .Arguments -}}, {{.Name}}{{end}}) {{- end}} } -{{- if eq .Unwrapper "SessionIterator"}} - +{{ if eq .Unwrapper "SessionIterator" }} // {{.Name}}Expanded is similar to {{.Name}} (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) {{.Name}}Expanded({{range $index, $arg := .Arguments}}{{.Name}} {{.Type}}, {{end}}_numOfIteratorItems int) ([]stackitem.Item, error) { - return unwrap.Array(c.invoker.CallAndExpandIterator(Hash, "{{.NameABI}}", _numOfIteratorItems{{range $arg := .Arguments}}, {{.Name}}{{end}})) + return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "{{.NameABI}}", _numOfIteratorItems{{range $arg := .Arguments}}, {{.Name}}{{end}})) } -{{- end -}} -{{- end -}} -{{- define "METHOD" -}} -{{- if eq .ReturnType "bool"}}func scriptFor{{.Name}}({{range $index, $arg := .Arguments -}} +{{ end }}{{ end }}` + methodDefinition = `{{ define "METHOD" }}{{ if eq .ReturnType "bool"}} +func (c *Contract) scriptFor{{.Name}}({{range $index, $arg := .Arguments -}} {{- if ne $index 0}}, {{end}} {{- .Name}} {{.Type}} {{- end}}) ([]byte, error) { - return smartcontract.CreateCallWithAssertScript(Hash, "{{ .NameABI }}"{{- range $index, $arg := .Arguments -}}, {{.Name}}{{end}}) + return smartcontract.CreateCallWithAssertScript(c.hash, "{{ .NameABI }}"{{- range $index, $arg := .Arguments -}}, {{.Name}}{{end}}) } - -{{end}}// {{.Name}} {{.Comment}} +{{ end }} +// {{.Name}} {{.Comment}} // 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) {{.Name}}({{range $index, $arg := .Arguments -}} {{- if ne $index 0}}, {{end}} {{- .Name}} {{.Type}} {{- end}}) (util.Uint256, uint32, error) { - {{if ne .ReturnType "bool"}}return c.actor.SendCall(Hash, "{{ .NameABI }}" - {{- range $index, $arg := .Arguments -}}, {{.Name}}{{end}}){{else}}script, err := scriptFor{{.Name}}({{- range $index, $arg := .Arguments -}}{{- if ne $index 0}}, {{end}}{{.Name}}{{end}}) + {{if ne .ReturnType "bool"}}return c.actor.SendCall(c.hash, "{{ .NameABI }}" + {{- range $index, $arg := .Arguments -}}, {{.Name}}{{end}}){{else}}script, err := c.scriptFor{{.Name}}({{- range $index, $arg := .Arguments -}}{{- if ne $index 0}}, {{end}}{{.Name}}{{end}}) if err != nil { return util.Uint256{}, 0, err } @@ -74,8 +87,8 @@ func (c *Contract) {{.Name}}Transaction({{range $index, $arg := .Arguments -}} {{- if ne $index 0}}, {{end}} {{- .Name}} {{.Type}} {{- end}}) (*transaction.Transaction, error) { - {{if ne .ReturnType "bool"}}return c.actor.MakeCall(Hash, "{{ .NameABI }}" - {{- range $index, $arg := .Arguments -}}, {{.Name}}{{end}}){{else}}script, err := scriptFor{{.Name}}({{- range $index, $arg := .Arguments -}}{{- if ne $index 0}}, {{end}}{{.Name}}{{end}}) + {{if ne .ReturnType "bool"}}return c.actor.MakeCall(c.hash, "{{ .NameABI }}" + {{- range $index, $arg := .Arguments -}}, {{.Name}}{{end}}){{else}}script, err := c.scriptFor{{.Name}}({{- range $index, $arg := .Arguments -}}{{- if ne $index 0}}, {{end}}{{.Name}}{{end}}) if err != nil { return nil, err } @@ -90,48 +103,51 @@ func (c *Contract) {{.Name}}Unsigned({{range $index, $arg := .Arguments -}} {{- if ne $index 0}}, {{end}} {{- .Name}} {{.Type}} {{- end}}) (*transaction.Transaction, error) { - {{if ne .ReturnType "bool"}}return c.actor.MakeUnsignedCall(Hash, "{{ .NameABI }}", nil - {{- range $index, $arg := .Arguments -}}, {{.Name}}{{end}}){{else}}script, err := scriptFor{{.Name}}({{- range $index, $arg := .Arguments -}}{{- if ne $index 0}}, {{end}}{{.Name}}{{end}}) + {{if ne .ReturnType "bool"}}return c.actor.MakeUnsignedCall(c.hash, "{{ .NameABI }}", nil + {{- range $index, $arg := .Arguments -}}, {{.Name}}{{end}}){{else}}script, err := c.scriptFor{{.Name}}({{- range $index, $arg := .Arguments -}}{{- if ne $index 0}}, {{end}}{{.Name}}{{end}}) if err != nil { return nil, err } return c.actor.MakeUnsignedRun(script, nil){{end}} } -{{- end -}} -// Package {{.PackageName}} contains RPC wrappers for {{.ContractName}} contract. +{{end}}` + + bindingDefinition = `// Package {{.PackageName}} contains RPC wrappers for {{.ContractName}} contract. package {{.PackageName}} import ( {{range $m := .Imports}} "{{ $m }}" {{end}}) - +{{if len .Hash}} // Hash contains contract hash. var Hash = {{ .Hash }} - -{{range $name, $typ := .NamedTypes}} +{{end -}} +{{- range $name, $typ := .NamedTypes }} // {{toTypeName $name}} is a contract-specific {{$name}} type used by its methods. type {{toTypeName $name}} struct { {{- range $m := $typ.Fields}} {{.Field}} {{etTypeToStr .ExtendedType}} {{- end}} } -{{end -}} -{{if .HasReader}}// Invoker is used by ContractReader to call various safe methods. +{{end}} +{{- range $e := .CustomEvents }}{{template "EVENT" $e }}{{ end -}} +{{- if .HasReader}} +// Invoker is used by ContractReader to call various safe methods. type Invoker interface { {{if or .IsNep11D .IsNep11ND}} nep11.Invoker {{else -}} {{ if .IsNep17}} nep17.Invoker -{{else if len .SafeMethods}} Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) +{{else if len .SafeMethods}} Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) {{end -}} -{{if .HasIterator}} CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...interface{}) (*result.Invoke, error) +{{if .HasIterator}} 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) {{end -}} {{end -}} } - {{end -}} -{{if .HasWriter}}// Actor is used by Contract to call state-changing methods. +{{- if .HasWriter}} +// Actor is used by Contract to call state-changing methods. type Actor interface { {{- if .HasReader}} Invoker @@ -142,17 +158,17 @@ type Actor interface { nep17.Actor {{end}} {{- if len .Methods}} - MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) + 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 ...interface{}) (*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 ...interface{}) (util.Uint256, uint32, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) SendRun(script []byte) (util.Uint256, uint32, error) {{end -}} } - {{end -}} -{{if .HasReader}}// ContractReader implements safe contract methods. +{{- if .HasReader}} +// ContractReader implements safe contract methods. type ContractReader struct { {{if .IsNep11D}}nep11.DivisibleReader {{end -}} @@ -161,10 +177,11 @@ type ContractReader struct { {{if .IsNep17}}nep17.TokenReader {{end -}} invoker Invoker + hash util.Uint160 } - {{end -}} -{{if .HasWriter}}// Contract implements all contract methods. +{{- if .HasWriter}} +// Contract implements all contract methods. type Contract struct { {{if .HasReader}}ContractReader {{end -}} @@ -175,80 +192,154 @@ type Contract struct { {{if .IsNep17}}nep17.TokenWriter {{end -}} actor Actor + hash util.Uint160 } - {{end -}} -{{if .HasReader}}// NewReader creates an instance of ContractReader using Hash and the given Invoker. -func NewReader(invoker Invoker) *ContractReader { +{{- if .HasReader}} +// NewReader creates an instance of ContractReader using {{if len .Hash -}}Hash{{- else -}}provided contract hash{{- end}} and the given Invoker. +func NewReader(invoker Invoker{{- if not (len .Hash) -}}, hash util.Uint160{{- end -}}) *ContractReader { + {{if len .Hash -}} + var hash = Hash + {{end -}} return &ContractReader{ - {{- if .IsNep11D}}*nep11.NewDivisibleReader(invoker, Hash), {{end}} - {{- if .IsNep11ND}}*nep11.NewNonDivisibleReader(invoker, Hash), {{end}} - {{- if .IsNep17}}*nep17.NewReader(invoker, Hash), {{end -}} - invoker} + {{- if .IsNep11D}}*nep11.NewDivisibleReader(invoker, hash), {{end}} + {{- if .IsNep11ND}}*nep11.NewNonDivisibleReader(invoker, hash), {{end}} + {{- if .IsNep17}}*nep17.NewReader(invoker, hash), {{end -}} + invoker, hash} } - {{end -}} -{{if .HasWriter}}// New creates an instance of Contract using Hash and the given Actor. -func New(actor Actor) *Contract { - {{if .IsNep11D}}var nep11dt = nep11.NewDivisible(actor, Hash) +{{- if .HasWriter}} +// New creates an instance of Contract using {{if len .Hash -}}Hash{{- else -}}provided contract hash{{- end}} and the given Actor. +func New(actor Actor{{- if not (len .Hash) -}}, hash util.Uint160{{- end -}}) *Contract { + {{if len .Hash -}} + var hash = Hash {{end -}} - {{if .IsNep11ND}}var nep11ndt = nep11.NewNonDivisible(actor, Hash) + {{if .IsNep11D}}var nep11dt = nep11.NewDivisible(actor, hash) {{end -}} - {{if .IsNep17}}var nep17t = nep17.New(actor, Hash) + {{if .IsNep11ND}}var nep11ndt = nep11.NewNonDivisible(actor, hash) + {{end -}} + {{if .IsNep17}}var nep17t = nep17.New(actor, hash) {{end -}} return &Contract{ {{- if .HasReader}}ContractReader{ {{- if .IsNep11D}}nep11dt.DivisibleReader, {{end -}} {{- if .IsNep11ND}}nep11ndt.NonDivisibleReader, {{end -}} {{- if .IsNep17}}nep17t.TokenReader, {{end -}} - actor}, {{end -}} + actor, hash}, {{end -}} {{- if .IsNep11D}}nep11dt.DivisibleWriter, {{end -}} {{- if .IsNep11ND}}nep11ndt.BaseWriter, {{end -}} {{- if .IsNep17}}nep17t.TokenWriter, {{end -}} - actor} + actor, hash} } - {{end -}} -{{range $m := .SafeMethods}} -{{template "SAFEMETHOD" $m }} -{{end}} -{{- range $m := .Methods}} -{{template "METHOD" $m }} -{{end}} -{{- range $name, $typ := .NamedTypes}} +{{- range $m := .SafeMethods }}{{template "SAFEMETHOD" $m }}{{ end -}} +{{- range $m := .Methods -}}{{template "METHOD" $m }}{{ end -}} +{{- range $name, $typ := .NamedTypes }} // itemTo{{toTypeName $name}} converts stack item into *{{toTypeName $name}}. func itemTo{{toTypeName $name}}(item stackitem.Item, err error) (*{{toTypeName $name}}, error) { if err != nil { return nil, err } + var res = new({{toTypeName $name}}) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of {{toTypeName $name}} from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +func (res *{{toTypeName $name}}) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != {{len $typ.Fields}} { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - - var res = new({{toTypeName $name}}) -{{if len .Fields}} var index = -1 +{{if len .Fields}} + var ( + index = -1 + err error + ) {{- range $m := $typ.Fields}} index++ res.{{.Field}}, err = {{etTypeConverter .ExtendedType "arr[index]"}} if err != nil { - return nil, fmt.Errorf("field {{.Field}}: %w", err) + return fmt.Errorf("field {{.Field}}: %w", err) } {{end}} -{{end}} - return res, err +{{- end}} + return nil } -{{end}}` +{{ end -}} +{{- range $e := .CustomEvents }} +// {{$e.Name}}sFromApplicationLog retrieves a set of all emitted events +// with "{{$e.ManifestName}}" name from the provided [result.ApplicationLog]. +func {{$e.Name}}sFromApplicationLog(log *result.ApplicationLog) ([]*{{$e.Name}}, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*{{$e.Name}} + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "{{$e.ManifestName}}" { + continue + } + event := new({{$e.Name}}) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize {{$e.Name}} from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to {{$e.Name}} or +// returns an error if it's not possible to do to so. +func (e *{{$e.Name}}) 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) != {{len $e.Parameters}} { + return errors.New("wrong number of structure elements") + } + + {{if len $e.Parameters}}var ( + index = -1 + err error + ) + {{- range $p := $e.Parameters}} + index++ + e.{{.Name}}, err = {{etTypeConverter .ExtType "arr[index]"}} + if err != nil { + return fmt.Errorf("field {{.Name}}: %w", err) + } +{{end}} +{{- end}} + return nil +} +{{end -}}` + + srcTmpl = bindingDefinition + + eventDefinition + + safemethodDefinition + + methodDefinition +) type ( ContractTmpl struct { binding.ContractTmpl - SafeMethods []SafeMethodTmpl - NamedTypes map[string]binding.ExtendedType + SafeMethods []SafeMethodTmpl + CustomEvents []CustomEventTemplate + NamedTypes map[string]binding.ExtendedType IsNep11D bool IsNep11ND bool @@ -265,6 +356,25 @@ type ( ItemTo string ExtendedReturn binding.ExtendedType } + + CustomEventTemplate struct { + // Name is the event's name that will be used as the event structure name in + // the resulting RPC binding. It is a valid go structure name and may differ + // from ManifestName. + Name string + // ManifestName is the event's name declared in the contract manifest. + // It may contain any UTF8 character. + ManifestName string + Parameters []EventParamTmpl + } + + EventParamTmpl struct { + binding.ParamTmpl + + // ExtType holds the event parameter's type information provided by Manifest, + // i.e. simple types only. + ExtType binding.ExtendedType + } ) // NewConfig initializes and returns a new config instance. @@ -296,12 +406,14 @@ func Generate(cfg binding.Config) error { mfst.ABI.Methods = dropStdMethods(mfst.ABI.Methods, standard.Nep11NonDivisible) ctr.IsNep11ND = true } + mfst.ABI.Events = dropStdEvents(mfst.ABI.Events, standard.Nep11Base) break // Can't be NEP-17 at the same time. } if std == manifest.NEP17StandardName && standard.ComplyABI(cfg.Manifest, standard.Nep17) == nil { mfst.ABI.Methods = dropStdMethods(mfst.ABI.Methods, standard.Nep17) imports["github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"] = struct{}{} ctr.IsNep17 = true + mfst.ABI.Events = dropStdEvents(mfst.ABI.Events, standard.Nep17) break // Can't be NEP-11 at the same time. } } @@ -315,7 +427,7 @@ func Generate(cfg binding.Config) error { } ctr.ContractTmpl = binding.TemplateFromManifest(cfg, scTypeToGo) - ctr = scTemplateToRPC(cfg, ctr, imports) + ctr = scTemplateToRPC(cfg, ctr, imports, scTypeToGo) ctr.NamedTypes = cfg.NamedTypes var srcTemplate = template.Must(template.New("generate").Funcs(template.FuncMap{ @@ -344,6 +456,18 @@ func dropManifestMethods(meths []manifest.Method, manifested []manifest.Method) return meths } +func dropManifestEvents(events []manifest.Event, manifested []manifest.Event) []manifest.Event { + for _, e := range manifested { + for i := 0; i < len(events); i++ { + if events[i].Name == e.Name && len(events[i].Parameters) == len(e.Parameters) { + events = append(events[:i], events[i+1:]...) + i-- + } + } + } + return events +} + func dropStdMethods(meths []manifest.Method, std *standard.Standard) []manifest.Method { meths = dropManifestMethods(meths, std.Manifest.ABI.Methods) if std.Optional != nil { @@ -355,10 +479,18 @@ func dropStdMethods(meths []manifest.Method, std *standard.Standard) []manifest. return meths } +func dropStdEvents(events []manifest.Event, std *standard.Standard) []manifest.Event { + events = dropManifestEvents(events, std.Manifest.ABI.Events) + if std.Base != nil { + return dropStdEvents(events, std.Base) + } + return events +} + func extendedTypeToGo(et binding.ExtendedType, named map[string]binding.ExtendedType) (string, string) { switch et.Base { case smartcontract.AnyType: - return "interface{}", "" + return "any", "" case smartcontract.BoolType: return "bool", "" case smartcontract.IntegerType: @@ -385,14 +517,19 @@ func extendedTypeToGo(et binding.ExtendedType, named map[string]binding.Extended sub, pkg := extendedTypeToGo(*et.Value, named) return "[]" + sub, pkg } - return "[]interface{}", "" + return "[]any", "" case smartcontract.MapType: kt, _ := extendedTypeToGo(binding.ExtendedType{Base: et.Key}, named) - vt, _ := extendedTypeToGo(*et.Value, named) + var vt string + if et.Value != nil { + vt, _ = extendedTypeToGo(*et.Value, named) + } else { + vt = "any" + } return "map[" + kt + "]" + vt, "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" case smartcontract.InteropInterfaceType: - return "interface{}", "" + return "any", "" case smartcontract.VoidType: return "", "" } @@ -402,7 +539,7 @@ func extendedTypeToGo(et binding.ExtendedType, named map[string]binding.Extended func etTypeConverter(et binding.ExtendedType, v string) string { switch et.Base { case smartcontract.AnyType: - return v + ".Value(), nil" + return v + ".Value(), error(nil)" case smartcontract.BoolType: return v + ".TryBool()" case smartcontract.IntegerType: @@ -484,8 +621,9 @@ func etTypeConverter(et binding.ExtendedType, v string) string { }, v) case smartcontract.MapType: - at, _ := extendedTypeToGo(et, nil) - return `func (item stackitem.Item) (` + at + `, error) { + if et.Value != nil { + at, _ := extendedTypeToGo(et, nil) + return `func (item stackitem.Item) (` + at + `, error) { m, ok := item.Value().([]stackitem.MapElement) if !ok { return nil, fmt.Errorf("%s is not a map", item.Type().String()) @@ -504,6 +642,14 @@ func etTypeConverter(et binding.ExtendedType, v string) string { } return res, nil } (` + v + `)` + } + return etTypeConverter(binding.ExtendedType{ + Base: smartcontract.MapType, + Key: et.Key, + Value: &binding.ExtendedType{ + Base: smartcontract.AnyType, + }, + }, v) case smartcontract.InteropInterfaceType: return "item.Value(), nil" case smartcontract.VoidType: @@ -520,11 +666,13 @@ func scTypeToGo(name string, typ smartcontract.ParamType, cfg *binding.Config) ( return extendedTypeToGo(et, cfg.NamedTypes) } -func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]struct{}) ContractTmpl { +func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]struct{}, scTypeConverter func(string, smartcontract.ParamType, *binding.Config) (string, string)) ContractTmpl { for i := range ctr.Imports { imports[ctr.Imports[i]] = struct{}{} } - ctr.Hash = fmt.Sprintf("%#v", cfg.Hash) + if !cfg.Hash.Equals(util.Uint160{}) { + ctr.Hash = fmt.Sprintf("%#v", cfg.Hash) + } for i := 0; i < len(ctr.Methods); i++ { abim := cfg.Manifest.ABI.GetMethod(ctr.Methods[i].NameABI, len(ctr.Methods[i].Arguments)) if abim.Safe { @@ -551,10 +699,51 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st if len(cfg.NamedTypes) > 0 { imports["errors"] = struct{}{} } + for _, abiEvent := range cfg.Manifest.ABI.Events { + eBindingName := ToEventBindingName(abiEvent.Name) + eTmp := CustomEventTemplate{ + Name: eBindingName, + ManifestName: abiEvent.Name, + } + for i := range abiEvent.Parameters { + pBindingName := ToParameterBindingName(abiEvent.Parameters[i].Name) + fullPName := eBindingName + "." + pBindingName + typeStr, pkg := scTypeConverter(fullPName, abiEvent.Parameters[i].Type, &cfg) + if pkg != "" { + imports[pkg] = struct{}{} + } + + var ( + extType binding.ExtendedType + ok bool + ) + if extType, ok = cfg.Types[fullPName]; !ok { + extType = binding.ExtendedType{ + Base: abiEvent.Parameters[i].Type, + } + addETImports(extType, ctr.NamedTypes, imports) + } + eTmp.Parameters = append(eTmp.Parameters, EventParamTmpl{ + ParamTmpl: binding.ParamTmpl{ + Name: pBindingName, + Type: typeStr, + }, + ExtType: extType, + }) + } + ctr.CustomEvents = append(ctr.CustomEvents, eTmp) + } + + if len(ctr.CustomEvents) > 0 { + imports["github.com/nspcc-dev/neo-go/pkg/neorpc/result"] = struct{}{} + imports["github.com/nspcc-dev/neo-go/pkg/vm/stackitem"] = struct{}{} + imports["fmt"] = struct{}{} + imports["errors"] = struct{}{} + } for i := range ctr.SafeMethods { switch ctr.SafeMethods[i].ReturnType { - case "interface{}": + case "any": abim := cfg.Manifest.ABI.GetMethod(ctr.SafeMethods[i].NameABI, len(ctr.SafeMethods[i].Arguments)) if abim.ReturnType == smartcontract.InteropInterfaceType { imports["github.com/google/uuid"] = struct{}{} @@ -565,7 +754,7 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st ctr.HasIterator = true } else { imports["github.com/nspcc-dev/neo-go/pkg/vm/stackitem"] = struct{}{} - ctr.SafeMethods[i].ReturnType = "interface{}" + ctr.SafeMethods[i].ReturnType = "any" ctr.SafeMethods[i].Unwrapper = "Item" } case "bool": @@ -582,7 +771,7 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st ctr.SafeMethods[i].Unwrapper = "PublicKey" case "[]byte": ctr.SafeMethods[i].Unwrapper = "Bytes" - case "[]interface{}": + case "[]any": imports["github.com/nspcc-dev/neo-go/pkg/vm/stackitem"] = struct{}{} ctr.SafeMethods[i].ReturnType = "[]stackitem.Item" ctr.SafeMethods[i].Unwrapper = "Array" @@ -680,3 +869,44 @@ func toTypeName(s string) string { func addIndent(str string, ind string) string { return strings.ReplaceAll(str, "\n", "\n"+ind) } + +// ToEventBindingName converts event name specified in the contract manifest to +// a valid go exported event structure name. +func ToEventBindingName(eventName string) string { + return toPascalCase(eventName) + "Event" +} + +// ToParameterBindingName converts parameter name specified in the contract +// manifest to a valid go structure's exported field name. +func ToParameterBindingName(paramName string) string { + return toPascalCase(paramName) +} + +// toPascalCase removes all non-unicode characters from the provided string and +// converts it to pascal case using space as delimiter. +func toPascalCase(s string) string { + var res string + ss := strings.Split(s, " ") + for i := range ss { // TODO: use DecodeRuneInString instead. + var word string + for _, ch := range ss[i] { + var ok bool + if len(res) == 0 && len(word) == 0 { + ok = unicode.IsLetter(ch) + } else { + ok = unicode.IsLetter(ch) || unicode.IsDigit(ch) || ch == '_' + } + if ok { + word += string(ch) + } + } + if len(word) > 0 { + res += upperFirst(word) + } + } + return res +} + +func upperFirst(s string) string { + return strings.ToUpper(s[0:1]) + s[1:] +} diff --git a/pkg/util/uint160.go b/pkg/util/uint160.go index 2f107e05e..ddb992a98 100644 --- a/pkg/util/uint160.go +++ b/pkg/util/uint160.go @@ -141,7 +141,7 @@ func (u Uint160) MarshalJSON() ([]byte, error) { } // UnmarshalYAML implements the YAML Unmarshaler interface. -func (u *Uint160) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (u *Uint160) UnmarshalYAML(unmarshal func(any) error) error { var s string err := unmarshal(&s) @@ -155,7 +155,7 @@ func (u *Uint160) UnmarshalYAML(unmarshal func(interface{}) error) error { } // MarshalYAML implements the YAML marshaller interface. -func (u Uint160) MarshalYAML() (interface{}, error) { +func (u Uint160) MarshalYAML() (any, error) { return "0x" + u.StringLE(), nil } diff --git a/pkg/vm/context.go b/pkg/vm/context.go index 1d44c29c5..e777fdbeb 100644 --- a/pkg/vm/context.go +++ b/pkg/vm/context.go @@ -340,3 +340,10 @@ func DynamicOnUnload(v *VM, ctx *Context, commit bool) error { } return nil } + +// BreakPoints returns the current set of Context's breakpoints. +func (c *Context) BreakPoints() []int { + res := make([]int, len(c.sc.breakPoints)) + copy(res, c.sc.breakPoints) + return res +} diff --git a/pkg/vm/debug_test.go b/pkg/vm/debug_test.go index c30964b82..ea985aa24 100644 --- a/pkg/vm/debug_test.go +++ b/pkg/vm/debug_test.go @@ -4,6 +4,8 @@ import ( "math/big" "testing" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" + "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/stretchr/testify/require" ) @@ -40,3 +42,20 @@ func TestVM_Debug(t *testing.T) { require.Equal(t, big.NewInt(5), v.estack.Top().Value()) }) } + +func TestContext_BreakPoints(t *testing.T) { + prog := makeProgram(opcode.CALL, 3, opcode.RET, + opcode.PUSH2, opcode.PUSH3, opcode.ADD, opcode.RET) + v := load(prog) + v.AddBreakPoint(3) + v.AddBreakPoint(5) + require.Equal(t, []int{3, 5}, v.Context().BreakPoints()) + + // Preserve the set of breakpoints on Call. + v.Call(3) + require.Equal(t, []int{3, 5}, v.Context().BreakPoints()) + + // New context -> clean breakpoints. + v.loadScriptWithCallingHash(prog, nil, util.Uint160{}, util.Uint160{}, callflag.All, 1, 3, nil) + require.Equal(t, []int{}, v.Context().BreakPoints()) +} diff --git a/pkg/vm/emit/emit.go b/pkg/vm/emit/emit.go index 8292aa30e..776af4594 100644 --- a/pkg/vm/emit/emit.go +++ b/pkg/vm/emit/emit.go @@ -98,18 +98,29 @@ func bigInt(w *io.BinWriter, n *big.Int, trySmall bool) { w.WriteBytes(padRight(1<= 0; i-- { switch e := es[i].(type) { - case []interface{}: + case []any: Array(w, e...) case int64: Int(w, e) + case uint64: + BigInt(w, new(big.Int).SetUint64(e)) case int32: Int(w, int64(e)) case uint32: @@ -124,6 +135,8 @@ func Array(w *io.BinWriter, es ...interface{}) { Int(w, int64(e)) case int: Int(w, int64(e)) + case uint: + BigInt(w, new(big.Int).SetUint64(uint64(e))) case *big.Int: BigInt(w, e) case string: @@ -148,6 +161,10 @@ func Array(w *io.BinWriter, es ...interface{}) { Bytes(w, e) case bool: Bool(w, e) + case stackitem.Convertible: + Convertible(w, e) + case stackitem.Item: + StackItem(w, e) default: if es[i] != nil { w.Err = fmt.Errorf("unsupported type: %T", e) @@ -160,6 +177,63 @@ func Array(w *io.BinWriter, es ...interface{}) { Opcodes(w, opcode.PACK) } +// Convertible converts provided stackitem.Convertible to the stackitem.Item and +// emits the item to the given buffer. +func Convertible(w *io.BinWriter, c stackitem.Convertible) { + si, err := c.ToStackItem() + if err != nil { + w.Err = fmt.Errorf("failed to convert stackitem.Convertible to stackitem: %w", err) + return + } + StackItem(w, si) +} + +// StackItem emits provided stackitem.Item to the given buffer. +func StackItem(w *io.BinWriter, si stackitem.Item) { + switch t := si.Type(); t { + case stackitem.AnyT: + if si.Value() == nil { + Opcodes(w, opcode.PUSHNULL) + } else { + w.Err = fmt.Errorf("only nil value supported for %s", t) + return + } + case stackitem.BooleanT: + Bool(w, si.Value().(bool)) + case stackitem.IntegerT: + BigInt(w, si.Value().(*big.Int)) + case stackitem.ByteArrayT, stackitem.BufferT: + Bytes(w, si.Value().([]byte)) + case stackitem.ArrayT: + arr := si.Value().([]stackitem.Item) + arrAny := make([]any, len(arr)) + for i := range arr { + arrAny[i] = arr[i] + } + Array(w, arrAny...) + case stackitem.StructT: + arr := si.Value().([]stackitem.Item) + for i := len(arr) - 1; i >= 0; i-- { + StackItem(w, arr[i]) + } + + Int(w, int64(len(arr))) + Opcodes(w, opcode.PACKSTRUCT) + case stackitem.MapT: + arr := si.Value().([]stackitem.MapElement) + for i := len(arr) - 1; i >= 0; i-- { + StackItem(w, arr[i].Value) + StackItem(w, arr[i].Key) + } + + Int(w, int64(len(arr))) + Opcodes(w, opcode.PACKMAP) + default: + w.Err = fmt.Errorf("%s is unsuppoted", t) + return + } +} + // String emits a string to the given buffer. func String(w *io.BinWriter, s string) { Bytes(w, []byte(s)) @@ -225,7 +299,7 @@ func AppCallNoArgs(w *io.BinWriter, scriptHash util.Uint160, operation string, f } // AppCall emits SYSCALL with System.Contract.Call parameter for given contract, operation, call flag and arguments. -func AppCall(w *io.BinWriter, scriptHash util.Uint160, operation string, f callflag.CallFlag, args ...interface{}) { +func AppCall(w *io.BinWriter, scriptHash util.Uint160, operation string, f callflag.CallFlag, args ...any) { Array(w, args...) AppCallNoArgs(w, scriptHash, operation, f) } diff --git a/pkg/vm/emit/emit_test.go b/pkg/vm/emit/emit_test.go index a585161b4..697a79b3b 100644 --- a/pkg/vm/emit/emit_test.go +++ b/pkg/vm/emit/emit_test.go @@ -5,6 +5,7 @@ import ( "errors" "math" "math/big" + "strings" "testing" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" @@ -223,8 +224,35 @@ func TestEmitArray(t *testing.T) { u256 := util.Uint256{1, 2, 3} veryBig := new(big.Int).SetUint64(math.MaxUint64) veryBig.Add(veryBig, big.NewInt(1)) - Array(buf.BinWriter, p160, p256, &u160, &u256, u160, u256, big.NewInt(0), veryBig, - []interface{}{int64(1), int64(2)}, nil, int64(1), "str", false, true, []byte{0xCA, 0xFE}) + Array(buf.BinWriter, + uint64(math.MaxUint64), + uint(math.MaxUint32), // don't use MaxUint to keep test results the same throughout all platforms. + stackitem.NewMapWithValue([]stackitem.MapElement{ + { + Key: stackitem.Make(1), + Value: stackitem.Make("str1"), + }, + { + Key: stackitem.Make(2), + Value: stackitem.Make("str2"), + }, + }), + stackitem.NewStruct([]stackitem.Item{ + stackitem.Make(4), + stackitem.Make("str"), + }), + &ConvertibleStruct{ + SomeInt: 5, + SomeString: "str", + }, + stackitem.Make(5), + stackitem.Make("str"), + stackitem.NewArray([]stackitem.Item{ + stackitem.Make(true), + stackitem.Make("str"), + }), + p160, p256, &u160, &u256, u160, u256, big.NewInt(0), veryBig, + []any{int64(1), int64(2)}, nil, int64(1), "str", false, true, []byte{0xCA, 0xFE}) require.NoError(t, buf.Err) res := buf.Bytes() @@ -259,6 +287,65 @@ func TestEmitArray(t *testing.T) { assert.EqualValues(t, u160.BytesBE(), res[127:147]) assert.EqualValues(t, opcode.PUSHNULL, res[147]) assert.EqualValues(t, opcode.PUSHNULL, res[148]) + // Array of two stackitems: + assert.EqualValues(t, opcode.PUSHDATA1, res[149]) + assert.EqualValues(t, 3, res[150]) + assert.EqualValues(t, []byte("str"), res[151:154]) + assert.EqualValues(t, opcode.PUSHT, res[154]) + assert.EqualValues(t, opcode.PUSH2, res[155]) + assert.EqualValues(t, opcode.PACK, res[156]) + // ByteString stackitem ("str"): + assert.EqualValues(t, opcode.PUSHDATA1, res[157]) + assert.EqualValues(t, 3, res[158]) + assert.EqualValues(t, []byte("str"), res[159:162]) + // Integer stackitem (5): + assert.EqualValues(t, opcode.PUSH5, res[162]) + // Convertible struct: + assert.EqualValues(t, opcode.PUSHDATA1, res[163]) + assert.EqualValues(t, 3, res[164]) + assert.EqualValues(t, []byte("str"), res[165:168]) + assert.EqualValues(t, opcode.PUSH5, res[168]) + assert.EqualValues(t, opcode.PUSH2, res[169]) + assert.EqualValues(t, opcode.PACK, res[170]) + // Struct stackitem (4, "str") + assert.EqualValues(t, opcode.PUSHDATA1, res[171]) + assert.EqualValues(t, 3, res[172]) + assert.EqualValues(t, []byte("str"), res[173:176]) + assert.EqualValues(t, opcode.PUSH4, res[176]) + assert.EqualValues(t, opcode.PUSH2, res[177]) + assert.EqualValues(t, opcode.PACKSTRUCT, res[178]) + // Map stackitem (1:"str1", 2:"str2") + assert.EqualValues(t, opcode.PUSHDATA1, res[179]) + assert.EqualValues(t, 4, res[180]) + assert.EqualValues(t, []byte("str2"), res[181:185]) + assert.EqualValues(t, opcode.PUSH2, res[185]) + assert.EqualValues(t, opcode.PUSHDATA1, res[186]) + assert.EqualValues(t, 4, res[187]) + assert.EqualValues(t, []byte("str1"), res[188:192]) + assert.EqualValues(t, opcode.PUSH1, res[192]) + assert.EqualValues(t, opcode.PUSH2, res[193]) + assert.EqualValues(t, opcode.PACKMAP, res[194]) + // uint (MaxUint32) + assert.EqualValues(t, opcode.PUSHINT64, res[195]) + assert.EqualValues(t, []byte{ + 0xff, 0xff, 0xff, 0xff, + 0, 0, 0, 0, + }, res[196:204]) + // uint64 (MaxUint64) + assert.EqualValues(t, opcode.PUSHINT128, res[204]) + assert.EqualValues(t, []byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 0, 0, 0, + 0, 0, 0, 0}, res[205:221]) + + // Values packing: + assert.EqualValues(t, opcode.PUSHINT8, res[221]) + assert.EqualValues(t, byte(23), res[222]) + assert.EqualValues(t, opcode.PACK, res[223]) + + // Overall script length: + assert.EqualValues(t, 224, len(res)) }) t.Run("empty", func(t *testing.T) { @@ -374,3 +461,191 @@ func TestEmitCall(t *testing.T) { label := binary.LittleEndian.Uint16(result[1:3]) assert.Equal(t, label, uint16(100)) } + +func TestEmitStackitem(t *testing.T) { + t.Run("good", func(t *testing.T) { + buf := io.NewBufBinWriter() + itms := []stackitem.Item{ + stackitem.Make(true), + stackitem.Make(false), + stackitem.Make(5), + stackitem.Make("str"), + stackitem.Make([]stackitem.Item{ + stackitem.Make(true), + stackitem.Make([]stackitem.Item{ + stackitem.Make(1), + stackitem.Make("str"), + }), + }), + stackitem.NewStruct([]stackitem.Item{ + stackitem.Make(true), + stackitem.Make(7), + }), + stackitem.NewMapWithValue([]stackitem.MapElement{ + { + Key: stackitem.Make(7), + Value: stackitem.Make("str1"), + }, + { + Key: stackitem.Make(8), + Value: stackitem.Make("str2"), + }, + }), + stackitem.Null{}, + } + for _, si := range itms { + StackItem(buf.BinWriter, si) + } + require.NoError(t, buf.Err) + res := buf.Bytes() + + // Single values: + assert.EqualValues(t, opcode.PUSHT, res[0]) + assert.EqualValues(t, opcode.PUSHF, res[1]) + assert.EqualValues(t, opcode.PUSH5, res[2]) + assert.EqualValues(t, opcode.PUSHDATA1, res[3]) + assert.EqualValues(t, 3, res[4]) + assert.EqualValues(t, []byte("str"), res[5:8]) + // Nested array: + assert.EqualValues(t, opcode.PUSHDATA1, res[8]) + assert.EqualValues(t, 3, res[9]) + assert.EqualValues(t, []byte("str"), res[10:13]) + assert.EqualValues(t, opcode.PUSH1, res[13]) + assert.EqualValues(t, opcode.PUSH2, res[14]) + assert.EqualValues(t, opcode.PACK, res[15]) + assert.EqualValues(t, opcode.PUSHT, res[16]) + assert.EqualValues(t, opcode.PUSH2, res[17]) + assert.EqualValues(t, opcode.PACK, res[18]) + // Struct (true, 7): + assert.EqualValues(t, opcode.PUSH7, res[19]) + assert.EqualValues(t, opcode.PUSHT, res[20]) + assert.EqualValues(t, opcode.PUSH2, res[21]) + assert.EqualValues(t, opcode.PACKSTRUCT, res[22]) + // Map (7:"str1", 8:"str2"): + assert.EqualValues(t, opcode.PUSHDATA1, res[23]) + assert.EqualValues(t, 4, res[24]) + assert.EqualValues(t, []byte("str2"), res[25:29]) + assert.EqualValues(t, opcode.PUSH8, res[29]) + assert.EqualValues(t, opcode.PUSHDATA1, res[30]) + assert.EqualValues(t, 4, res[31]) + assert.EqualValues(t, []byte("str1"), res[32:36]) + assert.EqualValues(t, opcode.PUSH7, res[36]) + assert.EqualValues(t, opcode.PUSH2, res[37]) + assert.EqualValues(t, opcode.PACKMAP, res[38]) + // Null: + assert.EqualValues(t, opcode.PUSHNULL, res[39]) + + // Overall script length: + require.Equal(t, 40, len(res)) + }) + + t.Run("unsupported", func(t *testing.T) { + itms := []stackitem.Item{ + stackitem.NewInterop(nil), + stackitem.NewPointer(123, []byte{123}), + } + for _, si := range itms { + buf := io.NewBufBinWriter() + StackItem(buf.BinWriter, si) + require.Error(t, buf.Err) + } + }) + + t.Run("invalid any", func(t *testing.T) { + buf := io.NewBufBinWriter() + StackItem(buf.BinWriter, StrangeStackItem{}) + actualErr := buf.Err + require.Error(t, actualErr) + require.True(t, strings.Contains(actualErr.Error(), "only nil value supported"), actualErr.Error()) + }) +} + +type StrangeStackItem struct{} + +var _ = stackitem.Item(StrangeStackItem{}) + +func (StrangeStackItem) Value() any { + return struct{}{} +} +func (StrangeStackItem) Type() stackitem.Type { + return stackitem.AnyT +} +func (StrangeStackItem) String() string { + panic("TODO") +} +func (StrangeStackItem) Dup() stackitem.Item { + panic("TODO") +} +func (StrangeStackItem) TryBool() (bool, error) { + panic("TODO") +} +func (StrangeStackItem) TryBytes() ([]byte, error) { + panic("TODO") +} +func (StrangeStackItem) TryInteger() (*big.Int, error) { + panic("TODO") +} +func (StrangeStackItem) Equals(stackitem.Item) bool { + panic("TODO") +} +func (StrangeStackItem) Convert(stackitem.Type) (stackitem.Item, error) { + panic("TODO") +} + +type ConvertibleStruct struct { + SomeInt int + SomeString string + err error +} + +var _ = stackitem.Convertible(&ConvertibleStruct{}) + +func (s *ConvertibleStruct) ToStackItem() (stackitem.Item, error) { + if s.err != nil { + return nil, s.err + } + return stackitem.NewArray([]stackitem.Item{ + stackitem.Make(s.SomeInt), + stackitem.Make(s.SomeString), + }), nil +} + +func (s *ConvertibleStruct) FromStackItem(si stackitem.Item) error { + panic("TODO") +} + +func TestEmitConvertible(t *testing.T) { + t.Run("good", func(t *testing.T) { + buf := io.NewBufBinWriter() + str := &ConvertibleStruct{ + SomeInt: 5, + SomeString: "str", + } + Convertible(buf.BinWriter, str) + require.NoError(t, buf.Err) + res := buf.Bytes() + + // The struct itself: + assert.EqualValues(t, opcode.PUSHDATA1, res[0]) + assert.EqualValues(t, 3, res[1]) + assert.EqualValues(t, []byte("str"), res[2:5]) + assert.EqualValues(t, opcode.PUSH5, res[5]) + assert.EqualValues(t, opcode.PUSH2, res[6]) + assert.EqualValues(t, opcode.PACK, res[7]) + + // Overall length: + assert.EqualValues(t, 8, len(res)) + }) + + t.Run("error on conversion", func(t *testing.T) { + buf := io.NewBufBinWriter() + expectedErr := errors.New("error on conversion") + str := &ConvertibleStruct{ + err: expectedErr, + } + Convertible(buf.BinWriter, str) + actualErr := buf.Err + require.Error(t, actualErr) + require.ErrorIs(t, actualErr, expectedErr) + }) +} diff --git a/pkg/vm/exception.go b/pkg/vm/exception.go index 61c509931..5498e9b44 100644 --- a/pkg/vm/exception.go +++ b/pkg/vm/exception.go @@ -45,7 +45,7 @@ func (c *exceptionHandlingContext) String() string { } // Value implements the stackitem.Item interface. -func (c *exceptionHandlingContext) Value() interface{} { +func (c *exceptionHandlingContext) Value() any { return c } diff --git a/pkg/vm/json_test.go b/pkg/vm/json_test.go index ada80b499..8df690145 100644 --- a/pkg/vm/json_test.go +++ b/pkg/vm/json_test.go @@ -54,7 +54,7 @@ type ( vmUTStackItem struct { Type vmUTStackItemType - Value interface{} + Value any } vmUTStep struct { diff --git a/pkg/vm/opcodebench_test.go b/pkg/vm/opcodebench_test.go index 1494dbd9a..e64405e78 100644 --- a/pkg/vm/opcodebench_test.go +++ b/pkg/vm/opcodebench_test.go @@ -43,11 +43,11 @@ func opParamVM(op opcode.Opcode, param []byte) func() *VM { return opParamPushVM(op, param) } -func opParamPushVM(op opcode.Opcode, param []byte, items ...interface{}) func() *VM { +func opParamPushVM(op opcode.Opcode, param []byte, items ...any) func() *VM { return opParamSlotsPushVM(op, param, 0, 0, 0, items...) } -func opParamSlotsPushVM(op opcode.Opcode, param []byte, sslot int, slotloc int, slotarg int, items ...interface{}) func() *VM { +func opParamSlotsPushVM(op opcode.Opcode, param []byte, sslot int, slotloc int, slotarg int, items ...any) func() *VM { return func() *VM { script := []byte{byte(op)} script = append(script, param...) @@ -75,7 +75,7 @@ func opParamSlotsPushVM(op opcode.Opcode, param []byte, sslot int, slotloc int, } } -func exceptParamPushVM(op opcode.Opcode, param []byte, ilen int, elen int, exception bool, items ...interface{}) func() *VM { +func exceptParamPushVM(op opcode.Opcode, param []byte, ilen int, elen int, exception bool, items ...any) func() *VM { return func() *VM { regVMF := opParamPushVM(op, param, items...) v := regVMF() @@ -136,8 +136,8 @@ func arrayOfOnes(size int) []stackitem.Item { return elems } -func arrayOfIfaces(size int) []interface{} { - var elems = make([]interface{}, size) +func arrayOfIfaces(size int) []any { + var elems = make([]any, size) for i := range elems { elems[i] = 1 } @@ -388,14 +388,14 @@ func BenchmarkOpcodes(t *testing.B) { if cp.op == opcode.PICK { name += "/" + strconv.Itoa(cp.pos) } - var getitems = func(element interface{}) []interface{} { + var getitems = func(element any) []any { l := cp.l pos := cp.pos if cp.op == opcode.PICK { pos++ l++ } - var items = make([]interface{}, l) + var items = make([]any, l) for i := range items { items[i] = 0 } @@ -461,12 +461,12 @@ func BenchmarkOpcodes(t *testing.B) { if sw.op == opcode.ROLL || sw.op == opcode.REVERSEN { name += "/" + strconv.Itoa(sw.num) } - var getitems = func(element interface{}) []interface{} { + var getitems = func(element any) []any { l := sw.num if sw.op == opcode.ROLL || sw.op == opcode.REVERSEN { l++ } - var items = make([]interface{}, l) + var items = make([]any, l) for i := range items { items[i] = element } @@ -656,7 +656,7 @@ func BenchmarkOpcodes(t *testing.B) { var nums = []int{1, 255, 1024} for _, n := range nums { t.Run(strconv.Itoa(n), func(t *testing.B) { - var elems = make([]interface{}, n+1) + var elems = make([]any, n+1) for i := range elems { elems[i] = 0 } diff --git a/pkg/vm/stack.go b/pkg/vm/stack.go index 9e609766e..bd83fba6a 100644 --- a/pkg/vm/stack.go +++ b/pkg/vm/stack.go @@ -22,7 +22,7 @@ type Element struct { // NewElement returns a new Element object, with its underlying value inferred // to the corresponding type. -func NewElement(v interface{}) Element { +func NewElement(v any) Element { return Element{stackitem.Make(v)} } @@ -32,7 +32,7 @@ func (e Element) Item() stackitem.Item { } // Value returns the value of the Item contained in the element. -func (e Element) Value() interface{} { +func (e Element) Value() any { return e.value.Value() } @@ -185,7 +185,7 @@ func (s *Stack) PushItem(i stackitem.Item) { // PushVal pushes the given value on the stack. It will infer the // underlying Item to its corresponding type. -func (s *Stack) PushVal(v interface{}) { +func (s *Stack) PushVal(v any) { s.Push(NewElement(v)) } diff --git a/pkg/vm/stackitem/item.go b/pkg/vm/stackitem/item.go index 6e645bf28..9e645589c 100644 --- a/pkg/vm/stackitem/item.go +++ b/pkg/vm/stackitem/item.go @@ -36,7 +36,7 @@ const ( // Item represents the "real" value that is pushed on the stack. type Item interface { fmt.Stringer - Value() interface{} + Value() any // Dup duplicates current Item. Dup() Item // TryBool converts Item to a boolean value. @@ -60,6 +60,13 @@ type Convertible interface { FromStackItem(Item) error } +// Equatable describes a special value of Interop that can be compared with +// value of some other Interop that implements Equatable. +type Equatable interface { + // Equals checks if two objects are equal. + Equals(other Equatable) bool +} + var ( // ErrInvalidConversion is returned upon an attempt to make an incorrect // conversion between item types. @@ -88,7 +95,7 @@ func mkInvConversion(from Item, to Type) error { // Make tries to make an appropriate stack item from the provided value. // It will panic if it's not possible. -func Make(v interface{}) Item { +func Make(v any) Item { switch val := v.(type) { case int: return (*BigInteger)(big.NewInt(int64(val))) @@ -122,7 +129,7 @@ func Make(v interface{}) Item { a = append(a, Make(i)) } return Make(a) - case []interface{}: + case []any: res := make([]Item, len(val)) for i := range val { res[i] = Make(val[i]) @@ -132,6 +139,16 @@ func Make(v interface{}) Item { return Make(val.BytesBE()) case util.Uint256: return Make(val.BytesBE()) + case *util.Uint160: + if val == nil { + return Null{} + } + return Make(*val) + case *util.Uint256: + if val == nil { + return Null{} + } + return Make(*val) case nil: return Null{} default: @@ -210,7 +227,7 @@ func NewStruct(items []Item) *Struct { } // Value implements the Item interface. -func (i *Struct) Value() interface{} { +func (i *Struct) Value() any { return i.value } @@ -374,7 +391,7 @@ func (i Null) String() string { } // Value implements the Item interface. -func (i Null) Value() interface{} { +func (i Null) Value() any { return nil } @@ -483,7 +500,7 @@ func (i *BigInteger) Equals(s Item) bool { } // Value implements the Item interface. -func (i *BigInteger) Value() interface{} { +func (i *BigInteger) Value() any { return i.Big() } @@ -519,7 +536,7 @@ func NewBool(val bool) Bool { } // Value implements the Item interface. -func (i Bool) Value() interface{} { +func (i Bool) Value() any { return bool(i) } @@ -589,7 +606,7 @@ func NewByteArray(b []byte) *ByteArray { } // Value implements the Item interface. -func (i *ByteArray) Value() interface{} { +func (i *ByteArray) Value() any { return []byte(*i) } @@ -697,7 +714,7 @@ func NewArray(items []Item) *Array { } // Value implements the Item interface. -func (i *Array) Value() interface{} { +func (i *Array) Value() any { return i.value } @@ -818,7 +835,7 @@ func NewMapWithValue(value []MapElement) *Map { } // Value implements the Item interface. -func (i *Map) Value() interface{} { +func (i *Map) Value() any { return i.value } @@ -937,18 +954,18 @@ func IsValidMapKey(key Item) error { // Interop represents interop data on the stack. type Interop struct { - value interface{} + value any } // NewInterop returns a new Interop object. -func NewInterop(value interface{}) *Interop { +func NewInterop(value any) *Interop { return &Interop{ value: value, } } // Value implements the Item interface. -func (i *Interop) Value() interface{} { +func (i *Interop) Value() any { return i.value } @@ -984,7 +1001,12 @@ func (i *Interop) Equals(s Item) bool { return false } val, ok := s.(*Interop) - return ok && i.value == val.value + if !ok { + return false + } + a, okA := i.value.(Equatable) + b, okB := val.value.(Equatable) + return (okA && okB && a.Equals(b)) || (!okA && !okB && i.value == val.value) } // Type implements the Item interface. @@ -1041,7 +1063,7 @@ func (p *Pointer) String() string { } // Value implements the Item interface. -func (p *Pointer) Value() interface{} { +func (p *Pointer) Value() any { return p.pos } @@ -1114,7 +1136,7 @@ func NewBuffer(b []byte) *Buffer { } // Value implements the Item interface. -func (i *Buffer) Value() interface{} { +func (i *Buffer) Value() any { return []byte(*i) } diff --git a/pkg/vm/stackitem/item_test.go b/pkg/vm/stackitem/item_test.go index 210f0a83d..afdfee213 100644 --- a/pkg/vm/stackitem/item_test.go +++ b/pkg/vm/stackitem/item_test.go @@ -5,12 +5,13 @@ import ( "testing" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" + "github.com/nspcc-dev/neo-go/pkg/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) var makeStackItemTestCases = []struct { - input interface{} + input any result Item }{ { @@ -81,10 +82,26 @@ var makeStackItemTestCases = []struct { input: nil, result: Null{}, }, + { + input: &util.Uint160{1, 2, 3}, + result: NewByteArray(util.Uint160{1, 2, 3}.BytesBE()), + }, + { + input: &util.Uint256{1, 2, 3}, + result: NewByteArray(util.Uint256{1, 2, 3}.BytesBE()), + }, + { + input: (*util.Uint160)(nil), + result: Null{}, + }, + { + input: (*util.Uint256)(nil), + result: Null{}, + }, } var makeStackItemErrorCases = []struct { - input interface{} + input any }{ { input: map[int]int{1: 2}, diff --git a/pkg/vm/stackitem/json_test.go b/pkg/vm/stackitem/json_test.go index 8ef6112fb..679da1b3b 100644 --- a/pkg/vm/stackitem/json_test.go +++ b/pkg/vm/stackitem/json_test.go @@ -1,7 +1,6 @@ package stackitem import ( - "errors" "math/big" "testing" @@ -64,7 +63,7 @@ func TestFromToJSON(t *testing.T) { require.NoError(t, err) _, err = FromJSON([]byte(js), 3) - require.True(t, errors.Is(err, errTooBigElements), err) + require.ErrorIs(t, err, errTooBigElements) }) }) t.Run("Map", func(t *testing.T) { @@ -87,7 +86,7 @@ func TestFromToJSON(t *testing.T) { require.NoError(t, err) _, err = FromJSON([]byte(js), 4) - require.True(t, errors.Is(err, errTooBigElements), err) + require.ErrorIs(t, err, errTooBigElements) }) }) t.Run("Invalid", func(t *testing.T) { @@ -152,7 +151,7 @@ func TestFromJSON_CompatBigInt(t *testing.T) { func testToJSON(t *testing.T, expectedErr error, item Item) { data, err := ToJSON(item) if expectedErr != nil { - require.True(t, errors.Is(err, expectedErr), err) + require.ErrorIs(t, err, expectedErr) return } require.NoError(t, err) @@ -339,16 +338,16 @@ func TestToJSONWithTypesBadCases(t *testing.T) { items[i] = NewBuffer(bigBuf) } _, err := ToJSONWithTypes(NewArray(items)) - require.True(t, errors.Is(err, errTooBigSize), "got: %v", err) + require.ErrorIs(t, err, errTooBigSize) }) t.Run("overflow on primitive item", func(t *testing.T) { _, err := ToJSONWithTypes(NewBuffer(bigBuf)) - require.True(t, errors.Is(err, errTooBigSize), "got: %v", err) + require.ErrorIs(t, err, errTooBigSize) }) t.Run("overflow on array element", func(t *testing.T) { b := NewBuffer(bigBuf[:MaxSize/2]) _, err := ToJSONWithTypes(NewArray([]Item{b, b})) - require.True(t, errors.Is(err, errTooBigSize), "got: %v", err) + require.ErrorIs(t, err, errTooBigSize) }) t.Run("overflow on map key", func(t *testing.T) { m := NewMapWithValue([]MapElement{ @@ -356,7 +355,7 @@ func TestToJSONWithTypesBadCases(t *testing.T) { {NewByteArray(bigBuf), NewBool(true)}, }) _, err := ToJSONWithTypes(m) - require.True(t, errors.Is(err, errTooBigSize), "got: %v", err) + require.ErrorIs(t, err, errTooBigSize) }) t.Run("overflow on the last byte of array", func(t *testing.T) { // Construct big enough buffer and pad with integer digits @@ -366,7 +365,7 @@ func TestToJSONWithTypesBadCases(t *testing.T) { NewBigInteger(big.NewInt(1234)), }) _, err := ToJSONWithTypes(arr) - require.True(t, errors.Is(err, errTooBigSize), "got: %v", err) + require.ErrorIs(t, err, errTooBigSize) }) t.Run("overflow on the item prefix", func(t *testing.T) { arr := NewArray([]Item{ @@ -374,7 +373,7 @@ func TestToJSONWithTypesBadCases(t *testing.T) { NewBool(true), }) _, err := ToJSONWithTypes(arr) - require.True(t, errors.Is(err, errTooBigSize), "got: %v", err) + require.ErrorIs(t, err, errTooBigSize) }) t.Run("overflow on null", func(t *testing.T) { arr := NewArray([]Item{ @@ -382,7 +381,7 @@ func TestToJSONWithTypesBadCases(t *testing.T) { Null{}, }) _, err := ToJSONWithTypes(arr) - require.True(t, errors.Is(err, errTooBigSize), "got: %v", err) + require.ErrorIs(t, err, errTooBigSize) }) t.Run("overflow on interop", func(t *testing.T) { arr := NewArray([]Item{ @@ -390,17 +389,17 @@ func TestToJSONWithTypesBadCases(t *testing.T) { NewInterop(42), }) _, err := ToJSONWithTypes(arr) - require.True(t, errors.Is(err, errTooBigSize), "got: %v", err) + require.ErrorIs(t, err, errTooBigSize) }) t.Run("overflow on cached item", func(t *testing.T) { b := NewArray([]Item{NewByteArray(bigBuf[:MaxSize/2])}) arr := NewArray([]Item{b, b}) _, err := ToJSONWithTypes(arr) - require.True(t, errors.Is(err, errTooBigSize), "got: %v", err) + require.ErrorIs(t, err, errTooBigSize) }) t.Run("invalid type", func(t *testing.T) { _, err := ToJSONWithTypes(nil) - require.True(t, errors.Is(err, ErrUnserializable), "got: %v", err) + require.ErrorIs(t, err, ErrUnserializable) }) } diff --git a/pkg/vm/stackitem/serialization_test.go b/pkg/vm/stackitem/serialization_test.go index 3c0b64190..4722df88b 100644 --- a/pkg/vm/stackitem/serialization_test.go +++ b/pkg/vm/stackitem/serialization_test.go @@ -1,7 +1,6 @@ package stackitem import ( - "errors" "testing" "github.com/nspcc-dev/neo-go/pkg/io" @@ -20,13 +19,13 @@ func TestSerializationMaxErr(t *testing.T) { require.NoError(t, err) _, err = Serialize(aitem) - require.True(t, errors.Is(err, ErrTooBig), err) + require.ErrorIs(t, err, ErrTooBig) } func testSerialize(t *testing.T, expectedErr error, item Item) { data, err := Serialize(item) if expectedErr != nil { - require.True(t, errors.Is(err, expectedErr), err) + require.ErrorIs(t, err, expectedErr) return } require.NoError(t, err) @@ -62,7 +61,7 @@ func TestSerialize(t *testing.T) { data, err := Serialize(newItem(items)) require.NoError(t, err) _, err = Deserialize(data) - require.True(t, errors.Is(err, ErrTooBig), err) + require.ErrorIs(t, err, ErrTooBig) } t.Run("array", func(t *testing.T) { testArray(t, func(items []Item) Item { return NewArray(items) }) @@ -169,7 +168,7 @@ func TestSerialize(t *testing.T) { data, err := Serialize(m) require.NoError(t, err) _, err = Deserialize(data) - require.True(t, errors.Is(err, ErrTooBig), err) + require.ErrorIs(t, err, ErrTooBig) }) } @@ -189,7 +188,7 @@ func TestMapDeserializationError(t *testing.T) { EncodeBinaryProtected(m, w.BinWriter) require.NoError(t, w.Err) _, err := Deserialize(w.Bytes()) - require.True(t, errors.Is(err, ErrInvalidType), err) + require.ErrorIs(t, err, ErrInvalidType) } func TestDeserializeTooManyElements(t *testing.T) { @@ -206,7 +205,7 @@ func TestDeserializeTooManyElements(t *testing.T) { data, err = Serialize(item) require.NoError(t, err) _, err = Deserialize(data) - require.True(t, errors.Is(err, ErrTooBig), err) + require.ErrorIs(t, err, ErrTooBig) } func TestDeserializeLimited(t *testing.T) { @@ -226,7 +225,7 @@ func TestDeserializeLimited(t *testing.T) { require.NoError(t, err) _, err = DeserializeLimited(data, customLimit) require.Error(t, err) - require.True(t, errors.Is(err, ErrTooBig), err) + require.ErrorIs(t, err, ErrTooBig) } func BenchmarkEncodeBinary(b *testing.B) { diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 4ee109516..8c1af0df8 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -30,14 +30,14 @@ import ( type errorAtInstruct struct { ip int op opcode.Opcode - err interface{} + err any } func (e *errorAtInstruct) Error() string { return fmt.Sprintf("at instruction %d (%s): %s", e.ip, e.op, e.err) } -func newError(ip int, op opcode.Opcode, err interface{}) *errorAtInstruct { +func newError(ip int, op opcode.Opcode, err any) *errorAtInstruct { return &errorAtInstruct{ip: ip, op: op, err: err} } @@ -379,7 +379,7 @@ func (v *VM) Context() *Context { // PopResult is used to pop the first item of the evaluation stack. This allows // us to test the compiler and the vm in a bi-directional way. -func (v *VM) PopResult() interface{} { +func (v *VM) PopResult() any { if v.estack.Len() == 0 { return nil } diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index d78ab22c3..d19b42e6e 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -471,7 +471,7 @@ func TestPushData4BigN(t *testing.T) { checkVMFailed(t, vm) } -func getTestCallFlagsFunc(syscall []byte, flags callflag.CallFlag, result interface{}) func(t *testing.T) { +func getTestCallFlagsFunc(syscall []byte, flags callflag.CallFlag, result any) func(t *testing.T) { return func(t *testing.T) { script := append([]byte{byte(opcode.SYSCALL)}, syscall...) v := newTestVM() @@ -535,7 +535,7 @@ func getJMPProgram(op opcode.Opcode) []byte { return append(prog, byte(opcode.PUSH1), byte(opcode.RET), byte(opcode.PUSH2), byte(opcode.RET)) } -func testJMP(t *testing.T, op opcode.Opcode, res interface{}, items ...interface{}) { +func testJMP(t *testing.T, op opcode.Opcode, res any, items ...any) { prog := getJMPProgram(op) v := load(prog) for i := range items { @@ -552,35 +552,35 @@ func testJMP(t *testing.T, op opcode.Opcode, res interface{}, items ...interface func TestJMPs(t *testing.T) { testCases := []struct { name string - items []interface{} + items []any }{ { name: "no condition", }, { name: "single item (true)", - items: []interface{}{true}, + items: []any{true}, }, { name: "single item (false)", - items: []interface{}{false}, + items: []any{false}, }, { name: "24 and 42", - items: []interface{}{24, 42}, + items: []any{24, 42}, }, { name: "42 and 24", - items: []interface{}{42, 24}, + items: []any{42, 24}, }, { name: "42 and 42", - items: []interface{}{42, 42}, + items: []any{42, 42}, }, } // 2 is true, 1 is false - results := map[opcode.Opcode][]interface{}{ + results := map[opcode.Opcode][]any{ opcode.JMP: {2, 2, 2, 2, 2, 2}, opcode.JMPIF: {nil, 2, 1, 2, 2, 2}, opcode.JMPIFNOT: {nil, 1, 2, 1, 1, 1}, @@ -820,11 +820,23 @@ func TestDepth(t *testing.T) { assert.Equal(t, int64(3), vm.estack.Pop().BigInt().Int64()) } +type simpleEquatable struct { + ok bool +} + +var _ = stackitem.Equatable(simpleEquatable{}) + +func (e simpleEquatable) Equals(other stackitem.Equatable) bool { + _, ok := other.(simpleEquatable) + return ok && e.ok +} + func TestEQUALTrue(t *testing.T) { prog := makeProgram(opcode.DUP, opcode.EQUAL) t.Run("Array", getTestFuncForVM(prog, true, []stackitem.Item{})) t.Run("Map", getTestFuncForVM(prog, true, stackitem.NewMap())) t.Run("Buffer", getTestFuncForVM(prog, true, stackitem.NewBuffer([]byte{1, 2}))) + t.Run("Equatable", getTestFuncForVM(prog, true, stackitem.NewInterop(simpleEquatable{ok: true}))) } func TestEQUAL(t *testing.T) { @@ -837,6 +849,8 @@ func TestEQUAL(t *testing.T) { t.Run("Map", getTestFuncForVM(prog, false, stackitem.NewMap(), stackitem.NewMap())) t.Run("Array", getTestFuncForVM(prog, false, []stackitem.Item{}, []stackitem.Item{})) t.Run("Buffer", getTestFuncForVM(prog, false, stackitem.NewBuffer([]byte{42}), stackitem.NewBuffer([]byte{42}))) + t.Run("EquatableFalse", getTestFuncForVM(prog, false, stackitem.NewInterop(simpleEquatable{false}), stackitem.NewInterop(simpleEquatable{}))) + t.Run("EquatableTrue", getTestFuncForVM(prog, true, stackitem.NewInterop(simpleEquatable{true}), stackitem.NewInterop(simpleEquatable{}))) } func TestEQUALByteArrayWithLimit(t *testing.T) { @@ -896,11 +910,11 @@ func TestEQUALByteArrayWithLimit(t *testing.T) { }) } -func runWithArgs(t *testing.T, prog []byte, result interface{}, args ...interface{}) { +func runWithArgs(t *testing.T, prog []byte, result any, args ...any) { getTestFuncForVM(prog, result, args...)(t) } -func getCustomTestFuncForVM(prog []byte, check func(t *testing.T, v *VM), args ...interface{}) func(t *testing.T) { +func getCustomTestFuncForVM(prog []byte, check func(t *testing.T, v *VM), args ...any) func(t *testing.T) { return func(t *testing.T) { v := load(prog) for i := range args { @@ -915,7 +929,7 @@ func getCustomTestFuncForVM(prog []byte, check func(t *testing.T, v *VM), args . } } -func getTestFuncForVM(prog []byte, result interface{}, args ...interface{}) func(t *testing.T) { +func getTestFuncForVM(prog []byte, result any, args ...any) func(t *testing.T) { var f func(t *testing.T, v *VM) if result != nil { f = func(t *testing.T, v *VM) { @@ -1057,7 +1071,7 @@ func getTRYProgram(tryBlock, catchBlock, finallyBlock []byte) []byte { return prog } -func getTRYTestFunc(result interface{}, tryBlock, catchBlock, finallyBlock []byte) func(t *testing.T) { +func getTRYTestFunc(result any, tryBlock, catchBlock, finallyBlock []byte) func(t *testing.T) { return func(t *testing.T) { prog := getTRYProgram(tryBlock, catchBlock, finallyBlock) runWithArgs(t, prog, result) @@ -1695,7 +1709,7 @@ func TestROLLGood(t *testing.T) { assert.Equal(t, stackitem.Make(1), vm.estack.Pop().value) } -func getCheckEStackFunc(items ...interface{}) func(t *testing.T, v *VM) { +func getCheckEStackFunc(items ...any) func(t *testing.T, v *VM) { return func(t *testing.T, v *VM) { require.Equal(t, len(items), v.estack.Len()) for i := 0; i < len(items); i++ { @@ -2255,7 +2269,7 @@ func TestCLEARITEMS(t *testing.T) { } func TestPOPITEM(t *testing.T) { - testPOPITEM := func(t *testing.T, item, elem, arr interface{}) { + testPOPITEM := func(t *testing.T, item, elem, arr any) { prog := makeProgram(opcode.DUP, opcode.POPITEM) v := load(prog) v.estack.PushVal(item) @@ -2351,51 +2365,51 @@ func TestDupBool(t *testing.T) { var opcodesTestCases = map[opcode.Opcode][]struct { name string - args []interface{} - expected interface{} - actual func(vm *VM) interface{} + args []any + expected any + actual func(vm *VM) any }{ opcode.AND: { { name: "1_1", - args: []interface{}{1, 1}, + args: []any{1, 1}, expected: int64(1), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, { name: "1_0", - args: []interface{}{1, 0}, + args: []any{1, 0}, expected: int64(0), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, { name: "0_1", - args: []interface{}{0, 1}, + args: []any{0, 1}, expected: int64(0), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, { name: "0_0", - args: []interface{}{0, 0}, + args: []any{0, 0}, expected: int64(0), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, { name: "random_values", - args: []interface{}{ + args: []any{ []byte{1, 0, 1, 0, 1, 0, 1, 1}, []byte{1, 1, 0, 0, 0, 0, 0, 1}, }, expected: []byte{1, 0, 0, 0, 0, 0, 0, 1}, - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().Bytes() }, }, @@ -2403,44 +2417,44 @@ var opcodesTestCases = map[opcode.Opcode][]struct { opcode.OR: { { name: "1_1", - args: []interface{}{1, 1}, + args: []any{1, 1}, expected: int64(1), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, { name: "0_0", - args: []interface{}{0, 0}, + args: []any{0, 0}, expected: int64(0), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, { name: "0_1", - args: []interface{}{0, 1}, + args: []any{0, 1}, expected: int64(1), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, { name: "1_0", - args: []interface{}{1, 0}, + args: []any{1, 0}, expected: int64(1), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, { name: "random_values", - args: []interface{}{ + args: []any{ []byte{1, 0, 1, 0, 1, 0, 1, 1}, []byte{1, 1, 0, 0, 0, 0, 0, 1}, }, expected: []byte{1, 1, 1, 0, 1, 0, 1, 1}, - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().Bytes() }, }, @@ -2448,44 +2462,44 @@ var opcodesTestCases = map[opcode.Opcode][]struct { opcode.XOR: { { name: "1_1", - args: []interface{}{1, 1}, + args: []any{1, 1}, expected: int64(0), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, { name: "0_0", - args: []interface{}{0, 0}, + args: []any{0, 0}, expected: int64(0), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, { name: "0_1", - args: []interface{}{0, 1}, + args: []any{0, 1}, expected: int64(1), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, { name: "1_0", - args: []interface{}{1, 0}, + args: []any{1, 0}, expected: int64(1), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, { name: "random_values", - args: []interface{}{ + args: []any{ []byte{1, 0, 1, 0, 1, 0, 1, 1}, []byte{1, 1, 0, 0, 0, 0, 0, 1}, }, expected: []byte{0, 1, 1, 0, 1, 0, 1}, - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().Bytes() }, }, @@ -2493,33 +2507,33 @@ var opcodesTestCases = map[opcode.Opcode][]struct { opcode.BOOLOR: { { name: "1_1", - args: []interface{}{true, true}, + args: []any{true, true}, expected: true, - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().Bool() }, }, { name: "0_0", - args: []interface{}{false, false}, + args: []any{false, false}, expected: false, - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().Bool() }, }, { name: "0_1", - args: []interface{}{false, true}, + args: []any{false, true}, expected: true, - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().Bool() }, }, { name: "1_0", - args: []interface{}{true, false}, + args: []any{true, false}, expected: true, - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().Bool() }, }, @@ -2527,25 +2541,25 @@ var opcodesTestCases = map[opcode.Opcode][]struct { opcode.MIN: { { name: "3_5", - args: []interface{}{3, 5}, + args: []any{3, 5}, expected: int64(3), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, { name: "5_3", - args: []interface{}{5, 3}, + args: []any{5, 3}, expected: int64(3), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, { name: "3_3", - args: []interface{}{3, 3}, + args: []any{3, 3}, expected: int64(3), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, @@ -2553,25 +2567,25 @@ var opcodesTestCases = map[opcode.Opcode][]struct { opcode.MAX: { { name: "3_5", - args: []interface{}{3, 5}, + args: []any{3, 5}, expected: int64(5), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, { name: "5_3", - args: []interface{}{5, 3}, + args: []any{5, 3}, expected: int64(5), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, { name: "3_3", - args: []interface{}{3, 3}, + args: []any{3, 3}, expected: int64(3), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, @@ -2579,25 +2593,25 @@ var opcodesTestCases = map[opcode.Opcode][]struct { opcode.WITHIN: { { name: "within", - args: []interface{}{4, 3, 5}, + args: []any{4, 3, 5}, expected: true, - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().Bool() }, }, { name: "less", - args: []interface{}{2, 3, 5}, + args: []any{2, 3, 5}, expected: false, - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().Bool() }, }, { name: "more", - args: []interface{}{6, 3, 5}, + args: []any{6, 3, 5}, expected: false, - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().Bool() }, }, @@ -2605,25 +2619,25 @@ var opcodesTestCases = map[opcode.Opcode][]struct { opcode.NEGATE: { { name: "3", - args: []interface{}{3}, + args: []any{3}, expected: int64(-3), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, { name: "-3", - args: []interface{}{-3}, + args: []any{-3}, expected: int64(3), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, { name: "0", - args: []interface{}{0}, + args: []any{0}, expected: int64(0), - actual: func(vm *VM) interface{} { + actual: func(vm *VM) any { return vm.estack.Pop().BigInt().Int64() }, }, diff --git a/scripts/update_deps.sh b/scripts/update_deps.sh index 603ee2b7a..5a1d71028 100755 --- a/scripts/update_deps.sh +++ b/scripts/update_deps.sh @@ -15,7 +15,7 @@ go mod tidy for dir in "$root"/examples/*/; do cd "$dir" || exit 1 go get github.com/nspcc-dev/neo-go/pkg/interop@"$REV" - go mod tidy --compat=1.17 + go mod tidy done cd "$root"/internal/contracts/oracle_contract || exit 1