[#122] subnet: implement put method

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2021-11-18 15:21:14 +03:00 committed by Alex Vanin
parent 6250e5eaf7
commit ed6f90c180
3 changed files with 112 additions and 2 deletions

View file

@ -1,4 +1,12 @@
name: "NeoFS Subnet" name: "NeoFS Subnet"
safemethods: ["version"] safemethods: ["version"]
permissions: permissions:
- methods: ["update"] - methods: ["update"]
events:
- name: SubnetPut
parameters:
- name: ownerKey
type: PublicKey
- name: info
type: ByteArray

View file

@ -10,6 +10,17 @@ import (
) )
const ( const (
// ErrInvalidSubnetID is thrown when subnet id is not a slice of 4 bytes.
ErrInvalidSubnetID = "invalid subnet ID"
// ErrInvalidOwner is thrown when owner has invalid format.
ErrInvalidOwner = "invalid owner"
// ErrAlreadyExists is thrown when id already exists.
ErrAlreadyExists = "subnet id already exists"
// ErrNotExist is thrown when id doesn't exist.
ErrNotExist = "subnet id doesn't exist"
ownerPrefix = 'o'
infoPrefix = 'i'
notaryDisabledKey = 'z' notaryDisabledKey = 'z'
) )
@ -27,6 +38,68 @@ func _deploy(data interface{}, isUpdate bool) {
storage.Put(ctx, []byte{notaryDisabledKey}, args.notaryDisabled) storage.Put(ctx, []byte{notaryDisabledKey}, args.notaryDisabled)
} }
// Put creates new subnet with the specified owner and info.
func Put(id []byte, ownerKey interop.PublicKey, info []byte) {
if len(id) != 4 {
panic("put: " + ErrInvalidSubnetID)
}
if len(ownerKey) != interop.PublicKeyCompressedLen {
panic("put: " + ErrInvalidOwner)
}
ctx := storage.GetContext()
stKey := append([]byte{ownerPrefix}, id...)
if storage.Get(ctx, stKey) != nil {
panic("put: " + ErrAlreadyExists)
}
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
if notaryDisabled {
alphabet := common.AlphabetNodes()
nodeKey := common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
if !runtime.CheckWitness(ownerKey) {
panic("put: witness check failed")
}
runtime.Notify("SubnetPut", ownerKey, info)
return
}
threshold := len(alphabet)*2/3 + 1
id := common.InvokeID([]interface{}{ownerKey, info}, []byte("put"))
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
} else {
if !runtime.CheckWitness(ownerKey) {
panic("put: owner witness check failed")
}
multiaddr := common.AlphabetAddress()
if !runtime.CheckWitness(multiaddr) {
panic("put: alphabet witness check failed")
}
}
storage.Put(ctx, stKey, ownerKey)
stKey[0] = infoPrefix
storage.Put(ctx, stKey, info)
}
// Get returns info about subnet with the specified id.
func Get(id []byte) []byte {
ctx := storage.GetContext()
key := append([]byte{infoPrefix}, id...)
raw := storage.Get(ctx, key)
if raw == nil {
panic("get: " + ErrNotExist)
}
return raw.([]byte)
}
// Update method updates contract source code and manifest. Can be invoked // Update method updates contract source code and manifest. Can be invoked
// only by committee. // only by committee.
func Update(script []byte, manifest []byte, data interface{}) { func Update(script []byte, manifest []byte, data interface{}) {

View file

@ -1,19 +1,24 @@
package tests package tests
import ( import (
"encoding/binary"
"path" "path"
"testing" "testing"
"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"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neofs-contract/common" "github.com/nspcc-dev/neofs-contract/common"
"github.com/nspcc-dev/neofs-contract/subnet"
"github.com/stretchr/testify/require"
) )
const subnetPath = "../subnet" const subnetPath = "../subnet"
func deploySubnetContract(t *testing.T, e *neotest.Executor) util.Uint160 { func deploySubnetContract(t *testing.T, e *neotest.Executor) util.Uint160 {
c := neotest.CompileFile(t, e.CommitteeHash, subnetPath, path.Join(subnetPath, "config.yml")) c := neotest.CompileFile(t, e.CommitteeHash, subnetPath, path.Join(subnetPath, "config.yml"))
args := []interface{}{true} args := []interface{}{false}
e.DeployContract(t, c, args) e.DeployContract(t, c, args)
return c.Hash return c.Hash
} }
@ -28,3 +33,27 @@ func TestSubnet_Version(t *testing.T) {
e := newSubnetInvoker(t) e := newSubnetInvoker(t)
e.Invoke(t, common.Version, "version") e.Invoke(t, common.Version, "version")
} }
func TestSubnet_Put(t *testing.T) {
e := newSubnetInvoker(t)
acc := e.NewAccount(t)
pub, ok := vm.ParseSignatureContract(acc.Script())
require.True(t, ok)
id := make([]byte, 4)
binary.LittleEndian.PutUint32(id, 123)
info := randomBytes(10)
e.InvokeFail(t, "witness check failed", "put", id, pub, info)
cAcc := e.WithSigners(acc)
cAcc.InvokeFail(t, "alphabet witness check failed", "put", id, pub, info)
cBoth := e.WithSigners(e.Committee, acc)
cBoth.InvokeFail(t, subnet.ErrInvalidSubnetID, "put", []byte{1, 2, 3}, pub, info)
cBoth.InvokeFail(t, subnet.ErrInvalidOwner, "put", id, pub[10:], info)
cBoth.Invoke(t, stackitem.Null{}, "put", id, pub, info)
cAcc.Invoke(t, stackitem.NewBuffer(info), "get", id)
cBoth.InvokeFail(t, subnet.ErrAlreadyExists, "put", id, pub, info)
}