forked from TrueCloudLab/frostfs-contract
[#215] container/tests: add tests for container size estimation
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
parent
53f102344f
commit
7bca6bf782
3 changed files with 147 additions and 21 deletions
|
@ -61,7 +61,8 @@ const (
|
||||||
|
|
||||||
estimateKeyPrefix = "cnr"
|
estimateKeyPrefix = "cnr"
|
||||||
estimatePostfixSize = 10
|
estimatePostfixSize = 10
|
||||||
cleanupDelta = 3
|
// CleanupDelta contains number last epochs for which container estimations are present.
|
||||||
|
CleanupDelta = 3
|
||||||
|
|
||||||
// NotFoundError is returned if container is missing.
|
// NotFoundError is returned if container is missing.
|
||||||
NotFoundError = "container does not exist"
|
NotFoundError = "container does not exist"
|
||||||
|
@ -780,7 +781,7 @@ func cleanupContainers(ctx storage.Context, epoch int) {
|
||||||
|
|
||||||
var n interface{} = nbytes
|
var n interface{} = nbytes
|
||||||
|
|
||||||
if epoch-n.(int) > cleanupDelta {
|
if epoch-n.(int) > CleanupDelta {
|
||||||
storage.Delete(ctx, k)
|
storage.Delete(ctx, k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package tests
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -13,6 +14,7 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-contract/common"
|
"github.com/nspcc-dev/neofs-contract/common"
|
||||||
"github.com/nspcc-dev/neofs-contract/container"
|
"github.com/nspcc-dev/neofs-contract/container"
|
||||||
"github.com/nspcc-dev/neofs-contract/nns"
|
"github.com/nspcc-dev/neofs-contract/nns"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
const containerPath = "../container"
|
const containerPath = "../container"
|
||||||
|
@ -36,7 +38,7 @@ func deployContainerContract(t *testing.T, e *neotest.Executor, addrNetmap, addr
|
||||||
return c.Hash
|
return c.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
func newContainerInvoker(t *testing.T) (*neotest.ContractInvoker, *neotest.ContractInvoker) {
|
func newContainerInvoker(t *testing.T) (*neotest.ContractInvoker, *neotest.ContractInvoker, *neotest.ContractInvoker) {
|
||||||
e := newExecutor(t)
|
e := newExecutor(t)
|
||||||
|
|
||||||
ctrNNS := neotest.CompileFile(t, e.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml"))
|
ctrNNS := neotest.CompileFile(t, e.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml"))
|
||||||
|
@ -50,7 +52,7 @@ func newContainerInvoker(t *testing.T) (*neotest.ContractInvoker, *neotest.Contr
|
||||||
container.AliasFeeKey, int64(containerAliasFee))
|
container.AliasFeeKey, int64(containerAliasFee))
|
||||||
deployBalanceContract(t, e, ctrNetmap.Hash, ctrContainer.Hash)
|
deployBalanceContract(t, e, ctrNetmap.Hash, ctrContainer.Hash)
|
||||||
deployContainerContract(t, e, ctrNetmap.Hash, ctrBalance.Hash, ctrNNS.Hash)
|
deployContainerContract(t, e, ctrNetmap.Hash, ctrBalance.Hash, ctrNNS.Hash)
|
||||||
return e.CommitteeInvoker(ctrContainer.Hash), e.CommitteeInvoker(ctrBalance.Hash)
|
return e.CommitteeInvoker(ctrContainer.Hash), e.CommitteeInvoker(ctrBalance.Hash), e.CommitteeInvoker(ctrNetmap.Hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setContainerOwner(c []byte, acc neotest.Signer) {
|
func setContainerOwner(c []byte, acc neotest.Signer) {
|
||||||
|
@ -78,7 +80,7 @@ func dummyContainer(owner neotest.Signer) testContainer {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerPut(t *testing.T) {
|
func TestContainerPut(t *testing.T) {
|
||||||
c, cBal := newContainerInvoker(t)
|
c, cBal, _ := newContainerInvoker(t)
|
||||||
|
|
||||||
acc := c.NewAccount(t)
|
acc := c.NewAccount(t)
|
||||||
cnt := dummyContainer(acc)
|
cnt := dummyContainer(acc)
|
||||||
|
@ -155,7 +157,7 @@ func addContainer(t *testing.T, c, cBal *neotest.ContractInvoker) (neotest.Signe
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerDelete(t *testing.T) {
|
func TestContainerDelete(t *testing.T) {
|
||||||
c, cBal := newContainerInvoker(t)
|
c, cBal, _ := newContainerInvoker(t)
|
||||||
|
|
||||||
acc, cnt := addContainer(t, c, cBal)
|
acc, cnt := addContainer(t, c, cBal)
|
||||||
cAcc := c.WithSigners(acc)
|
cAcc := c.WithSigners(acc)
|
||||||
|
@ -174,7 +176,7 @@ func TestContainerDelete(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerOwner(t *testing.T) {
|
func TestContainerOwner(t *testing.T) {
|
||||||
c, cBal := newContainerInvoker(t)
|
c, cBal, _ := newContainerInvoker(t)
|
||||||
|
|
||||||
acc, cnt := addContainer(t, c, cBal)
|
acc, cnt := addContainer(t, c, cBal)
|
||||||
|
|
||||||
|
@ -189,7 +191,7 @@ func TestContainerOwner(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerGet(t *testing.T) {
|
func TestContainerGet(t *testing.T) {
|
||||||
c, cBal := newContainerInvoker(t)
|
c, cBal, _ := newContainerInvoker(t)
|
||||||
|
|
||||||
_, cnt := addContainer(t, c, cBal)
|
_, cnt := addContainer(t, c, cBal)
|
||||||
|
|
||||||
|
@ -227,7 +229,7 @@ func dummyEACL(containerID [32]byte) eacl {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerSetEACL(t *testing.T) {
|
func TestContainerSetEACL(t *testing.T) {
|
||||||
c, cBal := newContainerInvoker(t)
|
c, cBal, _ := newContainerInvoker(t)
|
||||||
|
|
||||||
acc, cnt := addContainer(t, c, cBal)
|
acc, cnt := addContainer(t, c, cBal)
|
||||||
|
|
||||||
|
@ -253,3 +255,111 @@ func TestContainerSetEACL(t *testing.T) {
|
||||||
})
|
})
|
||||||
c.Invoke(t, expected, "eACL", cnt.id[:])
|
c.Invoke(t, expected, "eACL", cnt.id[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContainerSizeEstimation(t *testing.T) {
|
||||||
|
c, cBal, cNm := newContainerInvoker(t)
|
||||||
|
|
||||||
|
_, cnt := addContainer(t, c, cBal)
|
||||||
|
nodes := []testNodeInfo{
|
||||||
|
newStorageNode(t, c),
|
||||||
|
newStorageNode(t, c),
|
||||||
|
newStorageNode(t, c),
|
||||||
|
}
|
||||||
|
for i := range nodes {
|
||||||
|
cNm.WithSigners(nodes[i].signer).Invoke(t, stackitem.Null{}, "addPeer", nodes[i].raw)
|
||||||
|
cNm.Invoke(t, stackitem.Null{}, "register", nodes[i].raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// putContainerSize retrieves storage nodes from the previous snapshot,
|
||||||
|
// so epoch must be incremented twice.
|
||||||
|
cNm.Invoke(t, stackitem.Null{}, "newEpoch", int64(1))
|
||||||
|
cNm.Invoke(t, stackitem.Null{}, "newEpoch", int64(2))
|
||||||
|
|
||||||
|
t.Run("must be witnessed by key in the argument", func(t *testing.T) {
|
||||||
|
c.WithSigners(nodes[1].signer).InvokeFail(t, common.ErrWitnessFailed, "putContainerSize",
|
||||||
|
int64(2), cnt.id[:], int64(123), nodes[0].pub)
|
||||||
|
})
|
||||||
|
|
||||||
|
c.WithSigners(nodes[0].signer).Invoke(t, stackitem.Null{}, "putContainerSize",
|
||||||
|
int64(2), cnt.id[:], int64(123), nodes[0].pub)
|
||||||
|
estimations := []estimation{{nodes[0].pub, 123}}
|
||||||
|
checkEstimations(t, c, 2, cnt, estimations...)
|
||||||
|
|
||||||
|
c.WithSigners(nodes[1].signer).Invoke(t, stackitem.Null{}, "putContainerSize",
|
||||||
|
int64(2), cnt.id[:], int64(42), nodes[1].pub)
|
||||||
|
estimations = append(estimations, estimation{nodes[1].pub, int64(42)})
|
||||||
|
checkEstimations(t, c, 2, cnt, estimations...)
|
||||||
|
|
||||||
|
t.Run("add estimation for a different epoch", func(t *testing.T) {
|
||||||
|
c.WithSigners(nodes[2].signer).Invoke(t, stackitem.Null{}, "putContainerSize",
|
||||||
|
int64(1), cnt.id[:], int64(777), nodes[2].pub)
|
||||||
|
checkEstimations(t, c, 1, cnt, estimation{nodes[2].pub, 777})
|
||||||
|
checkEstimations(t, c, 2, cnt, estimations...)
|
||||||
|
})
|
||||||
|
|
||||||
|
c.WithSigners(nodes[2].signer).Invoke(t, stackitem.Null{}, "putContainerSize",
|
||||||
|
int64(3), cnt.id[:], int64(888), nodes[2].pub)
|
||||||
|
checkEstimations(t, c, 3, cnt, estimation{nodes[2].pub, 888})
|
||||||
|
|
||||||
|
// Remove old estimations.
|
||||||
|
for i := int64(1); i <= container.CleanupDelta; i++ {
|
||||||
|
cNm.Invoke(t, stackitem.Null{}, "newEpoch", 2+i)
|
||||||
|
checkEstimations(t, c, 2, cnt, estimations...)
|
||||||
|
checkEstimations(t, c, 3, cnt, estimation{nodes[2].pub, 888})
|
||||||
|
}
|
||||||
|
|
||||||
|
cNm.Invoke(t, stackitem.Null{}, "newEpoch", int64(2+container.CleanupDelta+1))
|
||||||
|
checkEstimations(t, c, 2, cnt)
|
||||||
|
checkEstimations(t, c, 3, cnt, estimation{nodes[2].pub, 888})
|
||||||
|
}
|
||||||
|
|
||||||
|
type estimation struct {
|
||||||
|
from []byte
|
||||||
|
size int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkEstimations(t *testing.T, c *neotest.ContractInvoker, epoch int64, cnt testContainer, estimations ...estimation) {
|
||||||
|
s, err := c.TestInvoke(t, "listContainerSizes", epoch)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var id []byte
|
||||||
|
|
||||||
|
// When there are no estimations, listContainerSizes can also return nothing.
|
||||||
|
item := s.Top().Item()
|
||||||
|
switch it := item.(type) {
|
||||||
|
case stackitem.Null:
|
||||||
|
require.Equal(t, 0, len(estimations))
|
||||||
|
require.Equal(t, stackitem.Null{}, it)
|
||||||
|
return
|
||||||
|
case *stackitem.Array:
|
||||||
|
id, err = it.Value().([]stackitem.Item)[0].TryBytes()
|
||||||
|
require.NoError(t, err)
|
||||||
|
default:
|
||||||
|
require.FailNow(t, "invalid return type for listContainerSizes")
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err = c.TestInvoke(t, "getContainerSize", id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
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)
|
||||||
|
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])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require.True(t, found, "expected estimation from %x to be present", pub)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -56,13 +56,28 @@ func TestDeploySetConfig(t *testing.T) {
|
||||||
"config", container.AliasFeeKey)
|
"config", container.AliasFeeKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func dummyNodeInfo(acc neotest.Signer) []byte {
|
type testNodeInfo struct {
|
||||||
|
signer neotest.SingleSigner
|
||||||
|
pub []byte
|
||||||
|
raw []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func dummyNodeInfo(acc neotest.Signer) testNodeInfo {
|
||||||
ni := make([]byte, 66)
|
ni := make([]byte, 66)
|
||||||
rand.Read(ni)
|
rand.Read(ni)
|
||||||
|
|
||||||
pub, _ := vm.ParseSignatureContract(acc.Script())
|
s := acc.(neotest.SingleSigner)
|
||||||
|
pub := s.Account().PrivateKey().PublicKey().Bytes()
|
||||||
copy(ni[2:], pub)
|
copy(ni[2:], pub)
|
||||||
return ni
|
return testNodeInfo{
|
||||||
|
signer: s,
|
||||||
|
pub: pub,
|
||||||
|
raw: ni,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStorageNode(t *testing.T, c *neotest.ContractInvoker) testNodeInfo {
|
||||||
|
return dummyNodeInfo(c.NewAccount(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddPeer(t *testing.T) {
|
func TestAddPeer(t *testing.T) {
|
||||||
|
@ -74,25 +89,25 @@ func TestAddPeer(t *testing.T) {
|
||||||
|
|
||||||
acc1 := c.NewAccount(t)
|
acc1 := c.NewAccount(t)
|
||||||
cAcc1 := c.WithSigners(acc1)
|
cAcc1 := c.WithSigners(acc1)
|
||||||
cAcc1.InvokeFail(t, common.ErrWitnessFailed, "addPeer", dummyInfo)
|
cAcc1.InvokeFail(t, common.ErrWitnessFailed, "addPeer", dummyInfo.raw)
|
||||||
|
|
||||||
h := cAcc.Invoke(t, stackitem.Null{}, "addPeer", dummyInfo)
|
h := cAcc.Invoke(t, stackitem.Null{}, "addPeer", dummyInfo.raw)
|
||||||
aer := cAcc.CheckHalt(t, h)
|
aer := cAcc.CheckHalt(t, h)
|
||||||
require.Equal(t, 1, len(aer.Events))
|
require.Equal(t, 1, len(aer.Events))
|
||||||
require.Equal(t, "AddPeer", aer.Events[0].Name)
|
require.Equal(t, "AddPeer", aer.Events[0].Name)
|
||||||
require.Equal(t, stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(dummyInfo)}),
|
require.Equal(t, stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(dummyInfo.raw)}),
|
||||||
aer.Events[0].Item)
|
aer.Events[0].Item)
|
||||||
|
|
||||||
dummyInfo[0] ^= 0xFF
|
dummyInfo.raw[0] ^= 0xFF
|
||||||
h = cAcc.Invoke(t, stackitem.Null{}, "addPeer", dummyInfo)
|
h = cAcc.Invoke(t, stackitem.Null{}, "addPeer", dummyInfo.raw)
|
||||||
aer = cAcc.CheckHalt(t, h)
|
aer = cAcc.CheckHalt(t, h)
|
||||||
require.Equal(t, 1, len(aer.Events))
|
require.Equal(t, 1, len(aer.Events))
|
||||||
require.Equal(t, "AddPeer", aer.Events[0].Name)
|
require.Equal(t, "AddPeer", aer.Events[0].Name)
|
||||||
require.Equal(t, stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(dummyInfo)}),
|
require.Equal(t, stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(dummyInfo.raw)}),
|
||||||
aer.Events[0].Item)
|
aer.Events[0].Item)
|
||||||
|
|
||||||
c.InvokeFail(t, common.ErrWitnessFailed, "addPeer", dummyInfo)
|
c.InvokeFail(t, common.ErrWitnessFailed, "addPeer", dummyInfo.raw)
|
||||||
c.Invoke(t, stackitem.Null{}, "register", dummyInfo)
|
c.Invoke(t, stackitem.Null{}, "register", dummyInfo.raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateState(t *testing.T) {
|
func TestUpdateState(t *testing.T) {
|
||||||
|
@ -103,7 +118,7 @@ func TestUpdateState(t *testing.T) {
|
||||||
cBoth := e.WithSigners(e.Committee, acc)
|
cBoth := e.WithSigners(e.Committee, acc)
|
||||||
dummyInfo := dummyNodeInfo(acc)
|
dummyInfo := dummyNodeInfo(acc)
|
||||||
|
|
||||||
cBoth.Invoke(t, stackitem.Null{}, "addPeer", dummyInfo)
|
cBoth.Invoke(t, stackitem.Null{}, "addPeer", dummyInfo.raw)
|
||||||
|
|
||||||
pub, ok := vm.ParseSignatureContract(acc.Script())
|
pub, ok := vm.ParseSignatureContract(acc.Script())
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
Loading…
Reference in a new issue