From d0702c2cf974b45963704ec0e31717d1daba939b Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 19 Aug 2022 10:36:44 +0300 Subject: [PATCH 1/2] unwrap: provide ArrayOfUint160 It's a popular type as well. --- pkg/rpcclient/unwrap/unwrap.go | 22 ++++++++++++++++++++++ pkg/rpcclient/unwrap/unwrap_test.go | 17 +++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/pkg/rpcclient/unwrap/unwrap.go b/pkg/rpcclient/unwrap/unwrap.go index fd1d27a90..0e91a3d01 100644 --- a/pkg/rpcclient/unwrap/unwrap.go +++ b/pkg/rpcclient/unwrap/unwrap.go @@ -195,6 +195,28 @@ func ArrayOfBytes(r *result.Invoke, err error) ([][]byte, error) { return res, nil } +// ArrayOfUint160 checks the result for correct state (HALT) and then extracts a +// slice of util.Uint160 from the returned stack item. +func ArrayOfUint160(r *result.Invoke, err error) ([]util.Uint160, error) { + a, err := Array(r, err) + if err != nil { + return nil, err + } + res := make([]util.Uint160, len(a)) + for i := range a { + b, err := a[i].TryBytes() + if err != nil { + return nil, fmt.Errorf("element %d is not a byte string: %w", i, err) + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return nil, fmt.Errorf("element %d is not a uint160: %w", i, err) + } + res[i] = u + } + return res, nil +} + // ArrayOfPublicKeys checks the result for correct state (HALT) and then // extracts a slice of public keys from the returned stack item. func ArrayOfPublicKeys(r *result.Invoke, err error) (keys.PublicKeys, error) { diff --git a/pkg/rpcclient/unwrap/unwrap_test.go b/pkg/rpcclient/unwrap/unwrap_test.go index 2f895a07e..29606da5c 100644 --- a/pkg/rpcclient/unwrap/unwrap_test.go +++ b/pkg/rpcclient/unwrap/unwrap_test.go @@ -231,6 +231,23 @@ func TestArrayOfBytes(t *testing.T) { require.Equal(t, []byte("some"), a[0]) } +func TestArrayOfUint160(t *testing.T) { + _, err := ArrayOfUint160(&result.Invoke{State: "HALT", Stack: []stackitem.Item{stackitem.Make(42)}}, nil) + require.Error(t, err) + + _, err = ArrayOfUint160(&result.Invoke{State: "HALT", Stack: []stackitem.Item{stackitem.Make([]stackitem.Item{stackitem.Make([]stackitem.Item{})})}}, nil) + require.Error(t, err) + + _, err = ArrayOfUint160(&result.Invoke{State: "HALT", Stack: []stackitem.Item{stackitem.Make([]stackitem.Item{stackitem.Make([]byte("some"))})}}, nil) + require.Error(t, err) + + u160 := util.Uint160{1, 2, 3} + uints, err := ArrayOfUint160(&result.Invoke{State: "HALT", Stack: []stackitem.Item{stackitem.Make([]stackitem.Item{stackitem.Make(u160.BytesBE())})}}, nil) + require.NoError(t, err) + require.Equal(t, 1, len(uints)) + require.Equal(t, u160, uints[0]) +} + func TestArrayOfPublicKeys(t *testing.T) { _, err := ArrayOfPublicKeys(&result.Invoke{State: "HALT", Stack: []stackitem.Item{stackitem.Make(42)}}, nil) require.Error(t, err) From 194933a5cc068746d1d6e3f3e98ffe67bf40b237 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 19 Aug 2022 10:37:22 +0300 Subject: [PATCH 2/2] rpcclient: provide nep11 package for NEP-11 tokens Unfortunately Go doesn't allow to easily reuse readers in full packages, still we can have this wrapper with a little overhead (the alternative is to move specific methods into types of their own, but I'm not sure how it's going to be accepted user-side). --- cli/wallet/nep11.go | 40 +-- pkg/rpcclient/nep11.go | 48 ++++ pkg/rpcclient/nep11/base.go | 247 ++++++++++++++++++ pkg/rpcclient/nep11/base_test.go | 305 +++++++++++++++++++++++ pkg/rpcclient/nep11/divisible.go | 157 ++++++++++++ pkg/rpcclient/nep11/divisible_test.go | 218 ++++++++++++++++ pkg/rpcclient/nep11/nondivisible.go | 39 +++ pkg/rpcclient/nep11/nondivisible_test.go | 49 ++++ pkg/rpcclient/rpc.go | 2 +- pkg/services/rpcsrv/client_test.go | 84 ++++--- 10 files changed, 1144 insertions(+), 45 deletions(-) create mode 100644 pkg/rpcclient/nep11/base.go create mode 100644 pkg/rpcclient/nep11/base_test.go create mode 100644 pkg/rpcclient/nep11/divisible.go create mode 100644 pkg/rpcclient/nep11/divisible_test.go create mode 100644 pkg/rpcclient/nep11/nondivisible.go create mode 100644 pkg/rpcclient/nep11/nondivisible_test.go diff --git a/cli/wallet/nep11.go b/cli/wallet/nep11.go index 82154e056..3b81dfc39 100644 --- a/cli/wallet/nep11.go +++ b/cli/wallet/nep11.go @@ -5,16 +5,20 @@ import ( "errors" "fmt" "math/big" + "strconv" "github.com/nspcc-dev/neo-go/cli/cmdargs" "github.com/nspcc-dev/neo-go/cli/flags" "github.com/nspcc-dev/neo-go/cli/input" "github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/cli/paramcontext" + "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" "github.com/nspcc-dev/neo-go/pkg/rpcclient" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -23,6 +27,7 @@ import ( ) func newNEP11Commands() []cli.Command { + maxIters := strconv.Itoa(config.DefaultMaxIteratorResultItems) tokenAddressFlag := flags.AddressFlag{ Name: "token", Usage: "Token contract address or hash in LE", @@ -119,7 +124,7 @@ func newNEP11Commands() []cli.Command { }, { Name: "ownerOfD", - Usage: "print set of owners of divisible NEP-11 token with the specified ID (the default MaxIteratorResultItems will be printed at max)", + Usage: "print set of owners of divisible NEP-11 token with the specified ID (" + maxIters + " will be printed at max)", UsageText: "ownerOfD --rpc-endpoint --timeout