From f12c44fc786a9ed4e6ff24a38be3cde7cd624ee9 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Fri, 19 Nov 2021 16:35:58 +0300 Subject: [PATCH] nns: allow to register arbitrary-level domains Signed-off-by: Evgenii Stratonikov --- nns/nns_contract.go | 26 +++++++++++++++++++------- tests/nns_test.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/nns/nns_contract.go b/nns/nns_contract.go index 60035de..dea6adc 100644 --- a/nns/nns_contract.go +++ b/nns/nns_contract.go @@ -272,7 +272,7 @@ func parentExpired(ctx storage.Context, first int, fragments []string) bool { // Register registers 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, false) + fragments := splitAndCheck(name, true) if fragments == nil { panic("invalid domain name format") } @@ -292,9 +292,9 @@ func Register(name string, owner interop.Hash160, email string, refresh, retry, panic("TLD not found") } if parentExpired(ctx, 1, fragments) { - panic("one of the parent domains has expired") + panic("one of the parent domains is not registered") } - parentKey := getTokenKey([]byte(fragments[1])) + parentKey := getTokenKey([]byte(name[len(fragments[0])+1:])) nsBytes := storage.Get(ctx, append([]byte{prefixName}, parentKey...)) ns := std.Deserialize(nsBytes.([]byte)).(NameState) ns.checkAdmin() @@ -851,11 +851,23 @@ func tokenIDFromName(name string) string { if fragments == nil { panic("invalid domain name format") } - l := len(fragments) - if l == 1 { - return name + + ctx := storage.GetReadOnlyContext() + sum := 0 + l := len(fragments) - 1 + for i := 0; i < l; i++ { + tokenKey := getTokenKey([]byte(name[sum:])) + nameKey := append([]byte{prefixName}, tokenKey...) + nsBytes := storage.Get(ctx, nameKey) + if nsBytes != nil { + ns := std.Deserialize(nsBytes.([]byte)).(NameState) + if runtime.GetTime() < ns.Expiration { + return name[sum:] + } + } + sum += len(fragments[i]) + 1 } - return name[len(name)-(len(fragments[l-1])+len(fragments[l-2])+1):] + return name } // resolve resolves provided name using record with the specified type and given diff --git a/tests/nns_test.go b/tests/nns_test.go index c0feb47..c8aa5eb 100644 --- a/tests/nns_test.go +++ b/tests/nns_test.go @@ -134,6 +134,49 @@ func TestTLDRecord(t *testing.T) { c.Invoke(t, result, "resolve", "com", int64(nns.A)) } +func TestNNSRegisterMulti(t *testing.T) { + c := newNNSInvoker(t, true) + + newArgs := func(domain string, account neotest.Signer) []interface{} { + return []interface{}{ + domain, account.ScriptHash(), "doesnt@matter.com", + int64(101), int64(102), int64(103), int64(104), + } + } + acc := c.NewAccount(t) + cBoth := c.WithSigners(c.Committee, acc) + args := newArgs("neo.com", acc) + cBoth.Invoke(t, true, "register", args...) + + c1 := c.WithSigners(acc) + t.Run("parent domain is missing", func(t *testing.T) { + msg := "one of the parent domains is not registered" + args[0] = "testnet.fs.neo.com" + c1.InvokeFail(t, msg, "register", args...) + }) + + args[0] = "fs.neo.com" + c1.Invoke(t, true, "register", args...) + + args[0] = "testnet.fs.neo.com" + c1.Invoke(t, true, "register", args...) + + acc2 := c.NewAccount(t) + c2 := c.WithSigners(c.Committee, acc2) + args = newArgs("mainnet.fs.neo.com", acc2) + c2.InvokeFail(t, "not witnessed by admin", "register", args...) + + c2 = c.WithSigners(acc, acc2) + c2.Invoke(t, true, "register", args...) + + c2 = c.WithSigners(acc2) + c2.Invoke(t, stackitem.Null{}, "addRecord", + "cdn.mainnet.fs.neo.com", int64(nns.A), "166.15.14.13") + result := stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray([]byte("166.15.14.13"))}) + c2.Invoke(t, result, "resolve", "cdn.mainnet.fs.neo.com", int64(nns.A)) +} + func TestNNSUpdateSOA(t *testing.T) { c := newNNSInvoker(t, true)