From 18b5e56727a483b02d763f819b1abd8d7a170699 Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Mon, 22 Nov 2021 19:55:07 +0300 Subject: [PATCH] [#174] subnet: Add `AddNode` method Signed-off-by: Pavel Karpy --- subnet/subnet_contract.go | 72 +++++++++++++++++++++++++++++++++++---- tests/subnet_test.go | 18 ++++++++++ 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/subnet/subnet_contract.go b/subnet/subnet_contract.go index 091756b..663032d 100644 --- a/subnet/subnet_contract.go +++ b/subnet/subnet_contract.go @@ -24,6 +24,8 @@ const ( ErrSubNotExist = "subnet id doesn't exist" // ErrNodeAdmNotExist is thrown when node admin is not found. ErrNodeAdmNotExist = "node admin not found" + // ErrAccessDenied is thrown when operation is denied for caller. + ErrAccessDenied = "access denied" errCheckWitnessFailed = "owner witness check failed" @@ -166,14 +168,9 @@ func AddNodeAdmin(subnetID []byte, adminKey interop.PublicKey) { } stKey[0] = nodeAdminPrefix - prefixLen := len(stKey) - iter := storage.Find(ctx, stKey, storage.KeysOnly) - for iterator.Next(iter) { - key := iterator.Value(iter).([]byte) - if util.Equals(string(key[prefixLen:]), string(adminKey)) { - panic("addNodeAdmin: node admin has already been added") - } + if keyInList(ctx, adminKey, stKey) { + panic("addNodeAdmin: node admin has already been added") } storage.Put(ctx, append(stKey, adminKey...), []byte{1}) @@ -210,7 +207,68 @@ func RemoveNodeAdmin(subnetID []byte, adminKey interop.PublicKey) { storage.Delete(ctx, stNodeAdmKey) } +// AddNode adds node to the specified subnetwork. +// Must be called by subnet's owner or node administrator +// only. +func AddNode(subnetID []byte, node interop.PublicKey) { + if len(node) != interop.PublicKeyCompressedLen { + panic("addNode: " + ErrInvalidAdmin) + } + + ctx := storage.GetContext() + + stKey := append([]byte{ownerPrefix}, subnetID...) + prefixLen := len(stKey) + + rawOwner := storage.Get(ctx, stKey) + if rawOwner == nil { + panic("addNode: " + ErrSubNotExist) + } + + owner := rawOwner.([]byte) + if !runtime.CheckWitness(owner) { + var hasAccess bool + + stKey[0] = nodeAdminPrefix + + 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("addNode: " + ErrAccessDenied) + } + } + + stKey[0] = nodePrefix + + if keyInList(ctx, node, stKey) { + panic("addNode: node has already been added") + } + + storage.Put(ctx, append(stKey, node...), []byte{1}) +} + // Version returns version of the contract. func Version() int { return common.Version } + +func keyInList(ctx storage.Context, searchedKey interop.PublicKey, prefix []byte) bool { + prefixLen := len(prefix) + + iter := storage.Find(ctx, prefix, storage.KeysOnly) + for iterator.Next(iter) { + key := iterator.Value(iter).([]byte) + if util.Equals(string(key[prefixLen:]), string(searchedKey)) { + return true + } + } + + return false +} diff --git a/tests/subnet_test.go b/tests/subnet_test.go index d71361b..49abed7 100644 --- a/tests/subnet_test.go +++ b/tests/subnet_test.go @@ -124,6 +124,24 @@ func TestSubnet_RemoveNodeAdmin(t *testing.T) { cOwner.InvokeFail(t, method+errSeparator+subnet.ErrNodeAdmNotExist, 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, method+errSeparator+subnet.ErrInvalidAdmin, method, id, nodePub[1:]) + + cOwn.Invoke(t, stackitem.Null{}, method, id, nodePub) + cOwn.InvokeFail(t, method+errSeparator+"node has already been added", method, id, nodePub) +} + func createSubnet(t *testing.T, e *neotest.ContractInvoker) (id []byte, owner neotest.Signer) { var ( ok bool