unwrap: add ArrayOfPublicKeys()

This type of result is also popular in the NEO contract.
This commit is contained in:
Roman Khimov 2022-08-16 22:32:46 +03:00
parent cdc2a762a1
commit 37619743ad
3 changed files with 47 additions and 19 deletions

View file

@ -7,9 +7,6 @@ various methods to perform the only RoleManagement state-changing call.
package rolemgmt package rolemgmt
import ( import (
"crypto/elliptic"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" "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/native/noderoles"
"github.com/nspcc-dev/neo-go/pkg/core/state" "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 // given role at the given height. The list can be empty if no keys are
// configured for this role/height. // configured for this role/height.
func (c *ContractReader) GetDesignatedByRole(role noderoles.Role, index uint32) (keys.PublicKeys, error) { func (c *ContractReader) GetDesignatedByRole(role noderoles.Role, index uint32) (keys.PublicKeys, error) {
arr, err := unwrap.Array(c.invoker.Call(Hash, "getDesignatedByRole", int64(role), index)) return unwrap.ArrayOfPublicKeys(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
} }
// DesignateAsRole creates and sends a transaction that sets the keys used for // DesignateAsRole creates and sends a transaction that sets the keys used for

View file

@ -11,12 +11,14 @@ contract-specific packages.
package unwrap package unwrap
import ( import (
"crypto/elliptic"
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
"unicode/utf8" "unicode/utf8"
"github.com/google/uuid" "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/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
@ -190,6 +192,27 @@ func ArrayOfBytes(r *result.Invoke, err error) ([][]byte, error) {
return res, nil 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 // Map expects correct execution (HALT state) with a single stack item
// returned. A stackitem.Map is extracted from this item and returned. // returned. A stackitem.Map is extracted from this item and returned.
func Map(r *result.Invoke, err error) (*stackitem.Map, error) { func Map(r *result.Invoke, err error) (*stackitem.Map, error) {

View file

@ -7,6 +7,7 @@ import (
"testing" "testing"
"github.com/google/uuid" "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/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
@ -52,6 +53,9 @@ func TestStdErrors(t *testing.T) {
func(r *result.Invoke, err error) (interface{}, error) { func(r *result.Invoke, err error) (interface{}, error) {
return ArrayOfBytes(r, err) return ArrayOfBytes(r, err)
}, },
func(r *result.Invoke, err error) (interface{}, error) {
return ArrayOfPublicKeys(r, err)
},
func(r *result.Invoke, err error) (interface{}, error) { func(r *result.Invoke, err error) (interface{}, error) {
return Map(r, err) return Map(r, err)
}, },
@ -224,6 +228,25 @@ func TestArrayOfBytes(t *testing.T) {
require.Equal(t, []byte("some"), a[0]) 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) { func TestMap(t *testing.T) {
_, err := Map(&result.Invoke{State: "HALT", Stack: []stackitem.Item{stackitem.Make(42)}}, nil) _, err := Map(&result.Invoke{State: "HALT", Stack: []stackitem.Item{stackitem.Make(42)}}, nil)
require.Error(t, err) require.Error(t, err)