[#66] policy: Support IteratorChainsByPrefix method

Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
This commit is contained in:
Alexander Chuprov 2024-01-26 17:39:33 +03:00
parent da8ec5b447
commit 93781f1149
4 changed files with 49 additions and 1 deletions

View file

@ -4,4 +4,5 @@ safemethods:
- "listChains" - "listChains"
- "getChain" - "getChain"
- "listChainsByPrefix" - "listChainsByPrefix"
- "iteratorChainsByPrefix"
- "version" - "version"

View file

@ -150,3 +150,9 @@ func ListChainsByPrefix(entity Kind, entityName string, prefix []byte) [][]byte
return result return result
} }
func IteratorChainsByPrefix(entity Kind, entityName string, prefix []byte) iterator.Iterator {
ctx := storage.GetReadOnlyContext()
keyPrefix := storageKey(entity, entityName, prefix)
return storage.Find(ctx, keyPrefix, storage.ValuesOnly)
}

View file

@ -4,6 +4,7 @@
package ape package ape
import ( import (
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"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/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
@ -15,6 +16,9 @@ import (
// Invoker is used by ContractReader to call various safe methods. // Invoker is used by ContractReader to call various safe methods.
type Invoker interface { type Invoker interface {
Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error)
CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...any) (*result.Invoke, error)
TerminateSession(sessionID uuid.UUID) error
TraverseIterator(sessionID uuid.UUID, iterator *result.Iterator, num int) ([]stackitem.Item, error)
} }
// Actor is used by Contract to call state-changing methods. // Actor is used by Contract to call state-changing methods.
@ -62,6 +66,20 @@ func (c *ContractReader) GetChain(entity *big.Int, entityName string, name []byt
return unwrap.Bytes(c.invoker.Call(c.hash, "getChain", entity, entityName, name)) return unwrap.Bytes(c.invoker.Call(c.hash, "getChain", entity, entityName, name))
} }
// IteratorChainsByPrefix invokes `iteratorChainsByPrefix` method of contract.
func (c *ContractReader) IteratorChainsByPrefix(entity *big.Int, entityName string, prefix []byte) (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.invoker.Call(c.hash, "iteratorChainsByPrefix", entity, entityName, prefix))
}
// IteratorChainsByPrefixExpanded is similar to IteratorChainsByPrefix (uses the same contract
// method), but can be useful if the server used doesn't support sessions and
// doesn't expand iterators. It creates a script that will get the specified
// number of result items from the iterator right in the VM and return them to
// you. It's only limited by VM stack and GAS available for RPC invocations.
func (c *ContractReader) IteratorChainsByPrefixExpanded(entity *big.Int, entityName string, prefix []byte, _numOfIteratorItems int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "iteratorChainsByPrefix", _numOfIteratorItems, entity, entityName, prefix))
}
// ListChains invokes `listChains` method of contract. // ListChains invokes `listChains` method of contract.
func (c *ContractReader) ListChains(namespace string, container string, name []byte) ([]stackitem.Item, error) { func (c *ContractReader) ListChains(namespace string, container string, name []byte) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "listChains", namespace, container, name)) return unwrap.Array(c.invoker.Call(c.hash, "listChains", namespace, container, name))

View file

@ -6,6 +6,7 @@ import (
"testing" "testing"
"git.frostfs.info/TrueCloudLab/frostfs-contract/policy" "git.frostfs.info/TrueCloudLab/frostfs-contract/policy"
"github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/neotest" "github.com/nspcc-dev/neo-go/pkg/neotest"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
@ -56,6 +57,9 @@ func TestPolicy(t *testing.T) {
checkChainsByPrefix(t, e, policy.Container, "cnr1", "", [][]byte{p2, p33}) checkChainsByPrefix(t, e, policy.Container, "cnr1", "", [][]byte{p2, p33})
checkChainsByPrefix(t, e, policy.IAM, "", "", nil) checkChainsByPrefix(t, e, policy.IAM, "", "", nil)
checkChainsIteratorByPrefix(t, e, policy.Container, "cnr1", "ingress:myrule3", [][]byte{p33})
checkChainsIteratorByPrefix(t, e, policy.Container, "cnr1", "ingress", [][]byte{p2, p33})
t.Run("removal", func(t *testing.T) { t.Run("removal", func(t *testing.T) {
t.Run("wrong name", func(t *testing.T) { t.Run("wrong name", func(t *testing.T) {
e.Invoke(t, stackitem.Null{}, "removeChain", policy.Namespace, "mynamespace", "ingress") e.Invoke(t, stackitem.Null{}, "removeChain", policy.Namespace, "mynamespace", "ingress")
@ -103,6 +107,25 @@ func checkChainsByPrefix(t *testing.T, e *neotest.ContractInvoker, kind byte, en
checksChainsOnStack(t, s, expected) checksChainsOnStack(t, s, expected)
} }
func checkChainsIteratorByPrefix(t *testing.T, e *neotest.ContractInvoker, kind byte, entityName, prefix string, expected [][]byte) {
s, err := e.TestInvoke(t, "iteratorChainsByPrefix", kind, entityName, prefix)
require.NoError(t, err)
if s.Len() == 0 {
t.Fatal("Stack is empty")
}
iteratorItem := s.Pop().Value().(*storage.Iterator)
policys := iteratorToArray(iteratorItem)
require.Equal(t, len(expected), len(policys))
for i := range expected {
bytesPolicy, err := policys[i].TryBytes()
require.NoError(t, err)
require.Equal(t, expected[i], bytesPolicy)
}
}
func checksChainsOnStack(t *testing.T, s *vm.Stack, expected [][]byte) { func checksChainsOnStack(t *testing.T, s *vm.Stack, expected [][]byte) {
require.Equal(t, 1, s.Len()) require.Equal(t, 1, s.Len())