Support IteratorChainsByPrefix method #70
4 changed files with 49 additions and 1 deletions
|
@ -4,4 +4,5 @@ safemethods:
|
||||||
- "listChains"
|
- "listChains"
|
||||||
- "getChain"
|
- "getChain"
|
||||||
- "listChainsByPrefix"
|
- "listChainsByPrefix"
|
||||||
- "version"
|
- "iteratorChainsByPrefix"
|
||||||
|
- "version"
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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())
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue