From 6bd088aa9fe0352a5eff845db637c4557eb540b0 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Mon, 11 Oct 2021 10:42:14 +0300 Subject: [PATCH] [#142] container: panic on missing container Return more descriptive error message for a user. Signed-off-by: Evgenii Stratonikov --- container/container_contract.go | 43 ++++++++++++++++---- tests/container_test.go | 70 +++++++++++++++++++++++++++++++-- 2 files changed, 101 insertions(+), 12 deletions(-) diff --git a/container/container_contract.go b/container/container_contract.go index 03799ee..3cff17c 100644 --- a/container/container_contract.go +++ b/container/container_contract.go @@ -61,6 +61,9 @@ const ( estimateKeyPrefix = "cnr" estimatePostfixSize = 10 cleanupDelta = 3 + + // NotFoundError is returned if container is missing. + NotFoundError = "container does not exist" ) var ( @@ -292,13 +295,15 @@ func checkNiceNameAvailable(nnsContractAddr interop.Hash160, domain string) bool // Signature is a RFC6979 signature of container ID. // Token is optional and should be stable marshaled SessionToken structure from // API. +// +// If a container doesn't exist it panics with NotFoundError. func Delete(containerID []byte, signature interop.Signature, token []byte) { ctx := storage.GetContext() notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool) ownerID := getOwnerByID(ctx, containerID) - if len(ownerID) == 0 { - return + if ownerID == nil { + panic(NotFoundError) } if notaryDisabled { @@ -345,15 +350,27 @@ func Delete(containerID []byte, signature interop.Signature, token []byte) { // Get method returns structure that contains stable marshaled Container structure, // signature, public key of the container creator and stable marshaled SessionToken // structure if it was provided. +// +// If a container doesn't exist it panics with NotFoundError. func Get(containerID []byte) Container { ctx := storage.GetReadOnlyContext() - return getContainer(ctx, containerID) + cnt := getContainer(ctx, containerID) + if len(cnt.value) == 0 { + panic(NotFoundError) + } + return cnt } // Owner method returns 25 byte Owner ID of the container. +// +// If a container doesn't exist it panics with NotFoundError. func Owner(containerID []byte) []byte { ctx := storage.GetReadOnlyContext() - return getOwnerByID(ctx, containerID) + owner := getOwnerByID(ctx, containerID) + if owner == nil { + panic(NotFoundError) + } + return owner } // List method returns list of all container IDs owned by specified owner. @@ -384,6 +401,8 @@ func List(owner []byte) [][]byte { // PublicKey contains public key of the signer. // Token is optional and should be stable marshaled SessionToken structure from // API. +// +// If a container doesn't exist it panics with NotFoundError. func SetEACL(eACL []byte, signature interop.Signature, publicKey interop.PublicKey, token []byte) { ctx := storage.GetContext() notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool) @@ -394,8 +413,8 @@ func SetEACL(eACL []byte, signature interop.Signature, publicKey interop.PublicK containerID := eACL[offset : offset+32] ownerID := getOwnerByID(ctx, containerID) - if len(ownerID) == 0 { - panic("container does not exist") + if ownerID == nil { + panic(NotFoundError) } if notaryDisabled { @@ -439,12 +458,14 @@ func SetEACL(eACL []byte, signature interop.Signature, publicKey interop.PublicK // EACL method returns structure that contains stable marshaled EACLTable structure, // signature, public key of the extended ACL setter and stable marshaled SessionToken // structure if it was provided. +// +// If a container doesn't exist it panics with NotFoundError. func EACL(containerID []byte) ExtendedACL { ctx := storage.GetReadOnlyContext() ownerID := getOwnerByID(ctx, containerID) - if len(ownerID) == 0 { - panic("container does not exist") + if ownerID == nil { + panic(NotFoundError) } return getEACL(ctx, containerID) @@ -453,9 +474,15 @@ func EACL(containerID []byte) ExtendedACL { // PutContainerSize method saves container size estimation in contract // memory. Can be invoked only by Storage nodes from the network map. Method // checks witness based on the provided public key of the Storage node. +// +// If a container doesn't exist it panics with NotFoundError. func PutContainerSize(epoch int, cid []byte, usedSize int, pubKey interop.PublicKey) { ctx := storage.GetContext() + if getOwnerByID(ctx, cid) == nil { + panic(NotFoundError) + } + if !runtime.CheckWitness(pubKey) { panic("invalid witness of container size estimation") } diff --git a/tests/container_test.go b/tests/container_test.go index 92bc505..f0f94e4 100644 --- a/tests/container_test.go +++ b/tests/container_test.go @@ -2,6 +2,7 @@ package tests import ( "crypto/sha256" + "strings" "testing" "github.com/mr-tron/base58" @@ -185,11 +186,62 @@ func TestContainerDelete(t *testing.T) { AddBlockCheckHalt(t, bc, tx) tx = PrepareInvoke(t, bc, acc, h, "get", c.id[:]) + _, err := TestInvoke(bc, tx) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), container.NotFoundError)) +} + +func TestContainerOwner(t *testing.T) { + bc := NewChain(t) + h, balanceHash := prepareContainerContract(t, bc) + + acc := NewAccount(t, bc) + balanceMint(t, bc, acc, balanceHash, containerFee*1, []byte{}) + + c := dummyContainer(acc) + tx := PrepareInvoke(t, bc, CommitteeAcc, h, "put", c.value, c.sig, c.pub, c.token) + AddBlockCheckHalt(t, bc, tx) + + t.Run("missing container", func(t *testing.T) { + id := c.id + id[0] ^= 0xFF + tx = PrepareInvoke(t, bc, CommitteeAcc, h, "owner", id[:]) + _, err := TestInvoke(bc, tx) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), container.NotFoundError)) + }) + + tx = PrepareInvoke(t, bc, CommitteeAcc, h, "owner", c.id[:]) + owner, _ := base58.Decode(acc.Address) + CheckTestInvoke(t, bc, tx, stackitem.NewBuffer(owner)) +} + +func TestContainerGet(t *testing.T) { + bc := NewChain(t) + h, balanceHash := prepareContainerContract(t, bc) + + acc := NewAccount(t, bc) + balanceMint(t, bc, acc, balanceHash, containerFee*1, []byte{}) + + c := dummyContainer(acc) + tx := PrepareInvoke(t, bc, CommitteeAcc, h, "put", c.value, c.sig, c.pub, c.token) + AddBlockCheckHalt(t, bc, tx) + + t.Run("missing container", func(t *testing.T) { + id := c.id + id[0] ^= 0xFF + tx = PrepareInvoke(t, bc, CommitteeAcc, h, "get", id[:]) + _, err := TestInvoke(bc, tx) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), container.NotFoundError)) + }) + + tx = PrepareInvoke(t, bc, CommitteeAcc, h, "get", c.id[:]) CheckTestInvoke(t, bc, tx, stackitem.NewStruct([]stackitem.Item{ - stackitem.NewBuffer([]byte{}), - stackitem.NewBuffer([]byte{}), - stackitem.NewBuffer([]byte{}), - stackitem.NewBuffer([]byte{}), + stackitem.NewByteArray(c.value), + stackitem.NewByteArray(c.sig), + stackitem.NewByteArray(c.pub), + stackitem.NewByteArray(c.token), })) } @@ -222,6 +274,16 @@ func TestContainerSetEACL(t *testing.T) { tx := PrepareInvoke(t, bc, CommitteeAcc, h, "put", c.value, c.sig, c.pub, c.token) AddBlockCheckHalt(t, bc, tx) + t.Run("missing container", func(t *testing.T) { + id := c.id + id[0] ^= 0xFF + e := dummyEACL(id) + tx = PrepareInvoke(t, bc, CommitteeAcc, h, "setEACL", e.value, e.sig, e.pub, e.token) + _, err := TestInvoke(bc, tx) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), container.NotFoundError)) + }) + e := dummyEACL(c.id) tx = PrepareInvoke(t, bc, acc, h, "setEACL", e.value, e.sig, e.pub, e.token) AddBlock(t, bc, tx)