From 4dcb575caa29aff5b33d3c2e294fdb32e3bafb4c Mon Sep 17 00:00:00 2001 From: Ekaterina Lebedeva Date: Tue, 12 Dec 2023 20:22:33 +0300 Subject: [PATCH] [#56] Add multi-level domain name support Signed-off-by: Ekaterina Lebedeva --- nns/nns_contract.go | 26 +++++++++++++------------- tests/nns_test.go | 9 +++++++++ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/nns/nns_contract.go b/nns/nns_contract.go index d187584..202a03d 100644 --- a/nns/nns_contract.go +++ b/nns/nns_contract.go @@ -219,7 +219,7 @@ func GetPrice() int { // IsAvailable checks whether the provided domain name is available. func IsAvailable(name string) bool { - fragments := splitAndCheck(name, false) + fragments := splitAndCheck(name) if fragments == nil { panic("invalid domain name format") } @@ -231,16 +231,19 @@ func IsAvailable(name string) bool { } return true } - return parentExpired(ctx, 0, fragments) + if parentExpired(ctx, fragments) { + panic("parent does not exist or is expired") + } + return storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(name))...)) == nil } // parentExpired returns true if any domain from fragments doesn't exist or is expired. // first denotes the deepest subdomain to check. -func parentExpired(ctx storage.Context, first int, fragments []string) bool { +func parentExpired(ctx storage.Context, fragments []string) bool { now := int64(runtime.GetTime()) last := len(fragments) - 1 name := fragments[last] - for i := last; i >= first; i-- { + for i := last; i > 0; i-- { if i != last { name = fragments[i] + "." + name } @@ -258,7 +261,7 @@ func parentExpired(ctx storage.Context, first int, fragments []string) bool { // Register registers a new domain with the specified owner and name if it's available. func Register(name string, owner interop.Hash160, email string, refresh, retry, expire, ttl int) bool { - fragments := splitAndCheck(name, true) + fragments := splitAndCheck(name) if fragments == nil { panic("invalid domain name format") } @@ -277,7 +280,7 @@ func Register(name string, owner interop.Hash160, email string, refresh, retry, if tldBytes == nil { panic("TLD not found") } - if parentExpired(ctx, 1, fragments) { + if parentExpired(ctx, fragments) { panic("one of the parent domains is not registered") } parentKey := getTokenKey([]byte(name[len(fragments[0])+1:])) @@ -388,7 +391,7 @@ func checkBaseRecords(typ RecordType, data string) bool { case A: return checkIPv4(data) case CNAME: - return splitAndCheck(data, true) != nil + return splitAndCheck(data) != nil case TXT: return len(data) <= maxTXTRecordLength case AAAA: @@ -508,7 +511,7 @@ func getNameState(ctx storage.Context, tokenID []byte) NameState { tokenKey := getTokenKey(tokenID) ns := getNameStateWithKey(ctx, tokenKey) fragments := std.StringSplit(string(tokenID), ".") - if parentExpired(ctx, 1, fragments) { + if parentExpired(ctx, fragments) { panic("parent domain has expired") } return ns @@ -716,16 +719,13 @@ func isAlNum(c uint8) bool { } // splitAndCheck splits domain name into parts and validates it. -func splitAndCheck(name string, allowMultipleFragments bool) []string { +func splitAndCheck(name string) []string { l := len(name) if l < minDomainNameLength || maxDomainNameLength < l { return nil } fragments := std.StringSplit(name, ".") l = len(fragments) - if l > 2 && !allowMultipleFragments { - return nil - } for i := 0; i < l; i++ { if !checkFragment(fragments[i], i == l-1) { return nil @@ -846,7 +846,7 @@ func checkIPv6(data string) bool { // tokenIDFromName returns token ID (domain.root) from the provided name. func tokenIDFromName(name string) string { - fragments := splitAndCheck(name, true) + fragments := splitAndCheck(name) if fragments == nil { panic("invalid domain name format") } diff --git a/tests/nns_test.go b/tests/nns_test.go index 77a610c..7ee43ae 100644 --- a/tests/nns_test.go +++ b/tests/nns_test.go @@ -334,6 +334,15 @@ func TestNNSIsAvailable(t *testing.T) { "myemail@frostfs.info", refresh, retry, expire, ttl) c.Invoke(t, false, "isAvailable", "domain.com") + + c.Invoke(t, true, "isAvailable", "dom.domain.com") + c.InvokeFail(t, "parent does not exist or is expired", "isAvailable", "dom.dom.domain.com") + + c1.Invoke(t, true, "register", + "dom.domain.com", acc.ScriptHash(), + "myemail@frostfs.info", refresh, retry, expire, ttl) + c.Invoke(t, false, "isAvailable", "dom.domain.com") + c.Invoke(t, true, "isAvailable", "dom.dom.domain.com") } func TestNNSRenew(t *testing.T) {