Merge pull request #2679 from nspcc-dev/nns-upd

examples: NNS update, part 1
This commit is contained in:
Roman Khimov 2022-09-16 17:06:38 +03:00 committed by GitHub
commit 1f94ebe03d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 41 additions and 7 deletions

View file

@ -48,7 +48,7 @@ const (
// maxRootLength is the maximum domain root length. // maxRootLength is the maximum domain root length.
maxRootLength = 16 maxRootLength = 16
// maxDomainNameFragmentLength is the maximum length of the domain name fragment. // maxDomainNameFragmentLength is the maximum length of the domain name fragment.
maxDomainNameFragmentLength = 62 maxDomainNameFragmentLength = 63
// minDomainNameLength is minimum domain length. // minDomainNameLength is minimum domain length.
minDomainNameLength = 3 minDomainNameLength = 3
// maxDomainNameLength is maximum domain length. // maxDomainNameLength is maximum domain length.
@ -118,6 +118,7 @@ func Properties(tokenID []byte) map[string]interface{} {
return map[string]interface{}{ return map[string]interface{}{
"name": ns.Name, "name": ns.Name,
"expiration": ns.Expiration, "expiration": ns.Expiration,
"admin": ns.Admin,
} }
} }
@ -507,6 +508,8 @@ func checkCommittee() {
} }
// checkFragment validates root or a part of domain name. // checkFragment validates root or a part of domain name.
// 1. Root domain must start with a letter.
// 2. All other fragments must start and end in a letter or a digit.
func checkFragment(v string, isRoot bool) bool { func checkFragment(v string, isRoot bool) bool {
maxLength := maxDomainNameFragmentLength maxLength := maxDomainNameFragmentLength
if isRoot { if isRoot {
@ -525,12 +528,12 @@ func checkFragment(v string, isRoot bool) bool {
return false return false
} }
} }
for i := 1; i < len(v); i++ { for i := 1; i < len(v)-1; i++ {
if !isAlNum(v[i]) { if v[i] != '-' && !isAlNum(v[i]) {
return false return false
} }
} }
return true return isAlNum(v[len(v)-1])
} }
// isAlNum checks whether provided char is a lowercase letter or a number. // isAlNum checks whether provided char is a lowercase letter or a number.
@ -686,6 +689,12 @@ func resolve(ctx storage.Context, name string, typ RecordType, redirect int) str
if redirect < 0 { if redirect < 0 {
panic("invalid redirect") panic("invalid redirect")
} }
if len(name) == 0 {
panic("invalid name")
}
if name[len(name)-1] == '.' {
name = name[:len(name)-1]
}
records := getRecords(ctx, name) records := getRecords(ctx, name)
cname := "" cname := ""
for iterator.Next(records) { for iterator.Next(records) {

View file

@ -137,7 +137,10 @@ func TestExpiration(t *testing.T) {
cAcc.Invoke(t, stackitem.Null{}, "resolve", "first.com", int64(nns.TXT)) cAcc.Invoke(t, stackitem.Null{}, "resolve", "first.com", int64(nns.TXT))
} }
const millisecondsInYear = 365 * 24 * 3600 * 1000 const (
millisecondsInYear = 365 * 24 * 3600 * 1000
maxDomainNameFragmentLength = 63
)
func TestRegisterAndRenew(t *testing.T) { func TestRegisterAndRenew(t *testing.T) {
c := newNSClient(t) c := newNSClient(t)
@ -154,20 +157,34 @@ func TestRegisterAndRenew(t *testing.T) {
c.InvokeFail(t, "invalid domain name format", "register", "neo.com\n", e.CommitteeHash) c.InvokeFail(t, "invalid domain name format", "register", "neo.com\n", e.CommitteeHash)
c.InvokeWithFeeFail(t, "GAS limit exceeded", defaultNameServiceSysfee, "register", "neo.org", e.CommitteeHash) c.InvokeWithFeeFail(t, "GAS limit exceeded", defaultNameServiceSysfee, "register", "neo.org", e.CommitteeHash)
c.InvokeWithFeeFail(t, "GAS limit exceeded", defaultNameServiceDomainPrice, "register", "neo.com", e.CommitteeHash) c.InvokeWithFeeFail(t, "GAS limit exceeded", defaultNameServiceDomainPrice, "register", "neo.com", e.CommitteeHash)
var maxLenFragment string
for i := 0; i < maxDomainNameFragmentLength; i++ {
maxLenFragment += "q"
}
c.Invoke(t, true, "isAvailable", maxLenFragment+".com")
c.Invoke(t, true, "register", maxLenFragment+".com", e.CommitteeHash)
c.InvokeFail(t, "invalid domain name format", "register", maxLenFragment+"q.com", e.CommitteeHash)
c.Invoke(t, true, "isAvailable", "neo.com") c.Invoke(t, true, "isAvailable", "neo.com")
c.Invoke(t, 0, "balanceOf", e.CommitteeHash) c.Invoke(t, 1, "balanceOf", e.CommitteeHash)
c.Invoke(t, true, "register", "neo.com", e.CommitteeHash) c.Invoke(t, true, "register", "neo.com", e.CommitteeHash)
topBlock := e.TopBlock(t) topBlock := e.TopBlock(t)
expectedExpiration := topBlock.Timestamp + millisecondsInYear expectedExpiration := topBlock.Timestamp + millisecondsInYear
c.Invoke(t, false, "register", "neo.com", e.CommitteeHash) c.Invoke(t, false, "register", "neo.com", e.CommitteeHash)
c.Invoke(t, false, "isAvailable", "neo.com") c.Invoke(t, false, "isAvailable", "neo.com")
t.Run("domain names with hyphen", func(t *testing.T) {
c.InvokeFail(t, "invalid domain name format", "register", "-testdomain.com", e.CommitteeHash)
c.InvokeFail(t, "invalid domain name format", "register", "testdomain-.com", e.CommitteeHash)
c.Invoke(t, true, "register", "test-domain.com", e.CommitteeHash)
})
props := stackitem.NewMap() props := stackitem.NewMap()
props.Add(stackitem.Make("name"), stackitem.Make("neo.com")) props.Add(stackitem.Make("name"), stackitem.Make("neo.com"))
props.Add(stackitem.Make("expiration"), stackitem.Make(expectedExpiration)) props.Add(stackitem.Make("expiration"), stackitem.Make(expectedExpiration))
props.Add(stackitem.Make("admin"), stackitem.Null{}) // no admin was set
c.Invoke(t, props, "properties", "neo.com") c.Invoke(t, props, "properties", "neo.com")
c.Invoke(t, 1, "balanceOf", e.CommitteeHash) c.Invoke(t, 3, "balanceOf", e.CommitteeHash)
c.Invoke(t, e.CommitteeHash.BytesBE(), "ownerOf", []byte("neo.com")) c.Invoke(t, e.CommitteeHash.BytesBE(), "ownerOf", []byte("neo.com"))
t.Run("invalid token ID", func(t *testing.T) { t.Run("invalid token ID", func(t *testing.T) {
@ -303,6 +320,7 @@ func TestSetAdmin(t *testing.T) {
c.Invoke(t, stackitem.Null{}, "addRoot", "com") c.Invoke(t, stackitem.Null{}, "addRoot", "com")
cOwner.Invoke(t, true, "register", "neo.com", owner.ScriptHash()) cOwner.Invoke(t, true, "register", "neo.com", owner.ScriptHash())
expectedExpiration := e.TopBlock(t).Timestamp + millisecondsInYear
cGuest.InvokeFail(t, "not witnessed", "setAdmin", "neo.com", admin.ScriptHash()) cGuest.InvokeFail(t, "not witnessed", "setAdmin", "neo.com", admin.ScriptHash())
// Must be witnessed by both owner and admin. // Must be witnessed by both owner and admin.
@ -310,6 +328,11 @@ func TestSetAdmin(t *testing.T) {
cAdmin.InvokeFail(t, "not witnessed by owner", "setAdmin", "neo.com", admin.ScriptHash()) cAdmin.InvokeFail(t, "not witnessed by owner", "setAdmin", "neo.com", admin.ScriptHash())
cc := c.WithSigners(owner, admin) cc := c.WithSigners(owner, admin)
cc.Invoke(t, stackitem.Null{}, "setAdmin", "neo.com", admin.ScriptHash()) cc.Invoke(t, stackitem.Null{}, "setAdmin", "neo.com", admin.ScriptHash())
props := stackitem.NewMap()
props.Add(stackitem.Make("name"), stackitem.Make("neo.com"))
props.Add(stackitem.Make("expiration"), stackitem.Make(expectedExpiration))
props.Add(stackitem.Make("admin"), stackitem.Make(admin.ScriptHash().BytesBE()))
c.Invoke(t, props, "properties", "neo.com")
t.Run("set and delete by admin", func(t *testing.T) { t.Run("set and delete by admin", func(t *testing.T) {
cAdmin.Invoke(t, stackitem.Null{}, "setRecord", "neo.com", int64(nns.TXT), "sometext") cAdmin.Invoke(t, stackitem.Null{}, "setRecord", "neo.com", int64(nns.TXT), "sometext")
@ -420,6 +443,8 @@ func TestResolve(t *testing.T) {
c.Invoke(t, "1.2.3.4", "resolve", "neo.com", int64(nns.A)) c.Invoke(t, "1.2.3.4", "resolve", "neo.com", int64(nns.A))
c.Invoke(t, "alias.com", "resolve", "neo.com", int64(nns.CNAME)) c.Invoke(t, "alias.com", "resolve", "neo.com", int64(nns.CNAME))
c.Invoke(t, "sometxt", "resolve", "neo.com", int64(nns.TXT)) c.Invoke(t, "sometxt", "resolve", "neo.com", int64(nns.TXT))
c.Invoke(t, "sometxt", "resolve", "neo.com.", int64(nns.TXT))
c.InvokeFail(t, "invalid domain name format", "resolve", "neo.com..", int64(nns.TXT))
c.Invoke(t, stackitem.Null{}, "resolve", "neo.com", int64(nns.AAAA)) c.Invoke(t, stackitem.Null{}, "resolve", "neo.com", int64(nns.AAAA))
} }