diff --git a/nns/config.yml b/nns/config.yml index 9ae83cd..5090b40 100644 --- a/nns/config.yml +++ b/nns/config.yml @@ -2,7 +2,7 @@ name: "NameService" supportedstandards: ["NEP-11"] safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "tokens", "properties", "roots", "getPrice", "isAvailable", "getRecords", - "resolve", "version"] + "resolve", "version", "getDomains"] events: - name: RegisterDomain parameters: diff --git a/nns/nns_contract.go b/nns/nns_contract.go index 56dfb2c..b596508 100644 --- a/nns/nns_contract.go +++ b/nns/nns_contract.go @@ -1070,3 +1070,32 @@ func getAllRecords(ctx storage.Context, name string) iterator.Iterator { recordsKey := getRecordsKey(tokenID, name) return storage.Find(ctx, recordsKey, storage.ValuesOnly|storage.DeserializeValues) } + +// GetDomains returns a list of domains +func GetDomains(zone string) [][]byte { + ctx := storage.GetReadOnlyContext() + return getDomains(ctx, zone) +} + +func getDomains(ctx storage.Context, zone string) [][]byte { + if len(zone) > 0 && zone[0] != '.' { + zone = "." + zone + } + + var domains [][]byte + it := storage.Find(ctx, []byte{prefixName}, storage.ValuesOnly) + + for iterator.Next(it) { + domainRaw := iterator.Value(it).([]byte) + ns := std.Deserialize(domainRaw).(NameState) + firstSeparatorIndex := std.MemorySearch([]byte(ns.Name), []byte(".")) + lastSeparatorIndex := std.MemorySearchLastIndex([]byte(ns.Name), []byte(zone), len(ns.Name)) + + if lastSeparatorIndex == -1 || firstSeparatorIndex != lastSeparatorIndex || len(ns.Name)-lastSeparatorIndex != len(zone) { + continue + } + + domains = append(domains, []byte(ns.Name)) + } + return domains +} diff --git a/rpcclient/nns/client.go b/rpcclient/nns/client.go index b2cfa46..d47455f 100644 --- a/rpcclient/nns/client.go +++ b/rpcclient/nns/client.go @@ -85,6 +85,11 @@ func New(actor Actor, hash util.Uint160) *Contract { return &Contract{ContractReader{nep11ndt.NonDivisibleReader, actor, hash}, nep11ndt.BaseWriter, actor, hash} } +// GetDomains invokes `getDomains` method of contract. +func (c *ContractReader) GetDomains(zone string) ([]stackitem.Item, error) { + return unwrap.Array(c.invoker.Call(c.hash, "getDomains", zone)) +} + // GetPrice invokes `getPrice` method of contract. func (c *ContractReader) GetPrice() (*big.Int, error) { return unwrap.BigInt(c.invoker.Call(c.hash, "getPrice")) diff --git a/tests/nns_test.go b/tests/nns_test.go index 6c111b0..de6bd4f 100644 --- a/tests/nns_test.go +++ b/tests/nns_test.go @@ -232,6 +232,60 @@ func TestGlobalDomain(t *testing.T) { c.InvokeFail(t, "global domain is already taken", "isAvailable", "dom.testdomain.com") } + +func TestGetDomains(t *testing.T) { + c := newNNSInvoker(t, false) + + accTop := c.NewAccount(t) + refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104) + c1 := c.WithSigners(c.Committee, accTop) + c1.Invoke(t, true, "register", + "com", accTop.ScriptHash(), + "myemail@frostfs.info", refresh, retry, expire, ttl) + + c1.Invoke(t, true, "register", + "com.com", accTop.ScriptHash(), + "myemail@frostfs.info", refresh, retry, expire, ttl) + + c1.Invoke(t, true, "register", + "com.com.com", accTop.ScriptHash(), + "myemail@frostfs.info", refresh, retry, expire, ttl) + + c1.Invoke(t, true, "register", + "com.com.com.com", accTop.ScriptHash(), + "myemail@frostfs.info", refresh, retry, expire, ttl) + + c1.Invoke(t, true, "register", + "net", accTop.ScriptHash(), + "myemail@frostfs.info", refresh, retry, expire, ttl) + + c1.Invoke(t, true, "register", + "dom.com", accTop.ScriptHash(), + "myemail@frostfs.info", refresh, retry, expire, ttl) + + c1.Invoke(t, true, "register", + "globaldomain.com", accTop.ScriptHash(), + "myemail@frostfs.info", refresh, retry, expire, ttl) + + c1.Invoke(t, true, "register", + "domik.dom.com", accTop.ScriptHash(), + "myemail@frostfs.info", refresh, retry, expire, ttl) + + c1.Invoke(t, true, "register", + "such-tiny-domik.domik.dom.com", accTop.ScriptHash(), + "myemail@frostfs.info", refresh, retry, expire, ttl) + + c1.Invoke(t, []stackitem.Item{stackitem.NewBuffer([]byte("dom.com")), stackitem.NewBuffer([]byte("globaldomain.com")), + stackitem.NewBuffer([]byte("com.com"))}, "getDomains", "com") + + c1.Invoke(t, []stackitem.Item{stackitem.NewBuffer([]byte("domik.dom.com"))}, "getDomains", "dom.com") + + c1.Invoke(t, []stackitem.Item{stackitem.NewBuffer([]byte("such-tiny-domik.domik.dom.com"))}, "getDomains", "domik.dom.com") + + c1.Invoke(t, stackitem.Null{}, "getDomains", "net") + c1.Invoke(t, stackitem.Null{}, "getDomains", ".") +} + func TestTLDRecord(t *testing.T) { c := newNNSInvoker(t, true) c.Invoke(t, stackitem.Null{}, "addRecord",