diff --git a/pkg/morph/client/audit/client.go b/pkg/morph/client/audit/client.go index a84fd267..77876363 100644 --- a/pkg/morph/client/audit/client.go +++ b/pkg/morph/client/audit/client.go @@ -23,18 +23,30 @@ type Option func(*cfg) type cfg struct { 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 ( - defaultPutResultMethod = "put" // default "put audit result" method name - defaultListResultsMethod = "list" // default "list audit results" method name + defaultPutResultMethod = "put" // default "put audit result" 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 { return &cfg{ - putResultMethod: defaultPutResultMethod, - listResultsMethod: defaultListResultsMethod, + putResultMethod: defaultPutResultMethod, + getResultMethod: defaultGetResultMethod, + 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 -// specifies the "list audit results" method name. +// specifies the "list all audit result IDs" method name. // // Ignores empty value. 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 + } + } +} diff --git a/pkg/morph/client/audit/get_result.go b/pkg/morph/client/audit/get_result.go new file mode 100644 index 00000000..3df80aad --- /dev/null +++ b/pkg/morph/client/audit/get_result.go @@ -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 +} diff --git a/pkg/morph/client/audit/list_results.go b/pkg/morph/client/audit/list_results.go index e7da9b64..f18c1156 100644 --- a/pkg/morph/client/audit/list_results.go +++ b/pkg/morph/client/audit/list_results.go @@ -1,27 +1,65 @@ package audit import ( + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neofs-node/pkg/morph/client" "github.com/pkg/errors" ) // ListResultsArgs groups the arguments -// of "list audit results" test invoke call. +// of "list all audit result IDs" test invoke call. 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 // returned by "list audit results" test invoke. type ListResultsValues struct { 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. func (v *ListResultsValues) RawResults() [][]byte { 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. func (c *Client) ListAuditResults(args ListResultsArgs) (*ListResultsValues, error) { items, err := c.client.TestInvoke( @@ -29,13 +67,64 @@ func (c *Client) ListAuditResults(args ListResultsArgs) (*ListResultsValues, err ) if err != nil { 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 { - 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{ @@ -45,7 +134,7 @@ func (c *Client) ListAuditResults(args ListResultsArgs) (*ListResultsValues, err for i := range items { rawRes, err := client.BytesFromStackItem(items[i]) 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) diff --git a/pkg/morph/client/audit/wrapper/result.go b/pkg/morph/client/audit/wrapper/result.go index 743c1715..820c65f2 100644 --- a/pkg/morph/client/audit/wrapper/result.go +++ b/pkg/morph/client/audit/wrapper/result.go @@ -2,10 +2,16 @@ package audit import ( 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/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 // through Audit contract call. // @@ -24,9 +30,8 @@ func (w *ClientWrapper) PutAuditResult(result *auditAPI.Result) error { PutAuditResult(args) } -// ListAuditResults returns a list of all audit results in NeoFS system. -// The list is composed through Audit contract call. -func (w *ClientWrapper) ListAuditResults() ([]*auditAPI.Result, error) { +// ListAuditResults returns a list of all audit result IDs inside audit contract. +func (w *ClientWrapper) ListAllAuditResultID() ([]ResultID, error) { args := audit.ListResultsArgs{} values, err := (*audit.Client)(w).ListAuditResults(args) @@ -34,17 +39,89 @@ func (w *ClientWrapper) ListAuditResults() ([]*auditAPI.Result, error) { return nil, err } - rawResults := values.RawResults() - result := make([]*auditAPI.Result, 0, len(rawResults)) + return parseRawResult(values), nil +} - for i := range rawResults { - auditRes := auditAPI.NewResult() - if err := auditRes.Unmarshal(rawResults[i]); err != nil { - return nil, errors.Wrap(err, "could not unmarshal audit result structure") - } +// 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)) - result = append(result, auditRes) + values, err := (*audit.Client)(w).ListAuditResultsByEpoch(args) + if err != nil { + return nil, err } - return result, nil + 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() + result := make([]ResultID, 0, len(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() + if err := auditRes.Unmarshal(value.Result()); err != nil { + return nil, errors.Wrap(err, "could not unmarshal audit result structure") + } + + return auditRes, nil } diff --git a/pkg/morph/client/audit/wrapper/result_test.go b/pkg/morph/client/audit/wrapper/result_test.go index 752e5f20..91556eed 100644 --- a/pkg/morph/client/audit/wrapper/result_test.go +++ b/pkg/morph/client/audit/wrapper/result_test.go @@ -17,9 +17,10 @@ import ( func TestAuditResults(t *testing.T) { t.Skip() + const epoch = 11 endpoint := "http://morph_chain.neofs.devenv:30333" - sAuditHash := "96a746aa7186f775e5744a6e2c6566dc5c4a57a2" + sAuditHash := "cdfb3dab86e6d60e8a143d9e2ecb0b188f3dc2eb" irKeyWIF := "L3o221BojgcCPYgdbXsm6jn7ayTZ72xwREvBHXKknR8VJ3G4WmjB" key, err := crypto.WIFDecode(irKeyWIF) @@ -44,7 +45,7 @@ func TestAuditResults(t *testing.T) { cid.SetSHA256([sha256.Size]byte{1, 2, 3}) auditRes := auditAPI.NewResult() - auditRes.SetAuditEpoch(11) + auditRes.SetAuditEpoch(epoch) auditRes.SetPublicKey(pubKey) auditRes.SetContainerID(cid) @@ -52,8 +53,12 @@ func TestAuditResults(t *testing.T) { time.Sleep(5 * time.Second) - list, err := auditClientWrapper.ListAuditResults() + list, err := auditClientWrapper.ListAuditResultIDByCID(epoch, cid) require.NoError(t, err) 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) }