[#115] nns: Allow register TLD from nested domains
All checks were successful
DCO action / DCO (pull_request) Successful in 33s
Tests / Tests (pull_request) Successful in 1m25s
Code generation / Generate wrappers (pull_request) Successful in 1m30s

Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
This commit is contained in:
Alexander Chuprov 2024-10-15 17:08:18 +03:00
parent a90d54c332
commit 3f4f8feca7
Signed by: achuprov
GPG key ID: 2D916FFD803B0EDD
2 changed files with 89 additions and 7 deletions

View file

@ -44,6 +44,11 @@ const (
//prefixGlobalDomain contains a flag indicating that this domain was created using GlobalDomain. //prefixGlobalDomain contains a flag indicating that this domain was created using GlobalDomain.
//This is necessary to distinguish it from regular CNAME records. //This is necessary to distinguish it from regular CNAME records.
prefixGlobalDomain byte = 0x23 prefixGlobalDomain byte = 0x23
// prefixCountSubDomains contains information about the number of domains in the zone.
// 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. // Values constraints.
@ -333,11 +338,12 @@ func Register(name string, owner interop.Hash160, email string, refresh, retry,
// Register registers a new domain with the specified owner and name if it's available. // Register registers a new domain with the specified owner and name if it's available.
func register(ctx storage.Context, name string, owner interop.Hash160, email string, refresh, retry, expire, ttl int) bool { func register(ctx storage.Context, name string, owner interop.Hash160, email string, refresh, retry, expire, ttl int) bool {
fragments := splitAndCheck(name) fragments := splitAndCheck(name)
l := len(fragments) countZone := len(fragments)
tldKey := append([]byte{prefixRoot}, []byte(fragments[l-1])...) rootZone := []byte(fragments[countZone-1])
tldKey := append([]byte{prefixRoot}, rootZone...)
tldBytes := storage.Get(ctx, tldKey) tldBytes := storage.Get(ctx, tldKey)
if l == 1 { if countZone == 1 {
checkCommittee() checkCommittee()
if tldBytes != nil { if tldBytes != nil {
panic("TLD already exists") panic("TLD already exists")
@ -347,6 +353,8 @@ func register(ctx storage.Context, name string, owner interop.Hash160, email str
parent := checkParent(ctx, fragments) parent := checkParent(ctx, fragments)
if parent == "" { if parent == "" {
parent = fragments[len(fragments)-1] parent = fragments[len(fragments)-1]
storage.Put(ctx, append([]byte{prefixAutoCreated}, rootZone...), true)
register(ctx, parent, owner, email, refresh, retry, expire, ttl) register(ctx, parent, owner, email, refresh, retry, expire, ttl)
} }
@ -398,6 +406,7 @@ func register(ctx storage.Context, name string, owner interop.Hash160, email str
} }
checkAvailableGlobalDomain(ctx, name) checkAvailableGlobalDomain(ctx, name)
updateSubdDomainCounter(ctx, rootZone, countZone)
putNameStateWithKey(ctx, tokenKey, ns) putNameStateWithKey(ctx, tokenKey, ns)
putSoaRecord(ctx, name, email, refresh, retry, expire, ttl) putSoaRecord(ctx, name, email, refresh, retry, expire, ttl)
updateBalance(ctx, []byte(name), owner, +1) updateBalance(ctx, []byte(name), owner, +1)
@ -572,14 +581,23 @@ func DeleteDomain(name string) {
deleteDomain(ctx, name) deleteDomain(ctx, name)
} }
func deleteDomain(ctx storage.Context, name string) { func countSubdomains(name string) int {
countSubDomains := 0
it := Tokens() it := Tokens()
for iterator.Next(it) { for iterator.Next(it) {
domain := iterator.Value(it) domain := iterator.Value(it)
if std.MemorySearch([]byte(domain.(string)), []byte(name)) > 0 { 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...)
nsKey := append([]byte{prefixName}, getTokenKey([]byte(name))...) nsKey := append([]byte{prefixName}, getTokenKey([]byte(name))...)
nsRaw := storage.Get(ctx, nsKey) nsRaw := storage.Get(ctx, nsKey)
@ -590,6 +608,21 @@ func deleteDomain(ctx storage.Context, name string) {
ns := std.Deserialize(nsRaw.([]byte)).(NameState) ns := std.Deserialize(nsRaw.([]byte)).(NameState)
ns.checkAdmin() ns.checkAdmin()
countSubDomain := 0
countSubDomainRaw := storage.Get(ctx, countSubDomainsKey)
if countSubDomainRaw != nil {
countSubDomain = common.FromFixedWidth64(countSubDomainRaw.([]byte))
} else {
countSubDomain = countSubdomains(fragments[len(fragments)-1])
}
if countSubDomain > 1 && len(fragments) == 1 {
panic("can't delete TLD domain that has subdomains")
}
countSubDomain = countSubDomain - 1
storage.Put(ctx, countSubDomainsKey, common.ToFixedWidth64(countSubDomain))
globalNSKey := append([]byte{prefixGlobalDomain}, getTokenKey([]byte(name))...) globalNSKey := append([]byte{prefixGlobalDomain}, getTokenKey([]byte(name))...)
globalDomainRaw := storage.Get(ctx, globalNSKey) globalDomainRaw := storage.Get(ctx, globalNSKey)
globalDomain := globalDomainRaw.(string) globalDomain := globalDomainRaw.(string)
@ -603,6 +636,17 @@ func deleteDomain(ctx storage.Context, name string) {
deleteRecords(ctx, name, AAAA) deleteRecords(ctx, name, AAAA)
storage.Delete(ctx, nsKey) storage.Delete(ctx, nsKey)
storage.Delete(ctx, append([]byte{prefixRoot}, []byte(name)...)) storage.Delete(ctx, append([]byte{prefixRoot}, []byte(name)...))
isAutoCreated := storage.Get(ctx, autoCreatedPrefix)
if countSubDomain == 1 && isAutoCreated != nil && isAutoCreated.(bool) {
deleteDomain(ctx, fragments[len(fragments)-1])
}
if len(fragments) == 1 {
storage.Delete(ctx, countSubDomainsKey)
storage.Delete(ctx, autoCreatedPrefix)
}
runtime.Notify("DeleteDomain", name) runtime.Notify("DeleteDomain", name)
} }
@ -1122,3 +1166,16 @@ func getAllRecords(ctx storage.Context, name string) iterator.Iterator {
recordsKey := getRecordsKey(tokenID, name) recordsKey := getRecordsKey(tokenID, name)
return storage.Find(ctx, recordsKey, storage.ValuesOnly|storage.DeserializeValues) return storage.Find(ctx, recordsKey, storage.ValuesOnly|storage.DeserializeValues)
} }
func updateSubdDomainCounter(ctx storage.Context, rootZone []byte, countZone int) {
countSubDomain := 0
delInfoRaw := storage.Get(ctx, append([]byte{prefixCountSubDomains}, rootZone...))
if delInfoRaw != nil {
countSubDomain = common.FromFixedWidth64(delInfoRaw.([]byte))
}
if delInfoRaw != nil || countZone == 1 {
countSubDomain = countSubDomain + 1
storage.Put(ctx, append([]byte{prefixCountSubDomains}, rootZone...), common.ToFixedWidth64(countSubDomain))
}
}

View file

@ -100,6 +100,18 @@ func TestNNSRegister(t *testing.T) {
"com", accTop.ScriptHash(), "com", accTop.ScriptHash(),
"myemail@frostfs.info", refresh, retry, expire, ttl) "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) acc := c.NewAccount(t)
c2 := c.WithSigners(c.Committee, acc) c2 := c.WithSigners(c.Committee, acc)
c2.InvokeFail(t, "not witnessed by admin", "register", c2.InvokeFail(t, "not witnessed by admin", "register",
@ -251,10 +263,23 @@ func TestDeleteDomain(t *testing.T) {
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL) "myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
c1.InvokeFail(t, "domain not found", "deleteDomain", "ru") c1.InvokeFail(t, "domain not found", "deleteDomain", "ru")
c1.InvokeFail(t, "can't delete a domain that has subdomains", "deleteDomain", "testdomain.com") c1.InvokeFail(t, "can't delete TLD domain that has subdomains", "deleteDomain", "com")
c1.Invoke(t, stackitem.Null{}, "deleteDomain", "domik.testdomain.com")
c1.Invoke(t, stackitem.Null{}, "deleteDomain", "testdomain.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", c1.Invoke(t, true, "register",
"cn", acc1.ScriptHash(), "cn", acc1.ScriptHash(),
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL) "myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)