[#115] nns: Allow register TLD from nested domains

Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
This commit is contained in:
Alexander Chuprov 2024-10-11 01:17:25 +03:00
parent 81853bd242
commit 4e9adc2034
2 changed files with 31 additions and 31 deletions

View file

@ -234,13 +234,10 @@ func IsAvailable(name string) bool {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
l := len(fragments) l := len(fragments)
if storage.Get(ctx, append([]byte{prefixRoot}, []byte(fragments[l-1])...)) == nil { if storage.Get(ctx, append([]byte{prefixRoot}, []byte(fragments[l-1])...)) == nil {
if l != 1 {
panic("TLD not found")
}
return true return true
} }
checkParentExists(ctx, fragments) checkParent(ctx, fragments)
checkAvailableGlobalDomain(ctx, name) checkAvailableGlobalDomain(ctx, name)
return storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(name))...)) == nil return storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(name))...)) == nil
} }
@ -277,7 +274,6 @@ func getGlobalDomain(ctx storage.Context, domain string) string {
// extractCnametgt returns the value of the Cnametgt TXT record. // extractCnametgt returns the value of the Cnametgt TXT record.
func extractCnametgt(ctx storage.Context, name, domain string) string { func extractCnametgt(ctx storage.Context, name, domain string) string {
fragments := splitAndCheck(domain) fragments := splitAndCheck(domain)
tokenID := []byte(tokenIDFromName(name)) tokenID := []byte(tokenIDFromName(name))
records := getRecordsByType(ctx, tokenID, name, TXT) records := getRecordsByType(ctx, tokenID, name, TXT)
@ -304,33 +300,29 @@ func extractCnametgt(ctx storage.Context, name, domain string) string {
return fragments[0] + "." + globalDomain return fragments[0] + "." + globalDomain
} }
// checkParentExists panics if any domain from fragments doesn't exist or is expired. // checkParent returns parent domain or empty string if domain not found.
func checkParentExists(ctx storage.Context, fragments []string) { func checkParent(ctx storage.Context, fragments []string) string {
if dom := parentExpired(ctx, fragments); dom != "" {
panic("domain does not exist or is expired: " + dom)
}
}
// parentExpired returns domain from fragments that doesn't exist or is expired.
// first denotes the deepest subdomain to check.
func parentExpired(ctx storage.Context, fragments []string) string {
now := int64(runtime.GetTime()) now := int64(runtime.GetTime())
last := len(fragments) - 1 last := len(fragments) - 1
name := fragments[last] name := fragments[last]
parent := ""
for i := last; i > 0; i-- { for i := last; i > 0; i-- {
if i != last { if i != last {
name = fragments[i] + "." + name name = fragments[i] + "." + name
} }
nsBytes := storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(name))...)) nsBytes := storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(name))...))
if nsBytes == nil { if nsBytes == nil {
return name continue
} }
ns := std.Deserialize(nsBytes.([]byte)).(NameState) ns := std.Deserialize(nsBytes.([]byte)).(NameState)
if now >= ns.Expiration { if now >= ns.Expiration {
return name panic("domain expired: " + name)
} }
parent = name
} }
return "" return parent
} }
// 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.
@ -353,13 +345,17 @@ func register(ctx storage.Context, name string, owner interop.Hash160, email str
} }
storage.Put(ctx, tldKey, 0) storage.Put(ctx, tldKey, 0)
} else { } else {
if tldBytes == nil { parent := checkParent(ctx, fragments)
panic("TLD not found") if parent == "" {
parent = fragments[len(fragments)-1]
register(ctx, parent, owner, email, refresh, retry, expire, ttl)
} }
checkParentExists(ctx, fragments)
parentKey := getTokenKey([]byte(name[len(fragments[0])+1:])) parentKey := getTokenKey([]byte(parent))
nsBytes := storage.Get(ctx, append([]byte{prefixName}, parentKey...)) nsBytes := storage.Get(ctx, append([]byte{prefixName}, parentKey...))
if nsBytes == nil {
panic("parent does not exist:" + parent)
}
ns := std.Deserialize(nsBytes.([]byte)).(NameState) ns := std.Deserialize(nsBytes.([]byte)).(NameState)
ns.checkAdmin() ns.checkAdmin()
@ -681,7 +677,7 @@ func getNameState(ctx storage.Context, tokenID []byte) NameState {
tokenKey := getTokenKey(tokenID) tokenKey := getTokenKey(tokenID)
ns := getNameStateWithKey(ctx, tokenKey) ns := getNameStateWithKey(ctx, tokenKey)
fragments := std.StringSplit(string(tokenID), ".") fragments := std.StringSplit(string(tokenID), ".")
checkParentExists(ctx, fragments) checkParent(ctx, fragments)
return ns return ns
} }

View file

@ -266,6 +266,18 @@ func TestDeleteDomain(t *testing.T) {
c2.Invoke(t, true, "register", c2.Invoke(t, true, "register",
"cn", acc2.ScriptHash(), "cn", acc2.ScriptHash(),
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL) "myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
c1.Invoke(t, true, "register",
"gu.bububu.bubu.bu", c.CommitteeHash,
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
c1.Invoke(t, true, "register",
"buu.gu.bububu.bubu.bu", c.CommitteeHash,
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
c1.Invoke(t, true, "register",
"bubu.bu", c.CommitteeHash,
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
} }
func TestGlobalDomain(t *testing.T) { func TestGlobalDomain(t *testing.T) {
@ -335,11 +347,6 @@ func TestNNSRegisterMulti(t *testing.T) {
cBoth.Invoke(t, true, "register", args...) cBoth.Invoke(t, true, "register", args...)
c1 := c.WithSigners(acc) c1 := c.WithSigners(acc)
t.Run("parent domain is missing", func(t *testing.T) {
msg := "domain does not exist or is expired: fs.neo.com"
args[0] = "testnet.fs.neo.com"
c1.InvokeFail(t, msg, "register", args...)
})
args[0] = "fs.neo.com" args[0] = "fs.neo.com"
c1.Invoke(t, true, "register", args...) c1.Invoke(t, true, "register", args...)
@ -499,7 +506,6 @@ func TestNNSIsAvailable(t *testing.T) {
c := newNNSInvoker(t, false) c := newNNSInvoker(t, false)
c.Invoke(t, true, "isAvailable", "com") c.Invoke(t, true, "isAvailable", "com")
c.InvokeFail(t, "TLD not found", "isAvailable", "domain.com")
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104) refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
c.Invoke(t, true, "register", c.Invoke(t, true, "register",
@ -512,7 +518,6 @@ func TestNNSIsAvailable(t *testing.T) {
acc := c.NewAccount(t) acc := c.NewAccount(t)
c1 := c.WithSigners(c.Committee, acc) c1 := c.WithSigners(c.Committee, acc)
c1.InvokeFail(t, "domain does not exist or is expired: domain.com", "isAvailable", "dom.domain.com")
c1.Invoke(t, true, "register", c1.Invoke(t, true, "register",
"domain.com", acc.ScriptHash(), "domain.com", acc.ScriptHash(),
"myemail@frostfs.info", refresh, retry, expire, ttl) "myemail@frostfs.info", refresh, retry, expire, ttl)
@ -524,7 +529,6 @@ func TestNNSIsAvailable(t *testing.T) {
c.Invoke(t, false, "isAvailable", "domain.com") c.Invoke(t, false, "isAvailable", "domain.com")
c.Invoke(t, true, "isAvailable", "dom.domain.com") c.Invoke(t, true, "isAvailable", "dom.domain.com")
c.InvokeFail(t, "domain does not exist or is expired: dom.domain.com", "isAvailable", "dom.dom.domain.com")
c1.Invoke(t, true, "register", c1.Invoke(t, true, "register",
"dom.domain.com", acc.ScriptHash(), "dom.domain.com", acc.ScriptHash(),