[#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 62 additions and 1 deletions
Showing only changes of commit 5cc34f98e9 - Show all commits

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()
@ -77,3 +88,18 @@ func ListChains(namespace, container, name string) [][]byte {
return result return result
} }
// ListChainsByPrefix list all chains for the provided kind and entity by prefix.
func ListChainsByPrefix(entity Kind, entityName, prefix string) [][]byte {

I cannot decide should the return value be slice or iterator.
In IAM we are going to use two type entity name (managed and inline) so it seems we can have more that 2048 chains for every entityName. But probably as prototype we can keep slice

I cannot decide should the return value be slice or iterator. In IAM we are going to use two type entity name (`managed` and `inline`) so it seems we can have more that 2048 chains for every `entityName`. But probably as prototype we can keep slice
Review

ListChains already can list by prefix, what is the usecase for this?

`ListChains` already can list by prefix, what is the usecase for this?
Review

For i Kind

IAM = 'i'

For `i` Kind https://git.frostfs.info/TrueCloudLab/frostfs-contract/src/commit/dd5919348da9731f24504e7bc485516c2ba5f11c/policy/policy_contract.go#L16
Review

So looks like you can just edit ListChains method definition or edit ListChains method implementation to use ListChainsByPrefix method.

So looks like you can just edit `ListChains` method definition or edit `ListChains` method implementation to use `ListChainsByPrefix` method.
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
}

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,6 +75,18 @@ 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)
checksChainsOnStack(t, s, expected)
}
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()) require.Equal(t, 1, s.Len())
if len(expected) == 0 { if len(expected) == 0 {
@ -89,3 +106,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()))
}