From b004b05b17b4c3ca47f7ae8ad517c48cd95e33c6 Mon Sep 17 00:00:00 2001 From: Alexander Chuprov Date: Fri, 11 Oct 2024 01:24:38 +0300 Subject: [PATCH] [#115] nns: Make 'DeleteDomain' faster Signed-off-by: Alexander Chuprov --- nns/doc.go | 23 +++++++++-------- nns/nns_contract.go | 63 ++++++++++++++++++++++++++++++++++++++++++--- tests/nns_test.go | 29 +++++++++++++++++++-- 3 files changed, 99 insertions(+), 16 deletions(-) diff --git a/nns/doc.go b/nns/doc.go index 787cca4..6c34f06 100644 --- a/nns/doc.go +++ b/nns/doc.go @@ -2,17 +2,18 @@ # Contract storage scheme - | Key | Value | Description | - |--------------------------------------|------------|-----------------------------------| - | 0x0 | int | total supply of minted domains | - | 0x1 + accountAddr | int | account's balance | - | 0x2 + accountAddr + tokenKey | ByteArray | token ID | - | 0x10 | int | price for domain registration | - | 0x20 | int | set of roots | - | 0x21 + tokenKey | ByteArray | serialized NameState struct | - | 0x22 + tokenKey + Hash160(tokenName) | Hash160 | container contract hash | - | 0x23 + tokenKey + Hash160(tokenName) | string | global domain flag | - + | Key | Value | Description | + |--------------------------------------|------------|------------------------------------------------------| + | 0x0 | int | total supply of minted domains | + | 0x1 + accountAddr | int | account's balance | + | 0x2 + accountAddr + tokenKey | ByteArray | token ID | + | 0x10 | int | price for domain registration | + | 0x20 | int | set of roots | + | 0x21 + tokenKey | ByteArray | serialized NameState struct | + | 0x22 + tokenKey + Hash160(tokenName) | Hash160 | container contract hash | + | 0x23 + tokenKey + Hash160(tokenName) | string | global domain flag | + | 0x24 + tokenKey | int | number of subdomains for the TLD domain | + | 0x25 + tokenKey | bool | Flag indicating the automatically created TLD domain | */ package nns diff --git a/nns/nns_contract.go b/nns/nns_contract.go index 4131509..a296732 100644 --- a/nns/nns_contract.go +++ b/nns/nns_contract.go @@ -44,6 +44,11 @@ const ( //prefixGlobalDomain contains a flag indicating that this domain was created using GlobalDomain. //This is necessary to distinguish it from regular CNAME records. prefixGlobalDomain byte = 0x23 + // prefixCountSubDomains contains information about the number of subdomains for the TLD domain. + // If it is nil, it will definitely be calculated on the first removal + prefixCountSubDomains byte = 0x24 + // prefixAutoCreated contains a flag indicating whether the TLD domain was created automatically + prefixAutoCreated = 0x25 ) // Values constraints. @@ -335,7 +340,8 @@ func Register(name string, owner interop.Hash160, email string, refresh, retry, func register(ctx storage.Context, name string, owner interop.Hash160, email string, refresh, retry, expire, ttl int) bool { fragments := splitAndCheck(name) l := len(fragments) - tldKey := append([]byte{prefixRoot}, []byte(fragments[l-1])...) + rootZone := []byte(fragments[l-1]) + tldKey := append([]byte{prefixRoot}, rootZone...) tldBytes := storage.Get(ctx, tldKey) if l == 1 { @@ -348,6 +354,8 @@ func register(ctx storage.Context, name string, owner interop.Hash160, email str parent := checkParent(ctx, fragments) if parent == "" { parent = fragments[len(fragments)-1] + + storage.Put(ctx, append([]byte{prefixAutoCreated}, rootZone...), true) register(ctx, parent, owner, email, refresh, retry, expire, ttl) } @@ -404,6 +412,18 @@ func register(ctx storage.Context, name string, owner interop.Hash160, email str updateBalance(ctx, []byte(name), owner, +1) postTransfer(oldOwner, owner, []byte(name), nil) runtime.Notify("RegisterDomain", name) + + countSubDomain := 0 + delInfoRaw := storage.Get(ctx, append([]byte{prefixCountSubDomains}, rootZone...)) + if delInfoRaw != nil { + countSubDomain = common.FromFixedWidth64(delInfoRaw.([]byte)) + } + + if delInfoRaw != nil || l == 1 { + countSubDomain = countSubDomain + 1 + storage.Put(ctx, append([]byte{prefixCountSubDomains}, rootZone...), common.ToFixedWidth64(countSubDomain)) + } + return true } @@ -573,14 +593,37 @@ func DeleteDomain(name string) { deleteDomain(ctx, name) } -func deleteDomain(ctx storage.Context, name string) { +func restoreCountSubDomains(ctx storage.Context, name string) int { + countSubDomains := 0 it := Tokens() for iterator.Next(it) { domain := iterator.Value(it) if std.MemorySearch([]byte(domain.(string)), []byte(name)) > 0 { - panic("can't delete a domain that has subdomains") + countSubDomains = countSubDomains + 1 } } + return countSubDomains +} + +func deleteDomain(ctx storage.Context, name string) { + fragments := splitAndCheck(name) + parent := []byte(fragments[len(fragments)-1]) + countSubDomainsKey := append([]byte{prefixCountSubDomains}, parent...) + autoCreatedPrefix := append([]byte{prefixAutoCreated}, parent...) + + countSubDomain := 0 + + countSubDomainRaw := storage.Get(ctx, countSubDomainsKey) + if countSubDomainRaw != nil { + countSubDomain = common.FromFixedWidth64(countSubDomainRaw.([]byte)) + } else { + countSubDomain = restoreCountSubDomains(ctx, fragments[len(fragments)-1]) + storage.Put(ctx, countSubDomainsKey, std.Serialize(common.ToFixedWidth64(countSubDomain))) + } + + if countSubDomain > 1 && len(fragments) == 1 { + panic("can't delete TLD domain that has subdomains") + } nsKey := append([]byte{prefixName}, getTokenKey([]byte(name))...) nsRaw := storage.Get(ctx, nsKey) @@ -598,12 +641,26 @@ func deleteDomain(ctx storage.Context, name string) { deleteDomain(ctx, globalDomain) } + countSubDomain = countSubDomain - 1 + storage.Put(ctx, countSubDomainsKey, common.ToFixedWidth64(countSubDomain)) + deleteRecords(ctx, name, CNAME) deleteRecords(ctx, name, TXT) deleteRecords(ctx, name, A) deleteRecords(ctx, name, AAAA) storage.Delete(ctx, nsKey) storage.Delete(ctx, append([]byte{prefixRoot}, []byte(name)...)) + + isAutoCrated := storage.Get(ctx, autoCreatedPrefix) + if countSubDomain == 1 && isAutoCrated != nil { + deleteDomain(ctx, fragments[len(fragments)-1]) + } + + if len(fragments) == 1 { + storage.Delete(ctx, countSubDomainsKey) + storage.Delete(ctx, autoCreatedPrefix) + } + runtime.Notify("DeleteDomain", name) } diff --git a/tests/nns_test.go b/tests/nns_test.go index e3e6072..49ea4f7 100644 --- a/tests/nns_test.go +++ b/tests/nns_test.go @@ -100,6 +100,18 @@ func TestNNSRegister(t *testing.T) { "com", accTop.ScriptHash(), "myemail@frostfs.info", refresh, retry, expire, ttl) + c1.Invoke(t, true, "register", + "aa.bb.zz", accTop.ScriptHash(), + "myemail@frostfs.info", refresh, retry, expire, ttl) + + c1.InvokeFail(t, "TLD already exists", "register", + "zz", accTop.ScriptHash(), + "myemail@frostfs.info", refresh, retry, expire, ttl) + + c1.Invoke(t, true, "register", + "xx.bb.zz", accTop.ScriptHash(), + "myemail@frostfs.info", refresh, retry, expire, ttl) + acc := c.NewAccount(t) c2 := c.WithSigners(c.Committee, acc) c2.InvokeFail(t, "not witnessed by admin", "register", @@ -251,10 +263,23 @@ func TestDeleteDomain(t *testing.T) { "myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL) c1.InvokeFail(t, "domain not found", "deleteDomain", "ru") - c1.InvokeFail(t, "can't delete a domain that has subdomains", "deleteDomain", "testdomain.com") - c1.Invoke(t, stackitem.Null{}, "deleteDomain", "domik.testdomain.com") + c1.InvokeFail(t, "can't delete TLD domain that has subdomains", "deleteDomain", "com") c1.Invoke(t, stackitem.Null{}, "deleteDomain", "testdomain.com") + c1.Invoke(t, true, "register", + "aa.bb.zz", acc1.ScriptHash(), + "myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL) + + c1.InvokeFail(t, "TLD already exists", "register", + "zz", acc1.ScriptHash(), + "myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL) + + c1.Invoke(t, stackitem.Null{}, "deleteDomain", "aa.bb.zz") + + c1.Invoke(t, true, "register", + "zz", acc1.ScriptHash(), + "myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL) + c1.Invoke(t, true, "register", "cn", acc1.ScriptHash(), "myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)