[#20] subnet: Drop contract
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
This commit is contained in:
parent
e19fe15ed8
commit
8537293e11
8 changed files with 3 additions and 953 deletions
|
@ -6,6 +6,8 @@ Changelog for FrostFS Contract
|
||||||
### Added
|
### Added
|
||||||
### Changed
|
### Changed
|
||||||
### Removed
|
### Removed
|
||||||
|
- `subnet` contract (#20)
|
||||||
|
|
||||||
### Updated
|
### Updated
|
||||||
### Fixed
|
### Fixed
|
||||||
### Updating from v0.17.0
|
### Updating from v0.17.0
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -22,7 +22,7 @@ all: sidechain mainnet
|
||||||
sidechain: alphabet morph nns
|
sidechain: alphabet morph nns
|
||||||
|
|
||||||
alphabet_sc = alphabet
|
alphabet_sc = alphabet
|
||||||
morph_sc = audit balance container frostfsid netmap proxy reputation subnet
|
morph_sc = audit balance container frostfsid netmap proxy reputation
|
||||||
mainnet_sc = frostfs processing
|
mainnet_sc = frostfs processing
|
||||||
nns_sc = nns
|
nns_sc = nns
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ Sidechain contracts:
|
||||||
- nns
|
- nns
|
||||||
- proxy
|
- proxy
|
||||||
- reputation
|
- reputation
|
||||||
- subnet
|
|
||||||
|
|
||||||
# Getting started
|
# Getting started
|
||||||
|
|
||||||
|
@ -55,7 +54,6 @@ $ make all
|
||||||
/home/user/go/bin/cli contract compile -i netmap -c netmap/config.yml -m netmap/config.json -o netmap/netmap_contract.nef
|
/home/user/go/bin/cli contract compile -i netmap -c netmap/config.yml -m netmap/config.json -o netmap/netmap_contract.nef
|
||||||
/home/user/go/bin/cli contract compile -i proxy -c proxy/config.yml -m proxy/config.json -o proxy/proxy_contract.nef
|
/home/user/go/bin/cli contract compile -i proxy -c proxy/config.yml -m proxy/config.json -o proxy/proxy_contract.nef
|
||||||
/home/user/go/bin/cli contract compile -i reputation -c reputation/config.yml -m reputation/config.json -o reputation/reputation_contract.nef
|
/home/user/go/bin/cli contract compile -i reputation -c reputation/config.yml -m reputation/config.json -o reputation/reputation_contract.nef
|
||||||
/home/user/go/bin/cli contract compile -i subnet -c subnet/config.yml -m subnet/config.json -o subnet/subnet_contract.nef
|
|
||||||
/home/user/go/bin/cli contract compile -i nns -c nns/config.yml -m nns/config.json -o nns/nns_contract.nef
|
/home/user/go/bin/cli contract compile -i nns -c nns/config.yml -m nns/config.json -o nns/nns_contract.nef
|
||||||
/home/user/go/bin/cli contract compile -i frostfs -c frostfs/config.yml -m frostfs/config.json -o frostfs/frostfs_contract.nef
|
/home/user/go/bin/cli contract compile -i frostfs -c frostfs/config.yml -m frostfs/config.json -o frostfs/frostfs_contract.nef
|
||||||
/home/user/go/bin/cli contract compile -i processing -c processing/config.yml -m processing/config.json -o processing/processing_contract.nef
|
/home/user/go/bin/cli contract compile -i processing -c processing/config.yml -m processing/config.json -o processing/processing_contract.nef
|
||||||
|
|
1
debian/control
vendored
1
debian/control
vendored
|
@ -31,4 +31,3 @@ Description: FrostFS-Contract contains all FrostFS related contracts.
|
||||||
- nns
|
- nns
|
||||||
- proxy
|
- proxy
|
||||||
- reputation
|
- reputation
|
||||||
- subnet
|
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
name: "Subnet"
|
|
||||||
safemethods: ["version"]
|
|
||||||
permissions:
|
|
||||||
- methods: ["update"]
|
|
||||||
events:
|
|
||||||
- name: Put
|
|
||||||
parameters:
|
|
||||||
- name: id
|
|
||||||
type: ByteArray
|
|
||||||
- name: ownerKey
|
|
||||||
type: PublicKey
|
|
||||||
- name: info
|
|
||||||
type: ByteArray
|
|
||||||
- name: Delete
|
|
||||||
parameters:
|
|
||||||
- name: id
|
|
||||||
type: ByteArray
|
|
||||||
- name: RemoveNode
|
|
||||||
parameters:
|
|
||||||
- name: subnetID
|
|
||||||
type: ByteArray
|
|
||||||
- name: node
|
|
||||||
type: PublicKey
|
|
|
@ -1,37 +0,0 @@
|
||||||
/*
|
|
||||||
Subnet contract is a contract deployed in FrostFS sidechain.
|
|
||||||
|
|
||||||
Subnet contract stores and manages FrostFS subnetwork states. It allows registering
|
|
||||||
and deleting subnetworks, limiting access to them, and defining a list of the Storage
|
|
||||||
Nodes that can be included in them.
|
|
||||||
|
|
||||||
# Contract notifications
|
|
||||||
|
|
||||||
Put notification. This notification is produced when a new subnetwork is
|
|
||||||
registered by invoking Put method.
|
|
||||||
|
|
||||||
Put
|
|
||||||
- name: id
|
|
||||||
type: ByteArray
|
|
||||||
- name: ownerKey
|
|
||||||
type: PublicKey
|
|
||||||
- name: info
|
|
||||||
type: ByteArray
|
|
||||||
|
|
||||||
Delete notification. This notification is produced when some subnetwork is
|
|
||||||
deleted by invoking Delete method.
|
|
||||||
|
|
||||||
Delete
|
|
||||||
- name: id
|
|
||||||
type: ByteArray
|
|
||||||
|
|
||||||
RemoveNode notification. This notification is produced when some node is deleted
|
|
||||||
by invoking RemoveNode method.
|
|
||||||
|
|
||||||
RemoveNode
|
|
||||||
- name: subnetID
|
|
||||||
type: ByteArray
|
|
||||||
- name: node
|
|
||||||
type: PublicKey
|
|
||||||
*/
|
|
||||||
package subnet
|
|
|
@ -1,559 +0,0 @@
|
||||||
package subnet
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// ErrInvalidSubnetID is thrown when subnet id is not a slice of 5 bytes.
|
|
||||||
ErrInvalidSubnetID = "invalid subnet ID"
|
|
||||||
// ErrInvalidGroupID is thrown when group id is not a slice of 5 bytes.
|
|
||||||
ErrInvalidGroupID = "invalid group ID"
|
|
||||||
// ErrInvalidOwner is thrown when owner has invalid format.
|
|
||||||
ErrInvalidOwner = "invalid owner"
|
|
||||||
// ErrInvalidAdmin is thrown when admin has invalid format.
|
|
||||||
ErrInvalidAdmin = "invalid administrator"
|
|
||||||
// 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"
|
|
||||||
// ErrInvalidUser is thrown when user has invalid format.
|
|
||||||
ErrInvalidUser = "invalid user"
|
|
||||||
// ErrInvalidNode is thrown when node has invalid format.
|
|
||||||
ErrInvalidNode = "invalid node key"
|
|
||||||
// ErrAccessDenied is thrown when operation is denied for caller.
|
|
||||||
ErrAccessDenied = "access denied"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
nodeAdminPrefix = 'a'
|
|
||||||
infoPrefix = 'i'
|
|
||||||
clientAdminPrefix = 'm'
|
|
||||||
nodePrefix = 'n'
|
|
||||||
ownerPrefix = 'o'
|
|
||||||
userPrefix = 'u'
|
|
||||||
notaryDisabledKey = 'z'
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
userIDSize = 27
|
|
||||||
subnetIDSize = 5
|
|
||||||
groupIDSize = 5
|
|
||||||
)
|
|
||||||
|
|
||||||
// _deploy function sets up initial list of inner ring public keys.
|
|
||||||
func _deploy(data interface{}, isUpdate bool) {
|
|
||||||
common.RmAndCheckNotaryDisabledKey(data, notaryDisabledKey)
|
|
||||||
|
|
||||||
if isUpdate {
|
|
||||||
args := data.([]interface{})
|
|
||||||
common.CheckVersion(args[len(args)-1].(int))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update method updates contract source code and manifest. It can be invoked
|
|
||||||
// only by committee.
|
|
||||||
func Update(script []byte, manifest []byte, data interface{}) {
|
|
||||||
if !common.HasUpdateAccess() {
|
|
||||||
panic("only committee can update contract")
|
|
||||||
}
|
|
||||||
|
|
||||||
contract.Call(interop.Hash160(management.Hash), "update", contract.All,
|
|
||||||
script, manifest, common.AppendVersion(data))
|
|
||||||
runtime.Log("subnet contract updated")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put creates a new subnet with the specified owner and info.
|
|
||||||
func Put(id []byte, ownerKey interop.PublicKey, info []byte) {
|
|
||||||
// V2 format
|
|
||||||
if len(id) != subnetIDSize {
|
|
||||||
panic(ErrInvalidSubnetID)
|
|
||||||
}
|
|
||||||
if len(ownerKey) != interop.PublicKeyCompressedLen {
|
|
||||||
panic(ErrInvalidOwner)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := storage.GetContext()
|
|
||||||
stKey := append([]byte{ownerPrefix}, id...)
|
|
||||||
if storage.Get(ctx, stKey) != nil {
|
|
||||||
panic(ErrAlreadyExists)
|
|
||||||
}
|
|
||||||
|
|
||||||
common.CheckOwnerWitness(ownerKey)
|
|
||||||
|
|
||||||
common.CheckAlphabetWitness()
|
|
||||||
|
|
||||||
storage.Put(ctx, stKey, ownerKey)
|
|
||||||
stKey[0] = infoPrefix
|
|
||||||
storage.Put(ctx, stKey, info)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns info about the subnet with the specified id.
|
|
||||||
func Get(id []byte) []byte {
|
|
||||||
// V2 format
|
|
||||||
if len(id) != subnetIDSize {
|
|
||||||
panic(ErrInvalidSubnetID)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := storage.GetReadOnlyContext()
|
|
||||||
key := append([]byte{infoPrefix}, id...)
|
|
||||||
raw := storage.Get(ctx, key)
|
|
||||||
if raw == nil {
|
|
||||||
panic(ErrNotExist)
|
|
||||||
}
|
|
||||||
return raw.([]byte)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete deletes the subnet with the specified id.
|
|
||||||
func Delete(id []byte) {
|
|
||||||
// V2 format
|
|
||||||
if len(id) != subnetIDSize {
|
|
||||||
panic(ErrInvalidSubnetID)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := storage.GetContext()
|
|
||||||
key := append([]byte{ownerPrefix}, id...)
|
|
||||||
raw := storage.Get(ctx, key)
|
|
||||||
if raw == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
owner := raw.([]byte)
|
|
||||||
common.CheckOwnerWitness(owner)
|
|
||||||
|
|
||||||
storage.Delete(ctx, key)
|
|
||||||
|
|
||||||
key[0] = infoPrefix
|
|
||||||
storage.Delete(ctx, key)
|
|
||||||
|
|
||||||
key[0] = nodeAdminPrefix
|
|
||||||
deleteByPrefix(ctx, key)
|
|
||||||
|
|
||||||
key[0] = nodePrefix
|
|
||||||
deleteByPrefix(ctx, key)
|
|
||||||
|
|
||||||
key[0] = clientAdminPrefix
|
|
||||||
deleteByPrefix(ctx, key)
|
|
||||||
|
|
||||||
key[0] = userPrefix
|
|
||||||
deleteByPrefix(ctx, key)
|
|
||||||
|
|
||||||
runtime.Notify("Delete", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddNodeAdmin adds a new node administrator to the specified subnetwork.
|
|
||||||
func AddNodeAdmin(subnetID []byte, adminKey interop.PublicKey) {
|
|
||||||
// V2 format
|
|
||||||
if len(subnetID) != subnetIDSize {
|
|
||||||
panic(ErrInvalidSubnetID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(adminKey) != interop.PublicKeyCompressedLen {
|
|
||||||
panic(ErrInvalidAdmin)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := storage.GetContext()
|
|
||||||
|
|
||||||
stKey := append([]byte{ownerPrefix}, subnetID...)
|
|
||||||
|
|
||||||
rawOwner := storage.Get(ctx, stKey)
|
|
||||||
if rawOwner == nil {
|
|
||||||
panic(ErrNotExist)
|
|
||||||
}
|
|
||||||
|
|
||||||
owner := rawOwner.([]byte)
|
|
||||||
common.CheckOwnerWitness(owner)
|
|
||||||
|
|
||||||
stKey[0] = nodeAdminPrefix
|
|
||||||
|
|
||||||
if keyInList(ctx, adminKey, stKey) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
putKeyInList(ctx, adminKey, stKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveNodeAdmin removes node administrator from the specified subnetwork.
|
|
||||||
// Must be called by the subnet owner only.
|
|
||||||
func RemoveNodeAdmin(subnetID []byte, adminKey interop.PublicKey) {
|
|
||||||
// V2 format
|
|
||||||
if len(subnetID) != subnetIDSize {
|
|
||||||
panic(ErrInvalidSubnetID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(adminKey) != interop.PublicKeyCompressedLen {
|
|
||||||
panic(ErrInvalidAdmin)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := storage.GetContext()
|
|
||||||
|
|
||||||
stKey := append([]byte{ownerPrefix}, subnetID...)
|
|
||||||
|
|
||||||
rawOwner := storage.Get(ctx, stKey)
|
|
||||||
if rawOwner == nil {
|
|
||||||
panic(ErrNotExist)
|
|
||||||
}
|
|
||||||
|
|
||||||
owner := rawOwner.([]byte)
|
|
||||||
common.CheckOwnerWitness(owner)
|
|
||||||
|
|
||||||
stKey[0] = nodeAdminPrefix
|
|
||||||
|
|
||||||
if !keyInList(ctx, adminKey, stKey) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteKeyFromList(ctx, adminKey, stKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddNode adds a node to the specified subnetwork.
|
|
||||||
// Must be called by the subnet's owner or the node administrator
|
|
||||||
// only.
|
|
||||||
func AddNode(subnetID []byte, node interop.PublicKey) {
|
|
||||||
// V2 format
|
|
||||||
if len(subnetID) != subnetIDSize {
|
|
||||||
panic(ErrInvalidSubnetID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(node) != interop.PublicKeyCompressedLen {
|
|
||||||
panic(ErrInvalidNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := storage.GetContext()
|
|
||||||
|
|
||||||
stKey := append([]byte{ownerPrefix}, subnetID...)
|
|
||||||
|
|
||||||
rawOwner := storage.Get(ctx, stKey)
|
|
||||||
if rawOwner == nil {
|
|
||||||
panic(ErrNotExist)
|
|
||||||
}
|
|
||||||
|
|
||||||
stKey[0] = nodeAdminPrefix
|
|
||||||
|
|
||||||
owner := rawOwner.([]byte)
|
|
||||||
|
|
||||||
if !calledByOwnerOrAdmin(ctx, owner, stKey) {
|
|
||||||
panic(ErrAccessDenied)
|
|
||||||
}
|
|
||||||
|
|
||||||
stKey[0] = nodePrefix
|
|
||||||
|
|
||||||
if keyInList(ctx, node, stKey) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
putKeyInList(ctx, node, stKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveNode removes a node from the specified subnetwork.
|
|
||||||
// Must be called by the subnet's owner or the node administrator
|
|
||||||
// only.
|
|
||||||
func RemoveNode(subnetID []byte, node interop.PublicKey) {
|
|
||||||
// V2 format
|
|
||||||
if len(subnetID) != subnetIDSize {
|
|
||||||
panic(ErrInvalidSubnetID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(node) != interop.PublicKeyCompressedLen {
|
|
||||||
panic(ErrInvalidNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := storage.GetContext()
|
|
||||||
|
|
||||||
stKey := append([]byte{ownerPrefix}, subnetID...)
|
|
||||||
|
|
||||||
rawOwner := storage.Get(ctx, stKey)
|
|
||||||
if rawOwner == nil {
|
|
||||||
panic(ErrNotExist)
|
|
||||||
}
|
|
||||||
|
|
||||||
stKey[0] = nodeAdminPrefix
|
|
||||||
|
|
||||||
owner := rawOwner.([]byte)
|
|
||||||
|
|
||||||
if !calledByOwnerOrAdmin(ctx, owner, stKey) {
|
|
||||||
panic(ErrAccessDenied)
|
|
||||||
}
|
|
||||||
|
|
||||||
stKey[0] = nodePrefix
|
|
||||||
|
|
||||||
if !keyInList(ctx, node, stKey) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
storage.Delete(ctx, append(stKey, node...))
|
|
||||||
|
|
||||||
runtime.Notify("RemoveNode", subnetID, node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NodeAllowed checks if a node is included in the
|
|
||||||
// specified subnet.
|
|
||||||
func NodeAllowed(subnetID []byte, node interop.PublicKey) bool {
|
|
||||||
// V2 format
|
|
||||||
if len(subnetID) != subnetIDSize {
|
|
||||||
panic(ErrInvalidSubnetID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(node) != interop.PublicKeyCompressedLen {
|
|
||||||
panic(ErrInvalidNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := storage.GetReadOnlyContext()
|
|
||||||
|
|
||||||
stKey := append([]byte{ownerPrefix}, subnetID...)
|
|
||||||
|
|
||||||
rawOwner := storage.Get(ctx, stKey)
|
|
||||||
if rawOwner == nil {
|
|
||||||
panic(ErrNotExist)
|
|
||||||
}
|
|
||||||
|
|
||||||
stKey[0] = nodePrefix
|
|
||||||
|
|
||||||
return storage.Get(ctx, append(stKey, node...)) != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddClientAdmin adds a new client administrator of the specified group in the specified subnetwork.
|
|
||||||
// Must be called by the owner only.
|
|
||||||
func AddClientAdmin(subnetID []byte, groupID []byte, adminPublicKey interop.PublicKey) {
|
|
||||||
// V2 format
|
|
||||||
if len(subnetID) != subnetIDSize {
|
|
||||||
panic(ErrInvalidSubnetID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// V2 format
|
|
||||||
if len(groupID) != groupIDSize {
|
|
||||||
panic(ErrInvalidGroupID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(adminPublicKey) != interop.PublicKeyCompressedLen {
|
|
||||||
panic(ErrInvalidAdmin)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := storage.GetContext()
|
|
||||||
|
|
||||||
stKey := append([]byte{ownerPrefix}, subnetID...)
|
|
||||||
|
|
||||||
rawOwner := storage.Get(ctx, stKey)
|
|
||||||
if rawOwner == nil {
|
|
||||||
panic(ErrNotExist)
|
|
||||||
}
|
|
||||||
|
|
||||||
owner := rawOwner.([]byte)
|
|
||||||
common.CheckOwnerWitness(owner)
|
|
||||||
|
|
||||||
stKey[0] = clientAdminPrefix
|
|
||||||
stKey = append(stKey, groupID...)
|
|
||||||
|
|
||||||
if keyInList(ctx, adminPublicKey, stKey) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
putKeyInList(ctx, adminPublicKey, stKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveClientAdmin removes client administrator from the
|
|
||||||
// specified group in the specified subnetwork.
|
|
||||||
// Must be called by the owner only.
|
|
||||||
func RemoveClientAdmin(subnetID []byte, groupID []byte, adminPublicKey interop.PublicKey) {
|
|
||||||
// V2 format
|
|
||||||
if len(subnetID) != subnetIDSize {
|
|
||||||
panic(ErrInvalidSubnetID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// V2 format
|
|
||||||
if len(groupID) != groupIDSize {
|
|
||||||
panic(ErrInvalidGroupID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(adminPublicKey) != interop.PublicKeyCompressedLen {
|
|
||||||
panic(ErrInvalidAdmin)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := storage.GetContext()
|
|
||||||
|
|
||||||
stKey := append([]byte{ownerPrefix}, subnetID...)
|
|
||||||
|
|
||||||
rawOwner := storage.Get(ctx, stKey)
|
|
||||||
if rawOwner == nil {
|
|
||||||
panic(ErrNotExist)
|
|
||||||
}
|
|
||||||
|
|
||||||
owner := rawOwner.([]byte)
|
|
||||||
common.CheckOwnerWitness(owner)
|
|
||||||
|
|
||||||
stKey[0] = clientAdminPrefix
|
|
||||||
stKey = append(stKey, groupID...)
|
|
||||||
|
|
||||||
if !keyInList(ctx, adminPublicKey, stKey) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteKeyFromList(ctx, adminPublicKey, stKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddUser adds user to the specified subnetwork and group.
|
|
||||||
// Must be called by the owner or the group's admin only.
|
|
||||||
func AddUser(subnetID []byte, groupID []byte, userID []byte) {
|
|
||||||
// V2 format
|
|
||||||
if len(subnetID) != subnetIDSize {
|
|
||||||
panic(ErrInvalidSubnetID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// V2 format
|
|
||||||
if len(userID) != userIDSize {
|
|
||||||
panic(ErrInvalidUser)
|
|
||||||
}
|
|
||||||
|
|
||||||
// V2 format
|
|
||||||
if len(groupID) != groupIDSize {
|
|
||||||
panic(ErrInvalidGroupID)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := storage.GetContext()
|
|
||||||
|
|
||||||
stKey := append([]byte{ownerPrefix}, subnetID...)
|
|
||||||
|
|
||||||
rawOwner := storage.Get(ctx, stKey)
|
|
||||||
if rawOwner == nil {
|
|
||||||
panic(ErrNotExist)
|
|
||||||
}
|
|
||||||
|
|
||||||
stKey[0] = clientAdminPrefix
|
|
||||||
stKey = append(stKey, groupID...)
|
|
||||||
|
|
||||||
owner := rawOwner.([]byte)
|
|
||||||
|
|
||||||
if !calledByOwnerOrAdmin(ctx, owner, stKey) {
|
|
||||||
panic(ErrAccessDenied)
|
|
||||||
}
|
|
||||||
|
|
||||||
stKey[0] = userPrefix
|
|
||||||
|
|
||||||
if keyInList(ctx, userID, stKey) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
putKeyInList(ctx, userID, stKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveUser removes a user from the specified subnetwork and group.
|
|
||||||
// Must be called by the owner or the group's admin only.
|
|
||||||
func RemoveUser(subnetID []byte, groupID []byte, userID []byte) {
|
|
||||||
// V2 format
|
|
||||||
if len(subnetID) != subnetIDSize {
|
|
||||||
panic(ErrInvalidSubnetID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// V2 format
|
|
||||||
if len(groupID) != groupIDSize {
|
|
||||||
panic(ErrInvalidGroupID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// V2 format
|
|
||||||
if len(userID) != userIDSize {
|
|
||||||
panic(ErrInvalidUser)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := storage.GetContext()
|
|
||||||
|
|
||||||
stKey := append([]byte{ownerPrefix}, subnetID...)
|
|
||||||
|
|
||||||
rawOwner := storage.Get(ctx, stKey)
|
|
||||||
if rawOwner == nil {
|
|
||||||
panic(ErrNotExist)
|
|
||||||
}
|
|
||||||
|
|
||||||
stKey[0] = clientAdminPrefix
|
|
||||||
stKey = append(stKey, groupID...)
|
|
||||||
|
|
||||||
owner := rawOwner.([]byte)
|
|
||||||
|
|
||||||
if !calledByOwnerOrAdmin(ctx, owner, stKey) {
|
|
||||||
panic(ErrAccessDenied)
|
|
||||||
}
|
|
||||||
|
|
||||||
stKey[0] = userPrefix
|
|
||||||
|
|
||||||
if !keyInList(ctx, userID, stKey) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteKeyFromList(ctx, userID, stKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UserAllowed returns bool that indicates if a node is included in the
|
|
||||||
// specified subnet.
|
|
||||||
func UserAllowed(subnetID []byte, user []byte) bool {
|
|
||||||
// V2 format
|
|
||||||
if len(subnetID) != subnetIDSize {
|
|
||||||
panic(ErrInvalidSubnetID)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := storage.GetContext()
|
|
||||||
|
|
||||||
stKey := append([]byte{ownerPrefix}, subnetID...)
|
|
||||||
if storage.Get(ctx, stKey) == nil {
|
|
||||||
panic(ErrNotExist)
|
|
||||||
}
|
|
||||||
|
|
||||||
stKey[0] = userPrefix
|
|
||||||
prefixLen := len(stKey) + groupIDSize
|
|
||||||
|
|
||||||
iter := storage.Find(ctx, stKey, storage.KeysOnly)
|
|
||||||
for iterator.Next(iter) {
|
|
||||||
key := iterator.Value(iter).([]byte)
|
|
||||||
if common.BytesEqual(user, key[prefixLen:]) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version returns the version of the contract.
|
|
||||||
func Version() int {
|
|
||||||
return common.Version
|
|
||||||
}
|
|
||||||
|
|
||||||
func keyInList(ctx storage.Context, searchedKey interop.PublicKey, prefix []byte) bool {
|
|
||||||
return storage.Get(ctx, append(prefix, searchedKey...)) != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func putKeyInList(ctx storage.Context, keyToPut interop.PublicKey, prefix []byte) {
|
|
||||||
storage.Put(ctx, append(prefix, keyToPut...), []byte{1})
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteKeyFromList(ctx storage.Context, keyToDelete interop.PublicKey, prefix []byte) {
|
|
||||||
storage.Delete(ctx, append(prefix, keyToDelete...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteByPrefix(ctx storage.Context, prefix []byte) {
|
|
||||||
iter := storage.Find(ctx, prefix, storage.KeysOnly)
|
|
||||||
for iterator.Next(iter) {
|
|
||||||
k := iterator.Value(iter).([]byte)
|
|
||||||
storage.Delete(ctx, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func calledByOwnerOrAdmin(ctx storage.Context, owner []byte, adminPrefix []byte) bool {
|
|
||||||
if runtime.CheckWitness(owner) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
iter := storage.Find(ctx, adminPrefix, storage.KeysOnly|storage.RemovePrefix)
|
|
||||||
for iterator.Next(iter) {
|
|
||||||
key := iterator.Value(iter).([]byte)
|
|
||||||
if runtime.CheckWitness(key) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -1,330 +0,0 @@
|
||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"path"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/subnet"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
|
||||||
"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/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
const subnetPath = "../subnet"
|
|
||||||
|
|
||||||
func deploySubnetContract(t *testing.T, e *neotest.Executor) util.Uint160 {
|
|
||||||
c := neotest.CompileFile(t, e.CommitteeHash, subnetPath, path.Join(subnetPath, "config.yml"))
|
|
||||||
args := []interface{}{false}
|
|
||||||
e.DeployContract(t, c, args)
|
|
||||||
return c.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSubnetInvoker(t *testing.T) *neotest.ContractInvoker {
|
|
||||||
e := newExecutor(t)
|
|
||||||
h := deploySubnetContract(t, e)
|
|
||||||
return e.CommitteeInvoker(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubnet_Version(t *testing.T) {
|
|
||||||
e := newSubnetInvoker(t)
|
|
||||||
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, 5)
|
|
||||||
binary.LittleEndian.PutUint32(id, 123)
|
|
||||||
info := randomBytes(10)
|
|
||||||
|
|
||||||
e.InvokeFail(t, common.ErrWitnessFailed, "put", id, pub, info)
|
|
||||||
|
|
||||||
cAcc := e.WithSigners(acc)
|
|
||||||
cAcc.InvokeFail(t, common.ErrAlphabetWitnessFailed, "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)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubnet_Delete(t *testing.T) {
|
|
||||||
e := newSubnetInvoker(t)
|
|
||||||
|
|
||||||
id, owner := createSubnet(t, e)
|
|
||||||
|
|
||||||
e.InvokeFail(t, common.ErrWitnessFailed, "delete", id)
|
|
||||||
|
|
||||||
cAcc := e.WithSigners(owner)
|
|
||||||
cAcc.InvokeFail(t, subnet.ErrInvalidSubnetID, "delete", []byte{1, 1, 1, 1})
|
|
||||||
cAcc.Invoke(t, stackitem.Null{}, "delete", []byte{1, 1, 1, 1, 1})
|
|
||||||
cAcc.Invoke(t, stackitem.Null{}, "delete", id)
|
|
||||||
cAcc.InvokeFail(t, subnet.ErrNotExist, "get", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubnet_AddNodeAdmin(t *testing.T) {
|
|
||||||
e := newSubnetInvoker(t)
|
|
||||||
|
|
||||||
id, owner := createSubnet(t, e)
|
|
||||||
|
|
||||||
adm := e.NewAccount(t)
|
|
||||||
admPub, ok := vm.ParseSignatureContract(adm.Script())
|
|
||||||
require.True(t, ok)
|
|
||||||
|
|
||||||
const method = "addNodeAdmin"
|
|
||||||
|
|
||||||
e.InvokeFail(t, subnet.ErrInvalidSubnetID, method, []byte{0, 0, 0, 0}, admPub)
|
|
||||||
e.InvokeFail(t, subnet.ErrInvalidAdmin, method, id, admPub[1:])
|
|
||||||
e.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, admPub)
|
|
||||||
|
|
||||||
cAdm := e.WithSigners(adm)
|
|
||||||
cAdm.InvokeFail(t, common.ErrOwnerWitnessFailed, method, id, admPub)
|
|
||||||
|
|
||||||
cOwner := e.WithSigners(owner)
|
|
||||||
cOwner.Invoke(t, stackitem.Null{}, method, id, admPub)
|
|
||||||
|
|
||||||
cOwner.Invoke(t, stackitem.Null{}, method, id, admPub)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubnet_RemoveNodeAdmin(t *testing.T) {
|
|
||||||
e := newSubnetInvoker(t)
|
|
||||||
|
|
||||||
id, owner := createSubnet(t, e)
|
|
||||||
|
|
||||||
adm := e.NewAccount(t)
|
|
||||||
admPub, ok := vm.ParseSignatureContract(adm.Script())
|
|
||||||
require.True(t, ok)
|
|
||||||
|
|
||||||
const method = "removeNodeAdmin"
|
|
||||||
|
|
||||||
e.InvokeFail(t, subnet.ErrInvalidSubnetID, method, []byte{0, 0, 0, 0}, admPub)
|
|
||||||
e.InvokeFail(t, subnet.ErrInvalidAdmin, method, id, admPub[1:])
|
|
||||||
e.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, admPub)
|
|
||||||
|
|
||||||
cAdm := e.WithSigners(adm)
|
|
||||||
cAdm.InvokeFail(t, common.ErrOwnerWitnessFailed, method, id, admPub)
|
|
||||||
|
|
||||||
cOwner := e.WithSigners(owner)
|
|
||||||
|
|
||||||
cOwner.Invoke(t, stackitem.Null{}, method, id, admPub)
|
|
||||||
cOwner.Invoke(t, stackitem.Null{}, "addNodeAdmin", id, admPub)
|
|
||||||
cOwner.Invoke(t, stackitem.Null{}, method, id, admPub)
|
|
||||||
cOwner.Invoke(t, stackitem.Null{}, method, id, admPub)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubnet_AddNode(t *testing.T) {
|
|
||||||
e := newSubnetInvoker(t)
|
|
||||||
|
|
||||||
id, owner := createSubnet(t, e)
|
|
||||||
|
|
||||||
node := e.NewAccount(t)
|
|
||||||
nodePub, ok := vm.ParseSignatureContract(node.Script())
|
|
||||||
require.True(t, ok)
|
|
||||||
|
|
||||||
const method = "addNode"
|
|
||||||
|
|
||||||
cOwn := e.WithSigners(owner)
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrInvalidSubnetID, method, []byte{0, 0, 0, 0}, nodePub)
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrInvalidNode, method, id, nodePub[1:])
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, nodePub)
|
|
||||||
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, method, id, nodePub)
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, method, id, nodePub)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubnet_RemoveNode(t *testing.T) {
|
|
||||||
e := newSubnetInvoker(t)
|
|
||||||
|
|
||||||
id, owner := createSubnet(t, e)
|
|
||||||
|
|
||||||
node := e.NewAccount(t)
|
|
||||||
nodePub, ok := vm.ParseSignatureContract(node.Script())
|
|
||||||
require.True(t, ok)
|
|
||||||
|
|
||||||
adm := e.NewAccount(t)
|
|
||||||
admPub, ok := vm.ParseSignatureContract(adm.Script())
|
|
||||||
require.True(t, ok)
|
|
||||||
|
|
||||||
const method = "removeNode"
|
|
||||||
|
|
||||||
cOwn := e.WithSigners(owner)
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrInvalidSubnetID, method, []byte{0, 0, 0, 0}, nodePub)
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrInvalidNode, method, id, nodePub[1:])
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, nodePub)
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, method, id, nodePub)
|
|
||||||
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, "addNode", id, nodePub)
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, method, id, nodePub)
|
|
||||||
|
|
||||||
cAdm := cOwn.WithSigners(adm)
|
|
||||||
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, "addNodeAdmin", id, admPub)
|
|
||||||
cAdm.Invoke(t, stackitem.Null{}, method, id, nodePub)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubnet_NodeAllowed(t *testing.T) {
|
|
||||||
e := newSubnetInvoker(t)
|
|
||||||
|
|
||||||
id, owner := createSubnet(t, e)
|
|
||||||
|
|
||||||
node := e.NewAccount(t)
|
|
||||||
nodePub, ok := vm.ParseSignatureContract(node.Script())
|
|
||||||
require.True(t, ok)
|
|
||||||
|
|
||||||
const method = "nodeAllowed"
|
|
||||||
|
|
||||||
cOwn := e.WithSigners(owner)
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrInvalidSubnetID, method, []byte{0, 0, 0, 0}, nodePub)
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrInvalidNode, method, id, nodePub[1:])
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, nodePub)
|
|
||||||
cOwn.Invoke(t, stackitem.NewBool(false), method, id, nodePub)
|
|
||||||
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, "addNode", id, nodePub)
|
|
||||||
cOwn.Invoke(t, stackitem.NewBool(true), method, id, nodePub)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubnet_AddClientAdmin(t *testing.T) {
|
|
||||||
e := newSubnetInvoker(t)
|
|
||||||
|
|
||||||
id, owner := createSubnet(t, e)
|
|
||||||
|
|
||||||
adm := e.NewAccount(t)
|
|
||||||
admPub, ok := vm.ParseSignatureContract(adm.Script())
|
|
||||||
require.True(t, ok)
|
|
||||||
|
|
||||||
const method = "addClientAdmin"
|
|
||||||
|
|
||||||
groupId := randomBytes(5)
|
|
||||||
|
|
||||||
cOwn := e.WithSigners(owner)
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrInvalidSubnetID, method, []byte{0, 0, 0, 0}, groupId, admPub)
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrInvalidAdmin, method, id, groupId, admPub[1:])
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, groupId, admPub)
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, method, id, groupId, admPub)
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, method, id, groupId, admPub)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubnet_RemoveClientAdmin(t *testing.T) {
|
|
||||||
e := newSubnetInvoker(t)
|
|
||||||
|
|
||||||
id, owner := createSubnet(t, e)
|
|
||||||
|
|
||||||
adm := e.NewAccount(t)
|
|
||||||
admPub, ok := vm.ParseSignatureContract(adm.Script())
|
|
||||||
require.True(t, ok)
|
|
||||||
|
|
||||||
const method = "removeClientAdmin"
|
|
||||||
|
|
||||||
groupId := randomBytes(5)
|
|
||||||
|
|
||||||
cOwn := e.WithSigners(owner)
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrInvalidSubnetID, method, []byte{0, 0, 0, 0}, groupId, admPub)
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrInvalidAdmin, method, id, groupId, admPub[1:])
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, groupId, admPub)
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, method, id, groupId, admPub)
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, "addClientAdmin", id, groupId, admPub)
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, method, id, groupId, admPub)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubnet_AddUser(t *testing.T) {
|
|
||||||
e := newSubnetInvoker(t)
|
|
||||||
|
|
||||||
id, owner := createSubnet(t, e)
|
|
||||||
|
|
||||||
adm := e.NewAccount(t)
|
|
||||||
admPub, ok := vm.ParseSignatureContract(adm.Script())
|
|
||||||
require.True(t, ok)
|
|
||||||
|
|
||||||
user := randomBytes(27)
|
|
||||||
|
|
||||||
groupId := randomBytes(5)
|
|
||||||
|
|
||||||
const method = "addUser"
|
|
||||||
|
|
||||||
cOwn := e.WithSigners(owner)
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrInvalidSubnetID, method, []byte{0, 0, 0, 0}, groupId, user)
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, groupId, user)
|
|
||||||
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, "addClientAdmin", id, groupId, admPub)
|
|
||||||
|
|
||||||
cAdm := e.WithSigners(adm)
|
|
||||||
cAdm.Invoke(t, stackitem.Null{}, method, id, groupId, user)
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, method, id, groupId, user)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubnet_RemoveUser(t *testing.T) {
|
|
||||||
e := newSubnetInvoker(t)
|
|
||||||
|
|
||||||
id, owner := createSubnet(t, e)
|
|
||||||
|
|
||||||
groupId := randomBytes(5)
|
|
||||||
user := randomBytes(27)
|
|
||||||
|
|
||||||
adm := e.NewAccount(t)
|
|
||||||
admPub, ok := vm.ParseSignatureContract(adm.Script())
|
|
||||||
require.True(t, ok)
|
|
||||||
|
|
||||||
const method = "removeUser"
|
|
||||||
|
|
||||||
cOwn := e.WithSigners(owner)
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrInvalidSubnetID, method, []byte{0, 0, 0, 0}, groupId, user)
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, groupId, user)
|
|
||||||
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, method, id, groupId, user)
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, "addUser", id, groupId, user)
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, method, id, groupId, user)
|
|
||||||
|
|
||||||
cAdm := cOwn.WithSigners(adm)
|
|
||||||
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, "addClientAdmin", id, groupId, admPub)
|
|
||||||
cAdm.Invoke(t, stackitem.Null{}, method, id, groupId, user)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubnet_UserAllowed(t *testing.T) {
|
|
||||||
e := newSubnetInvoker(t)
|
|
||||||
|
|
||||||
id, owner := createSubnet(t, e)
|
|
||||||
|
|
||||||
groupId := randomBytes(5)
|
|
||||||
|
|
||||||
user := randomBytes(27)
|
|
||||||
|
|
||||||
const method = "userAllowed"
|
|
||||||
|
|
||||||
cOwn := e.WithSigners(owner)
|
|
||||||
cOwn.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, user)
|
|
||||||
|
|
||||||
cOwn.Invoke(t, stackitem.NewBool(false), method, id, user)
|
|
||||||
cOwn.Invoke(t, stackitem.Null{}, "addUser", id, groupId, user)
|
|
||||||
cOwn.Invoke(t, stackitem.NewBool(true), method, id, user)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createSubnet(t *testing.T, e *neotest.ContractInvoker) (id []byte, owner neotest.Signer) {
|
|
||||||
var (
|
|
||||||
ok bool
|
|
||||||
pub []byte
|
|
||||||
)
|
|
||||||
|
|
||||||
owner = e.NewAccount(t)
|
|
||||||
pub, ok = vm.ParseSignatureContract(owner.Script())
|
|
||||||
require.True(t, ok)
|
|
||||||
|
|
||||||
id = make([]byte, 5)
|
|
||||||
binary.LittleEndian.PutUint32(id, 123)
|
|
||||||
info := randomBytes(10)
|
|
||||||
|
|
||||||
cBoth := e.WithSigners(e.Committee, owner)
|
|
||||||
cBoth.Invoke(t, stackitem.Null{}, "put", id, pub, info)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
Loading…
Reference in a new issue