From 68b469a79d6f3ff8ba451688aec8d1a6f24a19a2 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 19 May 2021 12:19:33 +0300 Subject: [PATCH] [#505] neofsid: Implement contract client Implement NeoFS ID contact's client which is responsible for collecting call arguments and parsing stack items. Initially only key listing method is supported. Signed-off-by: Leonard Lyubich --- pkg/morph/client/neofsid/client.go | 78 ++++++++++++++++++++++++++++++ pkg/morph/client/neofsid/keys.go | 69 ++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 pkg/morph/client/neofsid/client.go create mode 100644 pkg/morph/client/neofsid/keys.go diff --git a/pkg/morph/client/neofsid/client.go b/pkg/morph/client/neofsid/client.go new file mode 100644 index 000000000..06008d224 --- /dev/null +++ b/pkg/morph/client/neofsid/client.go @@ -0,0 +1,78 @@ +package neofsid + +import ( + "github.com/nspcc-dev/neofs-node/pkg/morph/client" +) + +// Client is a wrapper over StaticClient +// which makes calls with the names and arguments +// of the NeoFS ID contract. +// +// Working client must be created via constructor New. +// Using the Client that has been created with new(Client) +// expression (or just declaring a Client variable) is unsafe +// and can lead to panic. +type Client struct { + client *client.StaticClient // static NeoFS ID contract client + + *cfg // contract method names +} + +// Option is a client configuration change function. +type Option func(*cfg) + +type cfg struct { + keyListingMethod string +} + +const ( + defaultKeyListingMethod = "key" // default key listing method name +) + +func defaultConfig() *cfg { + return &cfg{ + keyListingMethod: defaultKeyListingMethod, + } +} + +// New creates, initializes and returns the Client instance. +// +// If StaticClient is nil, panic occurs. +// +// Other values are set according to provided options, or by default: +// * key listing method name: key. +// +// If desired option satisfies the default value, it can be omitted. +// If multiple options of the same config value are supplied, +// the option with the highest index in the arguments will be used. +func New(c *client.StaticClient, opts ...Option) *Client { + if c == nil { + panic("static client is nil") + } + + res := &Client{ + client: c, + cfg: defaultConfig(), // build default configuration + } + + // apply options + for _, opt := range opts { + opt(res.cfg) + } + + return res +} + +// WithKeyListingMethod returns a client constructor option that +// specifies the method name of key listing operation. +// +// Ignores empty value. +// +// If option not provided, "key" is used. +func WithKeyListingMethod(n string) Option { + return func(c *cfg) { + if n != "" { + c.keyListingMethod = n + } + } +} diff --git a/pkg/morph/client/neofsid/keys.go b/pkg/morph/client/neofsid/keys.go new file mode 100644 index 000000000..414352a00 --- /dev/null +++ b/pkg/morph/client/neofsid/keys.go @@ -0,0 +1,69 @@ +package neofsid + +import ( + "fmt" + + "github.com/nspcc-dev/neofs-node/pkg/morph/client" +) + +// KeyListingArgs groups the arguments +// of key listing call. +type KeyListingArgs struct { + ownerID []byte // account identifier +} + +// KeyListingValues groups the stack parameters +// returned by key listing call. +type KeyListingValues struct { + keys [][]byte // list of user public keys in binary format +} + +// SetOwnerID sets the NeoFS account identifier +// in a binary format. +func (l *KeyListingArgs) SetOwnerID(v []byte) { + l.ownerID = v +} + +// Keys returns the list of account keys +// in a binary format. +func (l *KeyListingValues) Keys() [][]byte { + return l.keys +} + +// AccountKeys requests public keys of NeoFS account +// through method of NeoFS ID contract. +func (x *Client) AccountKeys(args KeyListingArgs) (*KeyListingValues, error) { + invokeArgs := make([]interface{}, 0, 1) + + invokeArgs = append(invokeArgs, args.ownerID) + + items, err := x.client.TestInvoke( + x.keyListingMethod, + invokeArgs..., + ) + if err != nil { + return nil, fmt.Errorf("could not perform test invocation (%s): %w", x.keyListingMethod, err) + } else if ln := len(items); ln != 1 { + return nil, fmt.Errorf("unexpected stack item count (%s): %d", x.keyListingMethod, ln) + } + + items, err = client.ArrayFromStackItem(items[0]) + if err != nil { + return nil, fmt.Errorf("1st stack item must be an array (%s)", x.keyListingMethod) + } + + keys := make([][]byte, 0, len(items)) + + for i := range items { + key, err := client.BytesFromStackItem(items[i]) + if err != nil { + return nil, fmt.Errorf("invalid stack item, expected byte array (%s)", x.keyListingMethod) + } + + keys = append(keys, key) + } + + return &KeyListingValues{ + keys: keys, + }, nil +}