From 9f1a49a562cd8f90864e15e13aa795be9e45e775 Mon Sep 17 00:00:00 2001 From: Alex Vanin Date: Wed, 31 Mar 2021 17:02:02 +0300 Subject: [PATCH] [#452] morph/client: Add reputation contract client Signed-off-by: Alex Vanin --- pkg/morph/client/reputation/client.go | 128 ++++++++++++++++++++++++++ pkg/morph/client/reputation/get.go | 99 ++++++++++++++++++++ pkg/morph/client/reputation/list.go | 62 +++++++++++++ pkg/morph/client/reputation/put.go | 48 ++++++++++ 4 files changed, 337 insertions(+) create mode 100644 pkg/morph/client/reputation/client.go create mode 100644 pkg/morph/client/reputation/get.go create mode 100644 pkg/morph/client/reputation/list.go create mode 100644 pkg/morph/client/reputation/put.go diff --git a/pkg/morph/client/reputation/client.go b/pkg/morph/client/reputation/client.go new file mode 100644 index 00000000..34b5290c --- /dev/null +++ b/pkg/morph/client/reputation/client.go @@ -0,0 +1,128 @@ +package reputation + +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 reputation 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 reputation contract client + + *cfg // contract method names +} + +// Option is a client configuration change function. +type Option func(*cfg) + +type cfg struct { + putMethod, + getMethod, + getByIDMethod, + listByEpochMethod string +} + +const ( + defaultPutMethod = "put" + defaultGetMethod = "get" + defaultGetByIDMethod = "getByID" + defaultListByEpochMethod = "listByEpoch" +) + +func defaultConfig() *cfg { + return &cfg{ + putMethod: defaultPutMethod, + getMethod: defaultGetMethod, + getByIDMethod: defaultGetByIDMethod, + listByEpochMethod: defaultListByEpochMethod, + } +} + +// New creates, initializes and returns the Client instance. +// +// If StaticClient is nil, client.ErrNilStaticClient is returned. +// +// Other values are set according to provided options, or by default. +// +// 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, error) { + if c == nil { + return nil, client.ErrNilStaticClient + } + + res := &Client{ + client: c, + cfg: defaultConfig(), // build default configuration + } + + // apply options + for _, opt := range opts { + opt(res.cfg) + } + + return res, nil +} + +// WithPutMethod returns a client constructor option that +// specifies the method name to put reputation value. +// +// Ignores empty value. +// +// If option not provided, "put" is used. +func WithPutMethod(n string) Option { + return func(c *cfg) { + if n != "" { + c.putMethod = n + } + } +} + +// WithGetMethod returns a client constructor option that +// specifies the method name to get reputation value. +// +// Ignores empty value. +// +// If option not provided, "get" is used. +func WithGetMethod(n string) Option { + return func(c *cfg) { + if n != "" { + c.getMethod = n + } + } +} + +// WithGetByIDMethod returns a client constructor option that +// specifies the method name to get reputation value by it's ID in the contract. +// +// Ignores empty value. +// +// If option not provided, "getByID" is used. +func WithGetByIDMethod(n string) Option { + return func(c *cfg) { + if n != "" { + c.getByIDMethod = n + } + } +} + +// WithListByEpochMethod returns a client constructor option that +// specifies the method name to list reputation value IDs for certain epoch. +// +// Ignores empty value. +// +// If option not provided, "listByEpoch" is used. +func WithListByEpochDMethod(n string) Option { + return func(c *cfg) { + if n != "" { + c.listByEpochMethod = n + } + } +} diff --git a/pkg/morph/client/reputation/get.go b/pkg/morph/client/reputation/get.go new file mode 100644 index 00000000..c04f05b6 --- /dev/null +++ b/pkg/morph/client/reputation/get.go @@ -0,0 +1,99 @@ +package reputation + +import ( + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/pkg/errors" +) + +// GetArgs groups the arguments of "get reputation value" test invocation. +type GetArgs struct { + epoch uint64 + peerID []byte // object of reputation evaluation +} + +// GetByIDArgs groups the arguments of "get reputation value by reputation id" +// test invocation. +type GetByIDArgs struct { + id []byte // id of reputation value in reputation contract +} + +// GetResult groups the stack parameters returned by +// "get" and "get by id" test invocations. +type GetResult struct { + reputations [][]byte +} + +// SetEpoch sets epoch of expected reputation value. +func (g *GetArgs) SetEpoch(v uint64) { + g.epoch = v +} + +// SetPeerID sets peer id of expected reputation value. +func (g *GetArgs) SetPeerID(v []byte) { + g.peerID = v +} + +// SetID sets id of expected reputation value in reputation contract. +func (g *GetByIDArgs) SetID(v []byte) { + g.id = v +} + +// Reputations returns slice of marshalled reputation values. +func (g GetResult) Reputations() [][]byte { + return g.reputations +} + +// Get invokes the call of "get reputation value" method of reputation contract. +func (c *Client) Get(args GetArgs) (*GetResult, error) { + prms, err := c.client.TestInvoke( + c.getMethod, + int64(args.epoch), + args.peerID, + ) + if err != nil { + return nil, errors.Wrapf(err, "could not perform test invocation (%s)", c.getMethod) + } + + return parseReputations(prms, c.getMethod) +} + +// GetByID invokes the call of "get reputation value by reputation id" method +// of reputation contract. +func (c *Client) GetByID(args GetByIDArgs) (*GetResult, error) { + prms, err := c.client.TestInvoke( + c.getByIDMethod, + args.id, + ) + if err != nil { + return nil, errors.Wrapf(err, "could not perform test invocation (%s)", c.getByIDMethod) + } + + return parseReputations(prms, c.getByIDMethod) +} + +func parseReputations(items []stackitem.Item, method string) (*GetResult, error) { + if ln := len(items); ln != 1 { + return nil, errors.Errorf("unexpected stack item count (%s): %d", method, ln) + } + + items, err := client.ArrayFromStackItem(items[0]) + if err != nil { + return nil, errors.Wrapf(err, "could not get stack item array from stack item (%s)", method) + } + + res := &GetResult{ + reputations: make([][]byte, 0, len(items)), + } + + for i := range items { + rawReputation, err := client.BytesFromStackItem(items[i]) + if err != nil { + return nil, errors.Wrapf(err, "could not get byte array from stack item (%s)", method) + } + + res.reputations = append(res.reputations, rawReputation) + } + + return res, nil +} diff --git a/pkg/morph/client/reputation/list.go b/pkg/morph/client/reputation/list.go new file mode 100644 index 00000000..89ddfdf6 --- /dev/null +++ b/pkg/morph/client/reputation/list.go @@ -0,0 +1,62 @@ +package reputation + +import ( + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/pkg/errors" +) + +// ListByEpochArgs groups the arguments of +// "list reputation ids by epoch" test invoke call. +type ListByEpochArgs struct { + epoch uint64 +} + +// ListByEpochResult groups the stack parameters +// returned by "list reputation ids by epoch" test invoke. +type ListByEpochResult struct { + ids [][]byte +} + +// SetEpoch sets epoch of expected reputation ids. +func (l *ListByEpochArgs) SetEpoch(v uint64) { + l.epoch = v +} + +// IDs returns slice of reputation id values. +func (l ListByEpochResult) IDs() [][]byte { + return l.ids +} + +// ListByEpoch invokes the call of "list reputation ids by epoch" method of +// reputation contract. +func (c *Client) ListByEpoch(args ListByEpochArgs) (*ListByEpochResult, error) { + prms, err := c.client.TestInvoke( + c.listByEpochMethod, + int64(args.epoch), + ) + if err != nil { + return nil, errors.Wrapf(err, "could not perform test invocation (%s)", c.listByEpochMethod) + } else if ln := len(prms); ln != 1 { + return nil, errors.Errorf("unexpected stack item count (%s): %d", c.listByEpochMethod, ln) + } + + items, err := client.ArrayFromStackItem(prms[0]) + if err != nil { + return nil, errors.Wrapf(err, "could not get stack item array from stack item (%s)", c.listByEpochMethod) + } + + res := &ListByEpochResult{ + ids: make([][]byte, 0, len(items)), + } + + for i := range items { + rawReputation, err := client.BytesFromStackItem(items[i]) + if err != nil { + return nil, errors.Wrapf(err, "could not get byte array from stack item (%s)", c.listByEpochMethod) + } + + res.ids = append(res.ids, rawReputation) + } + + return res, nil +} diff --git a/pkg/morph/client/reputation/put.go b/pkg/morph/client/reputation/put.go new file mode 100644 index 00000000..dfd6d29a --- /dev/null +++ b/pkg/morph/client/reputation/put.go @@ -0,0 +1,48 @@ +package reputation + +import ( + "github.com/pkg/errors" +) + +// PutArgs groups the arguments of "put reputation value" invocation call. +type PutArgs struct { + epoch uint64 + peerID []byte + value []byte +} + +// SetEpoch sets epoch of reputation value. +func (p *PutArgs) SetEpoch(v uint64) { + p.epoch = v +} + +// SetPeerID sets peer id of reputation value. +func (p *PutArgs) SetPeerID(v []byte) { + p.peerID = v +} + +// SetValue sets marshaled reputation value. +func (p *PutArgs) SetValue(v []byte) { + p.value = v +} + +// Put invokes direct call of "put reputation value" method of reputation contract. +func (c *Client) Put(args PutArgs) error { + return errors.Wrapf(c.client.Invoke( + c.putMethod, + int64(args.epoch), + args.peerID, + args.value, + ), "could not invoke method (%s)", c.putMethod) +} + +// PutViaNotary invokes notary call of "put reputation value" method of +// reputation contract. +func (c *Client) PutViaNotary(args PutArgs) error { + return errors.Wrapf(c.client.NotaryInvoke( + c.putMethod, + int64(args.epoch), + args.peerID, + args.value, + ), "could not invoke method (%s)", c.putMethod) +}