From 9a05e213eb8a3dac5d2a6be3edf6a0ae9b0cfbc3 Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Tue, 23 Nov 2021 13:56:56 +0300 Subject: [PATCH] [#174] subnet: Add `AddUser` method Signed-off-by: Pavel Karpy --- subnet/subnet_contract.go | 60 +++++++++++++++++++++++++++++++++++++-- tests/subnet_test.go | 25 ++++++++++++++++ 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/subnet/subnet_contract.go b/subnet/subnet_contract.go index 6cd8ea2..969569a 100644 --- a/subnet/subnet_contract.go +++ b/subnet/subnet_contract.go @@ -21,6 +21,8 @@ const ( ErrAlreadyExists = "subnet id already exists" // ErrSubNotExist is thrown when id doesn't exist. ErrSubNotExist = "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" // ErrNodeAdmNotExist is thrown when node admin is not found. @@ -34,15 +36,19 @@ const ( errCheckWitnessFailed = "owner witness check failed" - ownerPrefix = 'o' nodeAdminPrefix = 'a' + infoPrefix = 'i' clientAdminPrefix = 'm' nodePrefix = 'n' - user = 'u' - infoPrefix = 'i' + ownerPrefix = 'o' + userPrefix = 'u' notaryDisabledKey = 'z' ) +const ( + userIDSize = 27 +) + // _deploy function sets up initial list of inner ring public keys. func _deploy(data interface{}, isUpdate bool) { if isUpdate { @@ -391,6 +397,54 @@ func RemoveClientAdmin(subnetID []byte, groupID []byte, adminPublicKey interop.P 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) { + if len(userID) != userIDSize { + panic("addUser: " + ErrInvalidUser) + } + + ctx := storage.GetContext() + + stKey := append([]byte{ownerPrefix}, subnetID...) + + rawOwner := storage.Get(ctx, stKey) + if rawOwner == nil { + panic("addUser: " + ErrSubNotExist) + } + + stKey = append(stKey, groupID...) + prefixLen := len(stKey) + + owner := rawOwner.([]byte) + if !runtime.CheckWitness(owner) { + var hasAccess bool + + stKey[0] = clientAdminPrefix + + iter := storage.Find(ctx, stKey, storage.KeysOnly) + for iterator.Next(iter) { + key := iterator.Value(iter).([]byte) + if runtime.CheckWitness(key[prefixLen:]) { + hasAccess = true + break + } + } + + if !hasAccess { + panic("addUser: " + ErrAccessDenied) + } + } + + stKey[0] = userPrefix + + if keyInList(ctx, userID, stKey) { + panic("addUser: user has already been added") + } + + putKeyInList(ctx, userID, stKey) +} + // Version returns version of the contract. func Version() int { return common.Version diff --git a/tests/subnet_test.go b/tests/subnet_test.go index f635ee4..b259cec 100644 --- a/tests/subnet_test.go +++ b/tests/subnet_test.go @@ -233,6 +233,31 @@ func TestSubnet_RemoveClientAdmin(t *testing.T) { 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(8) + + const method = "addUser" + + cOwn := e.WithSigners(owner) + cOwn.InvokeFail(t, method+errSeparator+subnet.ErrSubNotExist, method, []byte{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.InvokeFail(t, method+errSeparator+"user has already been added", method, id, groupId, user) +} + func createSubnet(t *testing.T, e *neotest.ContractInvoker) (id []byte, owner neotest.Signer) { var ( ok bool