diff --git a/nns/nns.yml b/nns/nns.yml index 0192a8a..d7f9625 100644 --- a/nns/nns.yml +++ b/nns/nns.yml @@ -15,6 +15,7 @@ safemethods: - "tokens" - "resolve" - "roots" + - "getDomains" events: - name: Transfer parameters: diff --git a/nns/nns_contract.go b/nns/nns_contract.go index bef2586..5ff4af9 100644 --- a/nns/nns_contract.go +++ b/nns/nns_contract.go @@ -1064,3 +1064,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) + firstIndex := std.MemorySearch([]byte(ns.Name), []byte(zone)) + lastIndex := std.MemorySearchLastIndex([]byte(ns.Name), []byte(zone), len(ns.Name)) + + if lastIndex == -1 || firstIndex != lastIndex || len(ns.Name)-lastIndex != len(zone) { + continue + } + + domains = append(domains, []byte(ns.Name)) + } + return domains +} diff --git a/rpcclient/nns/client.go b/rpcclient/nns/client.go index 858819d..5fcf45a 100644 --- a/rpcclient/nns/client.go +++ b/rpcclient/nns/client.go @@ -187,6 +187,28 @@ func (c *Contract) GetAllRecordsUnsigned(name string) (*transaction.Transaction, return c.actor.MakeUnsignedCall(c.hash, "getAllRecords", nil, name) } +// GetDomains creates a transaction invoking `getDomains` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) GetDomains(zone string) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "getDomains", zone) +} + +// GetDomainsTransaction creates a transaction invoking `getDomains` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) GetDomainsTransaction(zone string) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "getDomains", zone) +} + +// GetDomainsUnsigned creates a transaction invoking `getDomains` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) GetDomainsUnsigned(zone string) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "getDomains", nil, zone) +} + func (c *Contract) scriptForRegister(name string, owner util.Uint160, email string, refresh *big.Int, retry *big.Int, expire *big.Int, ttl *big.Int) ([]byte, error) { return smartcontract.CreateCallWithAssertScript(c.hash, "register", name, owner, email, refresh, retry, expire, ttl) } diff --git a/tests/nns_test.go b/tests/nns_test.go index e824303..3190fb9 100644 --- a/tests/nns_test.go +++ b/tests/nns_test.go @@ -202,6 +202,53 @@ 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, []stackitem.Item{stackitem.NewBuffer([]byte("dom.com")), stackitem.NewBuffer([]byte("globaldomain.com")), + stackitem.NewBuffer([]byte("com.com")), stackitem.NewBuffer([]byte("domik.dom.com"))}, "getDomains", "com") + + c1.Invoke(t, []stackitem.Item{stackitem.NewBuffer([]byte("domik.dom.com"))}, "getDomains", "dom.com") + + c1.Invoke(t, stackitem.Null{}, "getDomains", "net") +} + func TestTLDRecord(t *testing.T) { c := newNNSInvoker(t, true) c.Invoke(t, stackitem.Null{}, "addRecord",