container: Add DeletionInfo() method

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
This commit is contained in:
Evgenii Stratonikov 2023-08-08 11:10:59 +03:00 committed by Evgenii Stratonikov
parent 2b1fa53b67
commit c6a7820363
3 changed files with 57 additions and 1 deletions

View file

@ -2,6 +2,7 @@ name: "Container"
safemethods:
- "count"
- "containersOf"
- "deletionInfo"
- "eACL"
- "get"
- "getContainerSize"

View file

@ -64,6 +64,7 @@ const (
estimateKeyPrefix = "cnr"
containerKeyPrefix = 'x'
ownerKeyPrefix = 'o'
graveKeyPrefix = 'g'
estimatePostfixSize = 10
// CleanupDelta contains the number of the last epochs for which container estimations are present.
CleanupDelta = 3
@ -337,6 +338,25 @@ func Delete(containerID []byte, signature interop.Signature, publicKey interop.P
runtime.Notify("DeleteSuccess", containerID)
}
type DelInfo struct {
Owner interop.Hash160
Epoch int
}
// DeletionInfo method returns container deletion info.
// If the container had never existed, NotFoundError is throwed.
// It can be used to check whether non-existing container was indeed deleted
// or does not yet exist at some height.
func DeletionInfo(containerID []byte) DelInfo {
ctx := storage.GetReadOnlyContext()
graveKey := append([]byte{graveKeyPrefix}, containerID...)
data := storage.Get(ctx, graveKey).([]byte)
if data == nil {
panic(NotFoundError)
}
return std.Deserialize(data).(DelInfo)
}
// Get method returns a structure that contains a stable marshaled Container structure,
// the signature, the public key of the container creator and a stable marshaled SessionToken
// structure if it was provided.
@ -596,6 +616,9 @@ func addContainer(ctx storage.Context, id, owner []byte, container Container) {
idKey := append([]byte{containerKeyPrefix}, id...)
common.SetSerialized(ctx, idKey, container)
graveKey := append([]byte{graveKeyPrefix}, id...)
storage.Delete(ctx, graveKey)
}
func removeContainer(ctx storage.Context, id []byte, owner []byte) {
@ -604,6 +627,14 @@ func removeContainer(ctx storage.Context, id []byte, owner []byte) {
storage.Delete(ctx, containerListKey)
storage.Delete(ctx, append([]byte{containerKeyPrefix}, id...))
graveKey := append([]byte{graveKeyPrefix}, id...)
netmapContractAddr := storage.Get(ctx, netmapContractKey).(interop.Hash160)
epoch := contract.Call(netmapContractAddr, "epoch", contract.ReadOnly).(int)
common.SetSerialized(ctx, graveKey, DelInfo{
Owner: owner,
Epoch: epoch,
})
}
func getAllContainers(ctx storage.Context) [][]byte {

View file

@ -243,19 +243,43 @@ func addContainer(t *testing.T, c, cBal *neotest.ContractInvoker) (neotest.Signe
}
func TestContainerDelete(t *testing.T) {
c, cBal, _ := newContainerInvoker(t)
c, cBal, cNm := newContainerInvoker(t)
acc, cnt := addContainer(t, c, cBal)
cAcc := c.WithSigners(acc)
cAcc.InvokeFail(t, common.ErrAlphabetWitnessFailed, "delete",
cnt.id[:], cnt.sig, cnt.pub, cnt.token)
newDelInfo := func(acc neotest.Signer, epoch int64) *stackitem.Struct {
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewBuffer([]byte(signerToOwner(acc))),
stackitem.NewBigInteger(big.NewInt(epoch)),
})
}
c.InvokeFail(t, container.NotFoundError, "deletionInfo", cnt.id[:])
c.Invoke(t, stackitem.Null{}, "delete", cnt.id[:], cnt.sig, cnt.pub, cnt.token)
c.Invoke(t, newDelInfo(acc, 0), "deletionInfo", cnt.id[:])
t.Run("multi-epoch", func(t *testing.T) {
cNm.Invoke(t, stackitem.Null{}, "newEpoch", 1)
t.Run("epoch tick does not change deletion info", func(t *testing.T) {
c.Invoke(t, stackitem.Null{}, "delete", cnt.id[:], cnt.sig, cnt.pub, cnt.token)
c.Invoke(t, newDelInfo(acc, 0), "deletionInfo", cnt.id[:])
})
acc1, cnt1 := addContainer(t, c, cBal)
c.Invoke(t, stackitem.Null{}, "delete", cnt1.id[:], cnt1.sig, cnt1.pub, cnt1.token)
c.Invoke(t, newDelInfo(acc, 0), "deletionInfo", cnt.id[:])
c.Invoke(t, newDelInfo(acc1, 1), "deletionInfo", cnt1.id[:])
})
t.Run("missing container", func(t *testing.T) {
id := cnt.id
id[0] ^= 0xFF
c.Invoke(t, stackitem.Null{}, "delete", cnt.id[:], cnt.sig, cnt.pub, cnt.token)
c.InvokeFail(t, container.NotFoundError, "deletionInfo", id[:])
})
c.InvokeFail(t, container.NotFoundError, "get", cnt.id[:])