[#283] morph/client: Implement all calls for audit contract

Signed-off-by: Alex Vanin <alexey@nspcc.ru>
This commit is contained in:
Alex Vanin 2020-12-25 13:20:09 +03:00 committed by Alex Vanin
parent 7174abcc7c
commit fe2b1fdc4b
5 changed files with 312 additions and 30 deletions

View file

@ -23,18 +23,30 @@ type Option func(*cfg)
type cfg struct { type cfg struct {
putResultMethod, // put audit result method name for invocation putResultMethod, // put audit result method name for invocation
listResultsMethod string // list audit results method name for invocation getResultMethod, // get audit result method name for invocation
listResultsMethod string // list all audit result IDs method name for invocation
listByEpochResultsMethod string // list audit result IDs by epoch method name for invocation
listByCIDResultsMethod string // list audit result IDs by epoch and CID method name for invocation
listByNodeResultsMethod string // list audit result IDs by epoch, CID, and node key method name for invocation
} }
const ( const (
defaultPutResultMethod = "put" // default "put audit result" method name defaultPutResultMethod = "put" // default "put audit result" method name
defaultListResultsMethod = "list" // default "list audit results" method name defaultGetResultMethod = "get" // default "get audit result" method name
defaultListResultsMethod = "list" // default "list all audit result IDs" method name
defaultListByEpochResultsMethod = "listByEpoch" // default "list audit result IDs by epoch" method name
defaultListByCIDResultsMethod = "listByCID" // default "list audit result IDs by epoch and CID" method name
defaultListByNodeResultsMethod = "listByNode" // default "list audit result IDs by epoch, CID and node key" method name
) )
func defaultConfig() *cfg { func defaultConfig() *cfg {
return &cfg{ return &cfg{
putResultMethod: defaultPutResultMethod, putResultMethod: defaultPutResultMethod,
getResultMethod: defaultGetResultMethod,
listResultsMethod: defaultListResultsMethod, listResultsMethod: defaultListResultsMethod,
listByEpochResultsMethod: defaultListByEpochResultsMethod,
listByCIDResultsMethod: defaultListByCIDResultsMethod,
listByNodeResultsMethod: defaultListByNodeResultsMethod,
} }
} }
@ -73,8 +85,20 @@ func WithPutAuditResultMethod(n string) Option {
} }
} }
// WithGetAuditResultMethod returns a client constructor option that
// specifies the "get audit result" method name.
//
// Ignores empty value.
func WithGetAuditResultMethod(n string) Option {
return func(c *cfg) {
if n != "" {
c.getResultMethod = n
}
}
}
// WithListResultsMethod returns a client constructor option that // WithListResultsMethod returns a client constructor option that
// specifies the "list audit results" method name. // specifies the "list all audit result IDs" method name.
// //
// Ignores empty value. // Ignores empty value.
func WithListResultsMethod(n string) Option { func WithListResultsMethod(n string) Option {
@ -84,3 +108,39 @@ func WithListResultsMethod(n string) Option {
} }
} }
} }
// WithListByEpochResultsMethod returns a client constructor option that
// specifies the "list audit result IDs by epoch" method name.
//
// Ignores empty value.
func WithListByEpochResultsMethod(n string) Option {
return func(c *cfg) {
if n != "" {
c.listByEpochResultsMethod = n
}
}
}
// WithListByCIDResultsMethod returns a client constructor option that
// specifies the "list audit result IDs by epoch and CID" method name.
//
// Ignores empty value.
func WithListByCIDResultsMethod(n string) Option {
return func(c *cfg) {
if n != "" {
c.listByCIDResultsMethod = n
}
}
}
// WithListByNodeResultsMethod returns a client constructor option that
// specifies the "list audit result IDs by epoch, CID, and node key" method name.
//
// Ignores empty value.
func WithListByNodeResultsMethod(n string) Option {
return func(c *cfg) {
if n != "" {
c.listByNodeResultsMethod = n
}
}
}

View file

@ -0,0 +1,51 @@
package audit
import (
"github.com/nspcc-dev/neofs-node/pkg/morph/client"
"github.com/pkg/errors"
)
// GetAuditResultArgs groups the arguments
// of "get audit result" invocation call.
type GetAuditResultArgs struct {
id []byte
}
// GetAuditResultValue groups the stack parameters
// returned by "get audit result" test invoke.
type GetAuditResultValue struct {
rawResult []byte
}
// SetID sets audit result ID generated by audit contract.
func (g *GetAuditResultArgs) SetID(id []byte) {
g.id = id
}
// Result returns audit result structure in binary format.
func (v *GetAuditResultValue) Result() []byte {
return v.rawResult
}
// GetAuditResult invokes the call of "get audit result" method
// of NeoFS Audit contract.
func (c *Client) GetAuditResult(args GetAuditResultArgs) (*GetAuditResultValue, error) {
prms, err := c.client.TestInvoke(
c.getResultMethod,
args.id,
)
if err != nil {
return nil, errors.Wrapf(err, "could not perform test invocation (%s)", c.getResultMethod)
} else if ln := len(prms); ln != 1 {
return nil, errors.Errorf("unexpected stack item count (%s): %d", c.getResultMethod, ln)
}
resultBytes, err := client.BytesFromStackItem(prms[0])
if err != nil {
return nil, errors.Wrapf(err, "could not get byte array from stack item (%s)", c.getResultMethod)
}
return &GetAuditResultValue{
rawResult: resultBytes,
}, nil
}

View file

@ -1,27 +1,65 @@
package audit package audit
import ( import (
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neofs-node/pkg/morph/client" "github.com/nspcc-dev/neofs-node/pkg/morph/client"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// ListResultsArgs groups the arguments // ListResultsArgs groups the arguments
// of "list audit results" test invoke call. // of "list all audit result IDs" test invoke call.
type ListResultsArgs struct{} type ListResultsArgs struct{}
// ListResultsByEpochArgs groups the arguments
// of "list audit result IDs by epoch" test invoke call.
type ListResultsByEpochArgs struct {
epoch int64
}
// ListResultsByCIDArgs groups the arguments
// of "list audit result IDs by epoch and CID" test invoke call.
type ListResultsByCIDArgs struct {
ListResultsByEpochArgs
cid []byte
}
// ListResultsByNodeArgs groups the arguments
// of "list audit result IDs by epoch, CID, and node key" test invoke call.
type ListResultsByNodeArgs struct {
ListResultsByCIDArgs
nodeKey []byte
}
// ListResultsValues groups the stack parameters // ListResultsValues groups the stack parameters
// returned by "list audit results" test invoke. // returned by "list audit results" test invoke.
type ListResultsValues struct { type ListResultsValues struct {
rawResults [][]byte // audit results in a binary format rawResults [][]byte // audit results in a binary format
} }
// RawResults returns list of audit results // RawResults returns list of audit result IDs
// in a binary format. // in a binary format.
func (v *ListResultsValues) RawResults() [][]byte { func (v *ListResultsValues) RawResults() [][]byte {
return v.rawResults return v.rawResults
} }
// ListAuditResults performs the test invoke of "list audit results" // SetEpoch sets epoch of listing audit results.
func (v *ListResultsByEpochArgs) SetEpoch(epoch int64) {
v.epoch = epoch
}
// SetCID sets container ID of listing audit results.
func (v *ListResultsByCIDArgs) SetCID(cid []byte) {
v.cid = cid
}
// SetNodeKey sets public key of node that produced listing audit results.
func (v *ListResultsByNodeArgs) SetNodeKey(key []byte) {
v.nodeKey = key
}
// ListAuditResults performs the test invoke of "list all audit result IDs"
// method of NeoFS Audit contract. // method of NeoFS Audit contract.
func (c *Client) ListAuditResults(args ListResultsArgs) (*ListResultsValues, error) { func (c *Client) ListAuditResults(args ListResultsArgs) (*ListResultsValues, error) {
items, err := c.client.TestInvoke( items, err := c.client.TestInvoke(
@ -29,13 +67,64 @@ func (c *Client) ListAuditResults(args ListResultsArgs) (*ListResultsValues, err
) )
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "could not perform test invocation (%s)", c.listResultsMethod) return nil, errors.Wrapf(err, "could not perform test invocation (%s)", c.listResultsMethod)
} else if ln := len(items); ln != 1 {
return nil, errors.Errorf("unexpected stack item count (%s): %d", c.listResultsMethod, ln)
} }
items, err = client.ArrayFromStackItem(items[0]) return parseAuditResults(items, c.listResultsMethod)
}
// ListAuditResultsByEpoch performs the test invoke of "list audit result IDs
// by epoch" method of NeoFS Audit contract.
func (c *Client) ListAuditResultsByEpoch(args ListResultsByEpochArgs) (*ListResultsValues, error) {
items, err := c.client.TestInvoke(
c.listByEpochResultsMethod,
args.epoch,
)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "could not get stack item array from stack item (%s)", c.listResultsMethod) return nil, errors.Wrapf(err, "could not perform test invocation (%s)", c.listByEpochResultsMethod)
}
return parseAuditResults(items, c.listByEpochResultsMethod)
}
// ListAuditResultsByCID performs the test invoke of "list audit result IDs
// by epoch and CID" method of NeoFS Audit contract.
func (c *Client) ListAuditResultsByCID(args ListResultsByCIDArgs) (*ListResultsValues, error) {
items, err := c.client.TestInvoke(
c.listByCIDResultsMethod,
args.epoch,
args.cid,
)
if err != nil {
return nil, errors.Wrapf(err, "could not perform test invocation (%s)", c.listByCIDResultsMethod)
}
return parseAuditResults(items, c.listByCIDResultsMethod)
}
// ListAuditResultsByNode performs the test invoke of "list audit result IDs
// by epoch, CID, and node key" method of NeoFS Audit contract.
func (c *Client) ListAuditResultsByNode(args ListResultsByNodeArgs) (*ListResultsValues, error) {
items, err := c.client.TestInvoke(
c.listByNodeResultsMethod,
args.epoch,
args.cid,
args.nodeKey,
)
if err != nil {
return nil, errors.Wrapf(err, "could not perform test invocation (%s)", c.listByNodeResultsMethod)
}
return parseAuditResults(items, c.listByNodeResultsMethod)
}
func parseAuditResults(items []stackitem.Item, method string) (*ListResultsValues, 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 := &ListResultsValues{ res := &ListResultsValues{
@ -45,7 +134,7 @@ func (c *Client) ListAuditResults(args ListResultsArgs) (*ListResultsValues, err
for i := range items { for i := range items {
rawRes, err := client.BytesFromStackItem(items[i]) rawRes, err := client.BytesFromStackItem(items[i])
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "could not get byte array from stack item (%s)", c.listResultsMethod) return nil, errors.Wrapf(err, "could not get byte array from stack item (%s)", method)
} }
res.rawResults = append(res.rawResults, rawRes) res.rawResults = append(res.rawResults, rawRes)

View file

@ -2,10 +2,16 @@ package audit
import ( import (
auditAPI "github.com/nspcc-dev/neofs-api-go/pkg/audit" auditAPI "github.com/nspcc-dev/neofs-api-go/pkg/audit"
"github.com/nspcc-dev/neofs-api-go/pkg/container"
"github.com/nspcc-dev/neofs-node/pkg/morph/client/audit" "github.com/nspcc-dev/neofs-node/pkg/morph/client/audit"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// ResultID is an identity of audit result inside audit contract.
type ResultID []byte
var errUnsupported = errors.New("unsupported structure version")
// PutAuditResult saves passed audit result structure in NeoFS system // PutAuditResult saves passed audit result structure in NeoFS system
// through Audit contract call. // through Audit contract call.
// //
@ -24,9 +30,8 @@ func (w *ClientWrapper) PutAuditResult(result *auditAPI.Result) error {
PutAuditResult(args) PutAuditResult(args)
} }
// ListAuditResults returns a list of all audit results in NeoFS system. // ListAuditResults returns a list of all audit result IDs inside audit contract.
// The list is composed through Audit contract call. func (w *ClientWrapper) ListAllAuditResultID() ([]ResultID, error) {
func (w *ClientWrapper) ListAuditResults() ([]*auditAPI.Result, error) {
args := audit.ListResultsArgs{} args := audit.ListResultsArgs{}
values, err := (*audit.Client)(w).ListAuditResults(args) values, err := (*audit.Client)(w).ListAuditResults(args)
@ -34,17 +39,89 @@ func (w *ClientWrapper) ListAuditResults() ([]*auditAPI.Result, error) {
return nil, err return nil, err
} }
return parseRawResult(values), nil
}
// ListAuditResultIDByEpoch returns a list of audit result IDs inside audit
// contract for specific epoch number.
func (w *ClientWrapper) ListAuditResultIDByEpoch(epoch uint64) ([]ResultID, error) {
args := audit.ListResultsByEpochArgs{}
args.SetEpoch(int64(epoch))
values, err := (*audit.Client)(w).ListAuditResultsByEpoch(args)
if err != nil {
return nil, err
}
return parseRawResult(values), nil
}
// ListAuditResultIDByCID returns a list of audit result IDs inside audit
// contract for specific epoch number and container ID.
func (w *ClientWrapper) ListAuditResultIDByCID(epoch uint64, cid *container.ID) ([]ResultID, error) {
args := audit.ListResultsByCIDArgs{}
args.SetEpoch(int64(epoch))
if v2 := cid.ToV2(); v2 == nil {
return nil, errUnsupported // use other major version if there any
} else {
args.SetCID(v2.GetValue())
}
values, err := (*audit.Client)(w).ListAuditResultsByCID(args)
if err != nil {
return nil, err
}
return parseRawResult(values), nil
}
// ListAuditResultIDByNode returns a list of audit result IDs inside audit
// contract for specific epoch number, container ID and inner ring public key.
func (w *ClientWrapper) ListAuditResultIDByNode(epoch uint64, cid *container.ID, key []byte) ([]ResultID, error) {
args := audit.ListResultsByNodeArgs{}
args.SetEpoch(int64(epoch))
args.SetNodeKey(key)
if v2 := cid.ToV2(); v2 == nil {
return nil, errUnsupported // use other major version if there any
} else {
args.SetCID(v2.GetValue())
}
values, err := (*audit.Client)(w).ListAuditResultsByNode(args)
if err != nil {
return nil, err
}
return parseRawResult(values), nil
}
func parseRawResult(values *audit.ListResultsValues) []ResultID {
rawResults := values.RawResults() rawResults := values.RawResults()
result := make([]*auditAPI.Result, 0, len(rawResults)) result := make([]ResultID, 0, len(rawResults))
for i := range rawResults { for i := range rawResults {
result = append(result, rawResults[i])
}
return result
}
// GetAuditResult returns audit result structure stored in audit contract.
func (w *ClientWrapper) GetAuditResult(id ResultID) (*auditAPI.Result, error) {
args := audit.GetAuditResultArgs{}
args.SetID(id)
value, err := (*audit.Client)(w).GetAuditResult(args)
if err != nil {
return nil, err
}
auditRes := auditAPI.NewResult() auditRes := auditAPI.NewResult()
if err := auditRes.Unmarshal(rawResults[i]); err != nil { if err := auditRes.Unmarshal(value.Result()); err != nil {
return nil, errors.Wrap(err, "could not unmarshal audit result structure") return nil, errors.Wrap(err, "could not unmarshal audit result structure")
} }
result = append(result, auditRes) return auditRes, nil
}
return result, nil
} }

View file

@ -17,9 +17,10 @@ import (
func TestAuditResults(t *testing.T) { func TestAuditResults(t *testing.T) {
t.Skip() t.Skip()
const epoch = 11
endpoint := "http://morph_chain.neofs.devenv:30333" endpoint := "http://morph_chain.neofs.devenv:30333"
sAuditHash := "96a746aa7186f775e5744a6e2c6566dc5c4a57a2" sAuditHash := "cdfb3dab86e6d60e8a143d9e2ecb0b188f3dc2eb"
irKeyWIF := "L3o221BojgcCPYgdbXsm6jn7ayTZ72xwREvBHXKknR8VJ3G4WmjB" irKeyWIF := "L3o221BojgcCPYgdbXsm6jn7ayTZ72xwREvBHXKknR8VJ3G4WmjB"
key, err := crypto.WIFDecode(irKeyWIF) key, err := crypto.WIFDecode(irKeyWIF)
@ -44,7 +45,7 @@ func TestAuditResults(t *testing.T) {
cid.SetSHA256([sha256.Size]byte{1, 2, 3}) cid.SetSHA256([sha256.Size]byte{1, 2, 3})
auditRes := auditAPI.NewResult() auditRes := auditAPI.NewResult()
auditRes.SetAuditEpoch(11) auditRes.SetAuditEpoch(epoch)
auditRes.SetPublicKey(pubKey) auditRes.SetPublicKey(pubKey)
auditRes.SetContainerID(cid) auditRes.SetContainerID(cid)
@ -52,8 +53,12 @@ func TestAuditResults(t *testing.T) {
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
list, err := auditClientWrapper.ListAuditResults() list, err := auditClientWrapper.ListAuditResultIDByCID(epoch, cid)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, list, 1) require.Len(t, list, 1)
require.Contains(t, list, auditRes)
savedAuditRes, err := auditClientWrapper.GetAuditResult(list[0])
require.NoError(t, err)
require.Equal(t, auditRes, savedAuditRes)
} }