From 03bff785d246759c2b7bfdfdd29872fda197d0b4 Mon Sep 17 00:00:00 2001 From: Vladimir Domnich Date: Thu, 17 Nov 2022 18:30:21 +0300 Subject: [PATCH] [#293] container: Add IterateContainerSizes method Add method that allows to iterate over estimation records. Update tests to assert that list of estimations built with existing methods is identical to estimations from iterator. Signed-off-by: Vladimir Domnich --- container/config.yml | 2 +- container/container_contract.go | 15 +++++++- tests/container_test.go | 68 +++++++++++++++++++++++++-------- tests/util.go | 10 +++++ 4 files changed, 78 insertions(+), 17 deletions(-) diff --git a/container/config.yml b/container/config.yml index dfe3e65..b051403 100644 --- a/container/config.yml +++ b/container/config.yml @@ -1,5 +1,5 @@ name: "FrostFS Container" -safemethods: ["count", "get", "owner", "list", "eACL", "getContainerSize", "listContainerSizes", "version"] +safemethods: ["count", "get", "owner", "list", "eACL", "getContainerSize", "listContainerSizes", "iterateContainerSizes", "version"] permissions: - methods: ["update", "addKey", "transferX", "register", "addRecord", "deleteRecords"] diff --git a/container/container_contract.go b/container/container_contract.go index 85396c3..f08c1d1 100644 --- a/container/container_contract.go +++ b/container/container_contract.go @@ -551,7 +551,7 @@ func GetContainerSize(id []byte) containerSizes { } // ListContainerSizes method returns the IDs of container size estimations -// that has been registered for the specified epoch. +// that have been registered for the specified epoch. func ListContainerSizes(epoch int) [][]byte { ctx := storage.GetReadOnlyContext() @@ -582,6 +582,19 @@ func ListContainerSizes(epoch int) [][]byte { return result } +// IterateContainerSizes method returns iterator over container size estimations +// that have been registered for the specified epoch. +func IterateContainerSizes(epoch int) iterator.Iterator { + ctx := storage.GetReadOnlyContext() + + var buf interface{} = epoch + + key := []byte(estimateKeyPrefix) + key = append(key, buf.([]byte)...) + + return storage.Find(ctx, key, storage.DeserializeValues) +} + // NewEpoch method removes all container size estimations from epoch older than // epochNum + 3. It can be invoked only by NewEpoch method of the Netmap contract. func NewEpoch(epochNum int) { diff --git a/tests/container_test.go b/tests/container_test.go index 56d7cef..3cc24d1 100644 --- a/tests/container_test.go +++ b/tests/container_test.go @@ -3,6 +3,7 @@ package tests import ( "bytes" "crypto/sha256" + "math/big" "path" "testing" @@ -10,6 +11,7 @@ import ( "github.com/TrueCloudLab/frostfs-contract/container" "github.com/TrueCloudLab/frostfs-contract/nns" "github.com/mr-tron/base58" + "github.com/nspcc-dev/neo-go/pkg/core/interop/storage" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/neotest" "github.com/nspcc-dev/neo-go/pkg/util" @@ -366,6 +368,16 @@ type estimation struct { } func checkEstimations(t *testing.T, c *neotest.ContractInvoker, epoch int64, cnt testContainer, estimations ...estimation) { + // Check that listed estimations match expected + listEstimations := getListEstimations(t, c, epoch, cnt) + requireEstimationsMatch(t, estimations, listEstimations) + + // Check that iterated estimations match expected + iterEstimations := getIterEstimations(t, c, epoch) + requireEstimationsMatch(t, estimations, iterEstimations) +} + +func getListEstimations(t *testing.T, c *neotest.ContractInvoker, epoch int64, cnt testContainer) []estimation { s, err := c.TestInvoke(t, "listContainerSizes", epoch) require.NoError(t, err) @@ -375,9 +387,8 @@ func checkEstimations(t *testing.T, c *neotest.ContractInvoker, epoch int64, cnt item := s.Top().Item() switch it := item.(type) { case stackitem.Null: - require.Equal(t, 0, len(estimations)) require.Equal(t, stackitem.Null{}, it) - return + return make([]estimation, 0) case *stackitem.Array: id, err = it.Value().([]stackitem.Item)[0].TryBytes() require.NoError(t, err) @@ -388,25 +399,52 @@ func checkEstimations(t *testing.T, c *neotest.ContractInvoker, epoch int64, cnt s, err = c.TestInvoke(t, "getContainerSize", id) require.NoError(t, err) + // Here and below we assume that all estimations in the contract are related to our container sizes := s.Top().Array() require.Equal(t, cnt.id[:], sizes[0].Value()) - actual := sizes[1].Value().([]stackitem.Item) - require.Equal(t, len(estimations), len(actual)) - for i := range actual { - // type estimation struct { - // from interop.PublicKey - // size int - // } - est := actual[i].Value().([]stackitem.Item) - pub := est[0].Value().([]byte) + return convertStackToEstimations(sizes[1].Value().([]stackitem.Item)) +} + +func getIterEstimations(t *testing.T, c *neotest.ContractInvoker, epoch int64) []estimation { + iterStack, err := c.TestInvoke(t, "iterateContainerSizes", epoch) + require.NoError(t, err) + iter := iterStack.Pop().Value().(*storage.Iterator) + + // Iterator contains pairs: key + estimation (as stack item), we extract estimations only + pairs := iteratorToArray(iter) + estimationItems := make([]stackitem.Item, len(pairs)) + for i, pair := range pairs { + pairItems := pair.Value().([]stackitem.Item) + estimationItems[i] = pairItems[1] + } + + return convertStackToEstimations(estimationItems) +} + +func convertStackToEstimations(stackItems []stackitem.Item) []estimation { + estimations := make([]estimation, 0, len(stackItems)) + for _, item := range stackItems { + value := item.Value().([]stackitem.Item) + from := value[0].Value().([]byte) + size := value[1].Value().(*big.Int) + + estimation := estimation{from: from, size: size.Int64()} + estimations = append(estimations, estimation) + } + return estimations +} + +func requireEstimationsMatch(t *testing.T, expected []estimation, actual []estimation) { + require.Equal(t, len(expected), len(actual)) + for _, e := range expected { found := false - for i := range estimations { - if found = bytes.Equal(estimations[i].from, pub); found { - require.Equal(t, stackitem.Make(estimations[i].size), est[1]) + for _, a := range actual { + if found = bytes.Equal(e.from, a.from); found { + require.Equal(t, e.size, a.size) break } } - require.True(t, found, "expected estimation from %x to be present", pub) + require.True(t, found, "expected estimation from %x to be present", e.from) } } diff --git a/tests/util.go b/tests/util.go index 1e2958d..48f5f5d 100644 --- a/tests/util.go +++ b/tests/util.go @@ -3,10 +3,20 @@ package tests import ( "testing" + "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/chain" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) +func iteratorToArray(iter *storage.Iterator) []stackitem.Item { + stackItems := make([]stackitem.Item, 0) + for iter.Next() { + stackItems = append(stackItems, iter.Value()) + } + return stackItems +} + func newExecutor(t *testing.T) *neotest.Executor { bc, acc := chain.NewSingle(t) return neotest.NewExecutor(t, bc, acc, acc)