[#142] container: panic on missing container

Return more descriptive error message for a user.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2021-10-11 10:42:14 +03:00 committed by Alex Vanin
parent 654fc371fd
commit 6bd088aa9f
2 changed files with 101 additions and 12 deletions

View file

@ -61,6 +61,9 @@ const (
estimateKeyPrefix = "cnr" estimateKeyPrefix = "cnr"
estimatePostfixSize = 10 estimatePostfixSize = 10
cleanupDelta = 3 cleanupDelta = 3
// NotFoundError is returned if container is missing.
NotFoundError = "container does not exist"
) )
var ( var (
@ -292,13 +295,15 @@ func checkNiceNameAvailable(nnsContractAddr interop.Hash160, domain string) bool
// Signature is a RFC6979 signature of container ID. // Signature is a RFC6979 signature of container ID.
// Token is optional and should be stable marshaled SessionToken structure from // Token is optional and should be stable marshaled SessionToken structure from
// API. // API.
//
// If a container doesn't exist it panics with NotFoundError.
func Delete(containerID []byte, signature interop.Signature, token []byte) { func Delete(containerID []byte, signature interop.Signature, token []byte) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool) notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
ownerID := getOwnerByID(ctx, containerID) ownerID := getOwnerByID(ctx, containerID)
if len(ownerID) == 0 { if ownerID == nil {
return panic(NotFoundError)
} }
if notaryDisabled { 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, // Get method returns structure that contains stable marshaled Container structure,
// signature, public key of the container creator and stable marshaled SessionToken // signature, public key of the container creator and stable marshaled SessionToken
// structure if it was provided. // structure if it was provided.
//
// If a container doesn't exist it panics with NotFoundError.
func Get(containerID []byte) Container { func Get(containerID []byte) Container {
ctx := storage.GetReadOnlyContext() 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. // 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 { func Owner(containerID []byte) []byte {
ctx := storage.GetReadOnlyContext() 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. // 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. // PublicKey contains public key of the signer.
// Token is optional and should be stable marshaled SessionToken structure from // Token is optional and should be stable marshaled SessionToken structure from
// API. // API.
//
// If a container doesn't exist it panics with NotFoundError.
func SetEACL(eACL []byte, signature interop.Signature, publicKey interop.PublicKey, token []byte) { func SetEACL(eACL []byte, signature interop.Signature, publicKey interop.PublicKey, token []byte) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool) 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] containerID := eACL[offset : offset+32]
ownerID := getOwnerByID(ctx, containerID) ownerID := getOwnerByID(ctx, containerID)
if len(ownerID) == 0 { if ownerID == nil {
panic("container does not exist") panic(NotFoundError)
} }
if notaryDisabled { 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, // EACL method returns structure that contains stable marshaled EACLTable structure,
// signature, public key of the extended ACL setter and stable marshaled SessionToken // signature, public key of the extended ACL setter and stable marshaled SessionToken
// structure if it was provided. // structure if it was provided.
//
// If a container doesn't exist it panics with NotFoundError.
func EACL(containerID []byte) ExtendedACL { func EACL(containerID []byte) ExtendedACL {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
ownerID := getOwnerByID(ctx, containerID) ownerID := getOwnerByID(ctx, containerID)
if len(ownerID) == 0 { if ownerID == nil {
panic("container does not exist") panic(NotFoundError)
} }
return getEACL(ctx, containerID) return getEACL(ctx, containerID)
@ -453,9 +474,15 @@ func EACL(containerID []byte) ExtendedACL {
// PutContainerSize method saves container size estimation in contract // PutContainerSize method saves container size estimation in contract
// memory. Can be invoked only by Storage nodes from the network map. Method // 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. // 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) { func PutContainerSize(epoch int, cid []byte, usedSize int, pubKey interop.PublicKey) {
ctx := storage.GetContext() ctx := storage.GetContext()
if getOwnerByID(ctx, cid) == nil {
panic(NotFoundError)
}
if !runtime.CheckWitness(pubKey) { if !runtime.CheckWitness(pubKey) {
panic("invalid witness of container size estimation") panic("invalid witness of container size estimation")
} }

View file

@ -2,6 +2,7 @@ package tests
import ( import (
"crypto/sha256" "crypto/sha256"
"strings"
"testing" "testing"
"github.com/mr-tron/base58" "github.com/mr-tron/base58"
@ -185,11 +186,62 @@ func TestContainerDelete(t *testing.T) {
AddBlockCheckHalt(t, bc, tx) AddBlockCheckHalt(t, bc, tx)
tx = PrepareInvoke(t, bc, acc, h, "get", c.id[:]) 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{ CheckTestInvoke(t, bc, tx, stackitem.NewStruct([]stackitem.Item{
stackitem.NewBuffer([]byte{}), stackitem.NewByteArray(c.value),
stackitem.NewBuffer([]byte{}), stackitem.NewByteArray(c.sig),
stackitem.NewBuffer([]byte{}), stackitem.NewByteArray(c.pub),
stackitem.NewBuffer([]byte{}), 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) tx := PrepareInvoke(t, bc, CommitteeAcc, h, "put", c.value, c.sig, c.pub, c.token)
AddBlockCheckHalt(t, bc, tx) 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) e := dummyEACL(c.id)
tx = PrepareInvoke(t, bc, acc, h, "setEACL", e.value, e.sig, e.pub, e.token) tx = PrepareInvoke(t, bc, acc, h, "setEACL", e.value, e.sig, e.pub, e.token)
AddBlock(t, bc, tx) AddBlock(t, bc, tx)