nns: Allow 'Register' register TLD from nested domains #117
2 changed files with 89 additions and 7 deletions
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue