From d8e0a02a865dde1f16315f719c9181fc7b4e69eb Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Thu, 15 Sep 2022 22:27:10 +0300 Subject: [PATCH] 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. https://github.com/nspcc-dev/neofs-contract/pull/175/commits/f25296b17a4dcaca50855c40d44a42bbcf0bb6a1. --- examples/nft-nd-nns/nns.go | 35 ++++++++++++++++++++++----------- examples/nft-nd-nns/nns_test.go | 26 +++++++----------------- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/examples/nft-nd-nns/nns.go b/examples/nft-nd-nns/nns.go index f3e09d950..bc5ad890c 100644 --- a/examples/nft-nd-nns/nns.go +++ b/examples/nft-nd-nns/nns.go @@ -208,7 +208,7 @@ func GetPrice() int { // IsAvailable checks whether provided domain name is available. func IsAvailable(name string) bool { - fragments := splitAndCheck(name, false) + fragments := splitAndCheck(name, true) if fragments == nil { panic("invalid domain name format") } @@ -220,7 +220,27 @@ func IsAvailable(name string) bool { } 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. @@ -273,15 +293,8 @@ func Register(name string, owner interop.Hash160, email string, refresh, retry, ns := std.Deserialize(nsBytes.([]byte)).(NameState) ns.checkAdmin() - 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) { - panic("parent domain has conflicting records: " + r.Name) - } + if conflict := getParentConflictingRecord(ctx, name, fragments); len(conflict) != 0 { + panic("parent domain has conflicting records: " + conflict) } } diff --git a/examples/nft-nd-nns/nns_test.go b/examples/nft-nd-nns/nns_test.go index 09e96a46f..b43b1e32c 100644 --- a/examples/nft-nd-nns/nns_test.go +++ b/examples/nft-nd-nns/nns_test.go @@ -125,29 +125,15 @@ func TestExpiration(t *testing.T) { require.NoError(t, bc.AddBlock(e.SignBlock(b2))) e.CheckHalt(t, tx.Hash(), stackitem.NewBool(true)) - tx = cAcc.PrepareInvoke(t, "isAvailable", "first.com") - b3 := e.NewUnsignedBlock(t, tx) + b3 := e.NewUnsignedBlock(t) b3.Index = b2.Index + 1 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))) - e.CheckHalt(t, tx.Hash(), stackitem.NewBool(true)) // "first.com" has been expired - tx = cAcc.PrepareInvoke(t, "isAvailable", "second.com") - b4 := e.NewUnsignedBlock(t, tx) - b4.Index = b3.Index + 1 - 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") + cAcc.Invoke(t, true, "isAvailable", "first.com") // "first.com" has been expired + cAcc.Invoke(t, true, "isAvailable", "second.com") // TLD "com" has been expired + cAcc.InvokeFail(t, "name has expired", "getRecords", "first.com", int64(nns.TXT)) // 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 @@ -629,11 +615,13 @@ func TestNNSRegisterArbitraryLevelDomain(t *testing.T) { "another.fs.neo.com", int64(nns.A), "4.3.2.1") 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", "register", args...) c1.Invoke(t, stackitem.Null{}, "deleteRecords", "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 = c.WithSigners(acc2)