diff --git a/pkg/rpcclient/rolemgmt/roles.go b/pkg/rpcclient/rolemgmt/roles.go index d153feb9c..c7593e4b5 100644 --- a/pkg/rpcclient/rolemgmt/roles.go +++ b/pkg/rpcclient/rolemgmt/roles.go @@ -7,9 +7,6 @@ various methods to perform the only RoleManagement state-changing call. package rolemgmt import ( - "crypto/elliptic" - "fmt" - "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" @@ -78,22 +75,7 @@ func New(actor Actor) *Contract { // given role at the given height. The list can be empty if no keys are // configured for this role/height. func (c *ContractReader) GetDesignatedByRole(role noderoles.Role, index uint32) (keys.PublicKeys, error) { - arr, err := unwrap.Array(c.invoker.Call(Hash, "getDesignatedByRole", int64(role), index)) - if err != nil { - return nil, err - } - pks := make(keys.PublicKeys, len(arr)) - for i, item := range arr { - val, err := item.TryBytes() - if err != nil { - return nil, fmt.Errorf("invalid array element #%d: %s", i, item.Type()) - } - pks[i], err = keys.NewPublicKeyFromBytes(val, elliptic.P256()) - if err != nil { - return nil, err - } - } - return pks, nil + return unwrap.ArrayOfPublicKeys(c.invoker.Call(Hash, "getDesignatedByRole", int64(role), index)) } // DesignateAsRole creates and sends a transaction that sets the keys used for diff --git a/pkg/rpcclient/unwrap/unwrap.go b/pkg/rpcclient/unwrap/unwrap.go index 208b041c1..de82e6121 100644 --- a/pkg/rpcclient/unwrap/unwrap.go +++ b/pkg/rpcclient/unwrap/unwrap.go @@ -11,12 +11,14 @@ contract-specific packages. package unwrap import ( + "crypto/elliptic" "errors" "fmt" "math/big" "unicode/utf8" "github.com/google/uuid" + "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" @@ -190,6 +192,27 @@ func ArrayOfBytes(r *result.Invoke, err error) ([][]byte, error) { 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) { + arr, err := Array(r, err) + if err != nil { + return nil, err + } + pks := make(keys.PublicKeys, len(arr)) + for i, item := range arr { + val, err := item.TryBytes() + if err != nil { + return nil, fmt.Errorf("invalid array element #%d: %s", i, item.Type()) + } + pks[i], err = keys.NewPublicKeyFromBytes(val, elliptic.P256()) + if err != nil { + return nil, fmt.Errorf("array element #%d in not a key: %w", i, err) + } + } + return pks, nil +} + // Map expects correct execution (HALT state) with a single stack item // returned. A stackitem.Map is extracted from this item and returned. func Map(r *result.Invoke, err error) (*stackitem.Map, error) { diff --git a/pkg/rpcclient/unwrap/unwrap_test.go b/pkg/rpcclient/unwrap/unwrap_test.go index e62383239..60fc60088 100644 --- a/pkg/rpcclient/unwrap/unwrap_test.go +++ b/pkg/rpcclient/unwrap/unwrap_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/google/uuid" + "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" @@ -52,6 +53,9 @@ func TestStdErrors(t *testing.T) { func(r *result.Invoke, err error) (interface{}, error) { return ArrayOfBytes(r, err) }, + func(r *result.Invoke, err error) (interface{}, error) { + return ArrayOfPublicKeys(r, err) + }, func(r *result.Invoke, err error) (interface{}, error) { return Map(r, err) }, @@ -224,6 +228,25 @@ func TestArrayOfBytes(t *testing.T) { require.Equal(t, []byte("some"), a[0]) } +func TestArrayOfPublicKeys(t *testing.T) { + _, err := ArrayOfPublicKeys(&result.Invoke{State: "HALT", Stack: []stackitem.Item{stackitem.Make(42)}}, nil) + require.Error(t, err) + + _, err = ArrayOfPublicKeys(&result.Invoke{State: "HALT", Stack: []stackitem.Item{stackitem.Make([]stackitem.Item{stackitem.Make([]stackitem.Item{})})}}, nil) + require.Error(t, err) + + _, err = ArrayOfPublicKeys(&result.Invoke{State: "HALT", Stack: []stackitem.Item{stackitem.Make([]stackitem.Item{stackitem.Make([]byte("some"))})}}, nil) + require.Error(t, err) + + k, err := keys.NewPrivateKey() + require.NoError(t, err) + + pks, err := ArrayOfPublicKeys(&result.Invoke{State: "HALT", Stack: []stackitem.Item{stackitem.Make([]stackitem.Item{stackitem.Make(k.PublicKey().Bytes())})}}, nil) + require.NoError(t, err) + require.Equal(t, 1, len(pks)) + require.Equal(t, k.PublicKey(), pks[0]) +} + func TestMap(t *testing.T) { _, err := Map(&result.Invoke{State: "HALT", Stack: []stackitem.Item{stackitem.Make(42)}}, nil) require.Error(t, err)