forked from TrueCloudLab/frostfs-contract
[#122] subnet: implement put
method
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
parent
6250e5eaf7
commit
ed6f90c180
3 changed files with 112 additions and 2 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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{}) {
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue