nns: keep isAvailable in sync with register

If conflicting records '*.domain' are present on new domain
registration, then `isAvailable` should return false for this
domain. Ref.
f25296b17a.
This commit is contained in:
Anna Shaleva 2022-09-15 22:27:10 +03:00
parent d5b1c0e429
commit d8e0a02a86
2 changed files with 31 additions and 30 deletions

View file

@ -208,7 +208,7 @@ func GetPrice() int {
// IsAvailable checks whether provided domain name is available. // IsAvailable checks whether provided domain name is available.
func IsAvailable(name string) bool { func IsAvailable(name string) bool {
fragments := splitAndCheck(name, false) fragments := splitAndCheck(name, true)
if fragments == nil { if fragments == nil {
panic("invalid domain name format") panic("invalid domain name format")
} }
@ -220,7 +220,27 @@ func IsAvailable(name string) bool {
} }
return true return true
} }
return parentExpired(ctx, 0, fragments) if !parentExpired(ctx, 0, fragments) {
return false
}
return len(getParentConflictingRecord(ctx, name, fragments)) == 0
}
// getPrentConflictingRecord returns record of '*.name' format if they are presented.
// These records conflict with domain name to be registered.
func getParentConflictingRecord(ctx storage.Context, name string, fragments []string) string {
parentKey := getTokenKey([]byte(name[len(fragments[0])+1:]))
parentRecKey := append([]byte{prefixRecord}, parentKey...)
it := storage.Find(ctx, parentRecKey, storage.ValuesOnly|storage.DeserializeValues)
suffix := []byte(name)
for iterator.Next(it) {
r := iterator.Value(it).(RecordState)
ind := std.MemorySearchLastIndex([]byte(r.Name), suffix, len(r.Name))
if ind > 0 && ind+len(suffix) == len(r.Name) {
return r.Name
}
}
return ""
} }
// parentExpired returns true if any domain from fragments doesn't exist or expired. // parentExpired returns true if any domain from fragments doesn't exist or expired.
@ -273,15 +293,8 @@ func Register(name string, owner interop.Hash160, email string, refresh, retry,
ns := std.Deserialize(nsBytes.([]byte)).(NameState) ns := std.Deserialize(nsBytes.([]byte)).(NameState)
ns.checkAdmin() ns.checkAdmin()
parentRecKey := append([]byte{prefixRecord}, parentKey...) if conflict := getParentConflictingRecord(ctx, name, fragments); len(conflict) != 0 {
it := storage.Find(ctx, parentRecKey, storage.ValuesOnly|storage.DeserializeValues) panic("parent domain has conflicting records: " + conflict)
suffix := []byte(name)
for iterator.Next(it) {
r := iterator.Value(it).(RecordState)
ind := std.MemorySearchLastIndex([]byte(r.Name), suffix, len(r.Name))
if ind > 0 && ind+len(suffix) == len(r.Name) {
panic("parent domain has conflicting records: " + r.Name)
}
} }
} }

View file

@ -125,29 +125,15 @@ func TestExpiration(t *testing.T) {
require.NoError(t, bc.AddBlock(e.SignBlock(b2))) require.NoError(t, bc.AddBlock(e.SignBlock(b2)))
e.CheckHalt(t, tx.Hash(), stackitem.NewBool(true)) e.CheckHalt(t, tx.Hash(), stackitem.NewBool(true))
tx = cAcc.PrepareInvoke(t, "isAvailable", "first.com") b3 := e.NewUnsignedBlock(t)
b3 := e.NewUnsignedBlock(t, tx)
b3.Index = b2.Index + 1 b3.Index = b2.Index + 1
b3.PrevHash = b2.Hash() b3.PrevHash = b2.Hash()
b3.Timestamp = b1.Timestamp + (uint64(expire)*1000 + 1) b3.Timestamp = b1.Timestamp + (uint64(expire) * 1000)
require.NoError(t, bc.AddBlock(e.SignBlock(b3))) require.NoError(t, bc.AddBlock(e.SignBlock(b3)))
e.CheckHalt(t, tx.Hash(), stackitem.NewBool(true)) // "first.com" has been expired
tx = cAcc.PrepareInvoke(t, "isAvailable", "second.com") cAcc.Invoke(t, true, "isAvailable", "first.com") // "first.com" has been expired
b4 := e.NewUnsignedBlock(t, tx) cAcc.Invoke(t, true, "isAvailable", "second.com") // TLD "com" has been expired
b4.Index = b3.Index + 1 cAcc.InvokeFail(t, "name has expired", "getRecords", "first.com", int64(nns.TXT))
b4.PrevHash = b3.Hash()
b4.Timestamp = b3.Timestamp + 1000
require.NoError(t, bc.AddBlock(e.SignBlock(b4)))
e.CheckHalt(t, tx.Hash(), stackitem.NewBool(true)) // TLD "com" has been expired
tx = cAcc.PrepareInvoke(t, "getRecords", "first.com", int64(nns.TXT))
b5 := e.NewUnsignedBlock(t, tx)
b5.Index = b4.Index + 1
b5.PrevHash = b4.Hash()
b5.Timestamp = b4.Timestamp + 1000
require.NoError(t, bc.AddBlock(e.SignBlock(b5)))
e.CheckFault(t, tx.Hash(), "name has expired")
// TODO: According to the new code, we can't re-register expired "com" TLD, because it's already registered; at the // TODO: According to the new code, we can't re-register expired "com" TLD, because it's already registered; at the
// same time we can't renew it because it's already expired. We likely need to change this logic in the contract and // same time we can't renew it because it's already expired. We likely need to change this logic in the contract and
@ -629,11 +615,13 @@ func TestNNSRegisterArbitraryLevelDomain(t *testing.T) {
"another.fs.neo.com", int64(nns.A), "4.3.2.1") "another.fs.neo.com", int64(nns.A), "4.3.2.1")
c2 = c.WithSigners(acc, acc2) c2 = c.WithSigners(acc, acc2)
c2.Invoke(t, stackitem.NewBool(false), "isAvailable", "mainnet.fs.neo.com")
c2.InvokeFail(t, "parent domain has conflicting records: something.mainnet.fs.neo.com", c2.InvokeFail(t, "parent domain has conflicting records: something.mainnet.fs.neo.com",
"register", args...) "register", args...)
c1.Invoke(t, stackitem.Null{}, "deleteRecords", c1.Invoke(t, stackitem.Null{}, "deleteRecords",
"something.mainnet.fs.neo.com", int64(nns.A)) "something.mainnet.fs.neo.com", int64(nns.A))
c2.Invoke(t, stackitem.NewBool(true), "isAvailable", "mainnet.fs.neo.com")
c2.Invoke(t, true, "register", args...) c2.Invoke(t, true, "register", args...)
c2 = c.WithSigners(acc2) c2 = c.WithSigners(acc2)