forked from TrueCloudLab/frostfs-contract
[#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 <v.domnich@yadro.com>
This commit is contained in:
parent
1a4fa7e421
commit
03bff785d2
4 changed files with 78 additions and 17 deletions
|
@ -1,5 +1,5 @@
|
||||||
name: "FrostFS Container"
|
name: "FrostFS Container"
|
||||||
safemethods: ["count", "get", "owner", "list", "eACL", "getContainerSize", "listContainerSizes", "version"]
|
safemethods: ["count", "get", "owner", "list", "eACL", "getContainerSize", "listContainerSizes", "iterateContainerSizes", "version"]
|
||||||
permissions:
|
permissions:
|
||||||
- methods: ["update", "addKey", "transferX",
|
- methods: ["update", "addKey", "transferX",
|
||||||
"register", "addRecord", "deleteRecords"]
|
"register", "addRecord", "deleteRecords"]
|
||||||
|
|
|
@ -551,7 +551,7 @@ func GetContainerSize(id []byte) containerSizes {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListContainerSizes method returns the IDs of container size estimations
|
// 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 {
|
func ListContainerSizes(epoch int) [][]byte {
|
||||||
ctx := storage.GetReadOnlyContext()
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
|
||||||
|
@ -582,6 +582,19 @@ func ListContainerSizes(epoch int) [][]byte {
|
||||||
return result
|
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
|
// 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.
|
// epochNum + 3. It can be invoked only by NewEpoch method of the Netmap contract.
|
||||||
func NewEpoch(epochNum int) {
|
func NewEpoch(epochNum int) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package tests
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"math/big"
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -10,6 +11,7 @@ import (
|
||||||
"github.com/TrueCloudLab/frostfs-contract/container"
|
"github.com/TrueCloudLab/frostfs-contract/container"
|
||||||
"github.com/TrueCloudLab/frostfs-contract/nns"
|
"github.com/TrueCloudLab/frostfs-contract/nns"
|
||||||
"github.com/mr-tron/base58"
|
"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/encoding/address"
|
||||||
"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"
|
||||||
|
@ -366,6 +368,16 @@ type estimation struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkEstimations(t *testing.T, c *neotest.ContractInvoker, epoch int64, cnt testContainer, estimations ...estimation) {
|
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)
|
s, err := c.TestInvoke(t, "listContainerSizes", epoch)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -375,9 +387,8 @@ func checkEstimations(t *testing.T, c *neotest.ContractInvoker, epoch int64, cnt
|
||||||
item := s.Top().Item()
|
item := s.Top().Item()
|
||||||
switch it := item.(type) {
|
switch it := item.(type) {
|
||||||
case stackitem.Null:
|
case stackitem.Null:
|
||||||
require.Equal(t, 0, len(estimations))
|
|
||||||
require.Equal(t, stackitem.Null{}, it)
|
require.Equal(t, stackitem.Null{}, it)
|
||||||
return
|
return make([]estimation, 0)
|
||||||
case *stackitem.Array:
|
case *stackitem.Array:
|
||||||
id, err = it.Value().([]stackitem.Item)[0].TryBytes()
|
id, err = it.Value().([]stackitem.Item)[0].TryBytes()
|
||||||
require.NoError(t, err)
|
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)
|
s, err = c.TestInvoke(t, "getContainerSize", id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Here and below we assume that all estimations in the contract are related to our container
|
||||||
sizes := s.Top().Array()
|
sizes := s.Top().Array()
|
||||||
require.Equal(t, cnt.id[:], sizes[0].Value())
|
require.Equal(t, cnt.id[:], sizes[0].Value())
|
||||||
|
|
||||||
actual := sizes[1].Value().([]stackitem.Item)
|
return convertStackToEstimations(sizes[1].Value().([]stackitem.Item))
|
||||||
require.Equal(t, len(estimations), len(actual))
|
}
|
||||||
for i := range actual {
|
|
||||||
// type estimation struct {
|
func getIterEstimations(t *testing.T, c *neotest.ContractInvoker, epoch int64) []estimation {
|
||||||
// from interop.PublicKey
|
iterStack, err := c.TestInvoke(t, "iterateContainerSizes", epoch)
|
||||||
// size int
|
require.NoError(t, err)
|
||||||
// }
|
iter := iterStack.Pop().Value().(*storage.Iterator)
|
||||||
est := actual[i].Value().([]stackitem.Item)
|
|
||||||
pub := est[0].Value().([]byte)
|
// 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
|
found := false
|
||||||
for i := range estimations {
|
for _, a := range actual {
|
||||||
if found = bytes.Equal(estimations[i].from, pub); found {
|
if found = bytes.Equal(e.from, a.from); found {
|
||||||
require.Equal(t, stackitem.Make(estimations[i].size), est[1])
|
require.Equal(t, e.size, a.size)
|
||||||
break
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,20 @@ package tests
|
||||||
import (
|
import (
|
||||||
"testing"
|
"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"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
"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 {
|
func newExecutor(t *testing.T) *neotest.Executor {
|
||||||
bc, acc := chain.NewSingle(t)
|
bc, acc := chain.NewSingle(t)
|
||||||
return neotest.NewExecutor(t, bc, acc, acc)
|
return neotest.NewExecutor(t, bc, acc, acc)
|
||||||
|
|
Loading…
Reference in a new issue