[#51] policy: Support Get and ListByPrefix methods #51

Merged
dkirillov merged 2 commits from dkirillov/frostfs-contract:feature/support_iam_operations into master 2024-09-04 19:51:17 +00:00
4 changed files with 64 additions and 21 deletions

View file

@ -1,2 +1,2 @@
name: "APE" name: "APE"
safemethods: ["listChains"] safemethods: ["listChains","getChain","listChainsByPrefix"]

View file

@ -35,6 +35,17 @@ func AddChain(entity Kind, entityName, name string, chain []byte) {
storage.Put(ctx, key, chain) storage.Put(ctx, key, chain)
} }
func GetChain(entity Kind, entityName, name string) []byte {
ctx := storage.GetReadOnlyContext()
key := storageKey(entity, entityName, name)
data := storage.Get(ctx, key).([]byte)
if data == nil {
panic("not found")
}
return data
}
func RemoveChain(entity Kind, entityName string, name string) { func RemoveChain(entity Kind, entityName string, name string) {
common.CheckAlphabetWitness() common.CheckAlphabetWitness()
@ -57,22 +68,25 @@ func RemoveChainsByPrefix(entity Kind, entityName string, name string) {
// ListChains lists all chains for the namespace by prefix. // ListChains lists all chains for the namespace by prefix.
// container may be empty. // container may be empty.
func ListChains(namespace, container, name string) [][]byte { func ListChains(namespace, container, name string) [][]byte {
ctx := storage.GetReadOnlyContext() result := ListChainsByPrefix(Namespace, namespace, name)
var result [][]byte
prefixNs := storageKey(Namespace, namespace, name)
it := storage.Find(ctx, prefixNs, storage.ValuesOnly)
for iterator.Next(it) {
result = append(result, iterator.Value(it).([]byte))
}
if container != "" { if container != "" {
prefixCnr := storageKey(Container, container, name) result = append(result, ListChainsByPrefix(Container, container, name)...)
it = storage.Find(ctx, prefixCnr, storage.ValuesOnly) }
for iterator.Next(it) {
result = append(result, iterator.Value(it).([]byte)) return result
} }
// ListChainsByPrefix list all chains for the provided kind and entity by prefix.
func ListChainsByPrefix(entity Kind, entityName, prefix string) [][]byte {
ctx := storage.GetReadOnlyContext()
result := [][]byte{}
keyPrefix := storageKey(entity, entityName, prefix)
it := storage.Find(ctx, keyPrefix, storage.ValuesOnly)
for iterator.Next(it) {
result = append(result, iterator.Value(it).([]byte))
} }
return result return result

View file

@ -52,11 +52,21 @@ func New(actor Actor, hash util.Uint160) *Contract {
return &Contract{ContractReader{actor, hash}, actor, hash} return &Contract{ContractReader{actor, hash}, actor, hash}
} }
// GetChain invokes `getChain` method of contract.
func (c *ContractReader) GetChain(entity *big.Int, entityName string, name string) ([]byte, error) {
return unwrap.Bytes(c.invoker.Call(c.hash, "getChain", entity, entityName, name))
}
// ListChains invokes `listChains` method of contract. // ListChains invokes `listChains` method of contract.
func (c *ContractReader) ListChains(namespace string, container string, name string) ([]stackitem.Item, error) { func (c *ContractReader) ListChains(namespace string, container string, name string) ([]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))
} }
// ListChainsByPrefix invokes `listChainsByPrefix` method of contract.
func (c *ContractReader) ListChainsByPrefix(entity *big.Int, entityName string, prefix string) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "listChainsByPrefix", entity, entityName, prefix))
}
// AddChain creates a transaction invoking `addChain` method of the contract. // AddChain creates a transaction invoking `addChain` method of the contract.
// This transaction is signed and immediately sent to the network. // This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any. // The values returned are its hash, ValidUntilBlock value and error if any.

View file

@ -1,12 +1,14 @@
package tests package tests
import ( import (
"bytes"
"path" "path"
"testing" "testing"
"git.frostfs.info/TrueCloudLab/frostfs-contract/policy" "git.frostfs.info/TrueCloudLab/frostfs-contract/policy"
"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/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -49,7 +51,10 @@ func TestPolicy(t *testing.T) {
checkChains(t, e, "mynamespace", "cnr1", "ingress", [][]byte{p1, p2, p3}) checkChains(t, e, "mynamespace", "cnr1", "ingress", [][]byte{p1, p2, p3})
e.Invoke(t, stackitem.Null{}, "addChain", policy.Container, "cnr1", "ingress:myrule3", p33) e.Invoke(t, stackitem.Null{}, "addChain", policy.Container, "cnr1", "ingress:myrule3", p33)
checkChain(t, e, policy.Container, "cnr1", "ingress:myrule3", p33)
checkChains(t, e, "mynamespace", "cnr1", "ingress", [][]byte{p1, p2, p33}) // Override chain. checkChains(t, e, "mynamespace", "cnr1", "ingress", [][]byte{p1, p2, p33}) // Override chain.
checkChainsByPrefix(t, e, policy.Container, "cnr1", "", [][]byte{p2, p33})
checkChainsByPrefix(t, e, policy.IAM, "", "", nil)
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) {
@ -70,14 +75,20 @@ func TestPolicy(t *testing.T) {
func checkChains(t *testing.T, e *neotest.ContractInvoker, namespace, container, name string, expected [][]byte) { func checkChains(t *testing.T, e *neotest.ContractInvoker, namespace, container, name string, expected [][]byte) {
s, err := e.TestInvoke(t, "listChains", namespace, container, name) s, err := e.TestInvoke(t, "listChains", namespace, container, name)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, s.Len())
if len(expected) == 0 { checksChainsOnStack(t, s, expected)
_, ok := s.Pop().Item().(stackitem.Null)
require.True(t, ok)
return
} }
func checkChainsByPrefix(t *testing.T, e *neotest.ContractInvoker, kind byte, entityName, prefix string, expected [][]byte) {
s, err := e.TestInvoke(t, "listChainsByPrefix", kind, entityName, prefix)
require.NoError(t, err)
checksChainsOnStack(t, s, expected)
}
func checksChainsOnStack(t *testing.T, s *vm.Stack, expected [][]byte) {
require.Equal(t, 1, s.Len())
var actual [][]byte var actual [][]byte
arr := s.Pop().Array() arr := s.Pop().Array()
for i := range arr { for i := range arr {
@ -89,3 +100,11 @@ func checkChains(t *testing.T, e *neotest.ContractInvoker, namespace, container,
require.ElementsMatch(t, expected, actual) require.ElementsMatch(t, expected, actual)
} }
func checkChain(t *testing.T, e *neotest.ContractInvoker, kind byte, entityName, name string, expected []byte) {
s, err := e.TestInvoke(t, "getChain", kind, entityName, name)
require.NoError(t, err)
require.Equal(t, 1, s.Len())
require.True(t, bytes.Equal(expected, s.Pop().Bytes()))
}