diff --git a/subnet/subnet_contract.go b/subnet/subnet_contract.go index 54a1be9..6cd8ea2 100644 --- a/subnet/subnet_contract.go +++ b/subnet/subnet_contract.go @@ -25,6 +25,8 @@ const ( ErrInvalidNode = "invalid node key" // ErrNodeAdmNotExist is thrown when node admin is not found. ErrNodeAdmNotExist = "node admin not found" + // ErrClientAdmNotExist is thrown when client admin is not found. + ErrClientAdmNotExist = "client admin not found" // ErrNodeNotExist is thrown when node is not found. ErrNodeNotExist = "node not found" // ErrAccessDenied is thrown when operation is denied for caller. @@ -357,6 +359,38 @@ func AddClientAdmin(subnetID []byte, groupID []byte, adminPublicKey interop.Publ putKeyInList(ctx, adminPublicKey, stKey) } +// RemoveClientAdmin removes client administrator from the +// specified group in the specified subnetwork. +// Must be called by owner only. +func RemoveClientAdmin(subnetID []byte, groupID []byte, adminPublicKey interop.PublicKey) { + if len(adminPublicKey) != interop.PublicKeyCompressedLen { + panic("removeClientAdmin: " + ErrInvalidAdmin) + } + + ctx := storage.GetContext() + + stKey := append([]byte{ownerPrefix}, subnetID...) + + rawOwner := storage.Get(ctx, stKey) + if rawOwner == nil { + panic("removeClientAdmin: " + ErrSubNotExist) + } + + owner := rawOwner.([]byte) + if !runtime.CheckWitness(owner) { + panic("removeClientAdmin: " + errCheckWitnessFailed) + } + + stKey[0] = clientAdminPrefix + stKey = append(stKey, groupID...) + + if !keyInList(ctx, adminPublicKey, stKey) { + panic("removeClientAdmin: " + ErrClientAdmNotExist) + } + + deleteKeyFromList(ctx, adminPublicKey, 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 7b0807c..f635ee4 100644 --- a/tests/subnet_test.go +++ b/tests/subnet_test.go @@ -212,6 +212,27 @@ func TestSubnet_AddClientAdmin(t *testing.T) { cOwn.InvokeFail(t, method+errSeparator+"client admin has already been added", 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(8) + + cOwn := e.WithSigners(owner) + cOwn.InvokeFail(t, method+errSeparator+subnet.ErrInvalidAdmin, method, id, groupId, admPub[1:]) + cOwn.InvokeFail(t, method+errSeparator+subnet.ErrSubNotExist, method, []byte{0, 0, 0, 0}, groupId, admPub) + cOwn.InvokeFail(t, method+errSeparator+subnet.ErrClientAdmNotExist, method, id, groupId, admPub) + cOwn.Invoke(t, stackitem.Null{}, "addClientAdmin", id, groupId, admPub) + cOwn.Invoke(t, stackitem.Null{}, method, id, groupId, admPub) +} + func createSubnet(t *testing.T, e *neotest.ContractInvoker) (id []byte, owner neotest.Signer) { var ( ok bool