Skip to content

Commit

Permalink
nns: Add dedicated method for TLD registration
Browse files Browse the repository at this point in the history
Top-level domains are controlled by the committee. Previously, NNS
contract provided `register` method that accepted fixed domain owner.
After recent changes, TLDs were forbidden to be treated as regular
tokens in terms of the NNS contract. According to this, `register`
method is no longer well-suited for TLD registration, so, it's worth to
provide dedicated method for this purpose.

Add `registerTLD` method with signature similar to the `register` one
but w/o owner parameter.

Closes 334.

Signed-off-by: Leonard Lyubich <leonard@morphbits.io>
  • Loading branch information
cthulhu-rider committed Jun 10, 2023
1 parent 5758c84 commit 5b1f59b
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 62 deletions.
2 changes: 1 addition & 1 deletion container/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: "NeoFS Container"
safemethods: ["count", "containersOf", "get", "owner", "list", "eACL", "getContainerSize", "listContainerSizes", "iterateContainerSizes", "iterateAllContainerSizes", "version"]
permissions:
- methods: ["update", "addKey", "transferX",
"register", "addRecord", "deleteRecords"]
"register", "registerTLD", "addRecord", "deleteRecords"]
events:
- name: PutSuccess
parameters:
Expand Down
9 changes: 3 additions & 6 deletions container/container_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,12 +208,9 @@ func registerNiceNameTLD(addrNNS interop.Hash160, nnsRoot string) {
return
}

res := contract.Call(addrNNS, "register", contract.All,
nnsRoot, runtime.GetExecutingScriptHash(), "ops@nspcc.ru",
defaultRefresh, defaultRetry, defaultExpire, defaultTTL).(bool)
if !res {
panic("can't register NNS TLD")
}
contract.Call(addrNNS, "registerTLD", contract.All,
nnsRoot, "ops@nspcc.ru",
defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
}

// Update method updates contract source code and manifest. It can be invoked
Expand Down
66 changes: 39 additions & 27 deletions nns/nns_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,14 @@ func _deploy(data interface{}, isUpdate bool) {
})

for i := range args.tldSet {
saveCommitteeDomain(ctx, args.tldSet[i].name, args.tldSet[i].email)
// TODO: values from NeoFS ADM, check
const (
refresh = 3600
retry = 600
expire = 10 * 365 * 24 * 60 * 60 // 10 years
ttl = 3600
)
saveCommitteeDomain(ctx, args.tldSet[i].name, args.tldSet[i].email, refresh, retry, expire, ttl)
runtime.Log("registered committee domain " + args.tldSet[i].name)
}
}
Expand Down Expand Up @@ -296,10 +303,11 @@ func parentExpired(ctx storage.Context, first int, fragments []string) bool {
return false
}

// Register registers a new domain with the specified owner and name if it's available.
// Register registers a new domain with the specified owner and name if it's
// available. Top-level domains MUST NOT be registered via Register, use
// RegisterTLD for this.
//
// Access rules::
// - TLD can be registered only by the committee
// - 2nd-level domain can be registered by anyone
// - starting from the 3rd level, the domain can only be registered by the
// owner or administrator (if any) of the previous level domain
Expand All @@ -310,20 +318,13 @@ func Register(name string, owner interop.Hash160, email string, refresh, retry,
}

l := len(fragments)
tldKey := makeTLDKey(fragments[l-1])
ctx := storage.GetContext()
tldBytes := storage.Get(ctx, tldKey)
if l == 1 {
checkCommittee()
if tldBytes != nil {
panic("TLD already exists")
}
putTLD(ctx, tldKey)
saveDomain(ctx, name, email, refresh, retry, expire, ttl, nil)
return true
panic("TLD denied")
}

if tldBytes == nil {
ctx := storage.GetContext()

if storage.Get(ctx, makeTLDKey(fragments[l-1])) == nil {
panic("TLD not found")
}
if parentExpired(ctx, 1, fragments) {
Expand Down Expand Up @@ -374,16 +375,31 @@ func Register(name string, owner interop.Hash160, email string, refresh, retry,
return true
}

func saveCommitteeDomain(ctx storage.Context, name, email string) {
putTLD(ctx, makeTLDKey(name))
// RegisterTLD registers new top-level domain. RegisterTLD MUST be called by the
// committee only. Name MUST be a valid TLD.
//
// RegisterTLD panics with 'TLD already exists' if domain already exists.
func RegisterTLD(name, email string, refresh, retry, expire, ttl int) {
checkCommittee()
saveCommitteeDomain(storage.GetContext(), name, email, refresh, retry, expire, ttl)
}

// saveCommitteeDomain marks TLD as registered via prefixRoot<name> storage
// record and saves domain state calling saveDomain with given parameters and
// empty owner. The name MUST be a valid TLD name.
func saveCommitteeDomain(ctx storage.Context, name, email string, refresh, retry, expire, ttl int) {
fragments := splitAndCheck(name, false)
if len(fragments) != 1 {
panic("invalid domain name format")
}

tldKey := makeTLDKey(name)
if storage.Get(ctx, tldKey) != nil {
panic("TLD already exists")
}

storage.Put(ctx, tldKey, 0)

// TODO: values from NeoFS ADM, check
const (
refresh = 3600
retry = 600
expire = 10 * 365 * 24 * 60 * 60 // 10 years
ttl = 3600
)
var committeeOwner interop.Hash160
saveDomain(ctx, name, email, refresh, retry, expire, ttl, committeeOwner)
}
Expand Down Expand Up @@ -1033,10 +1049,6 @@ func getAllRecords(ctx storage.Context, name string) iterator.Iterator {
return storage.Find(ctx, recordsKey, storage.ValuesOnly|storage.DeserializeValues)
}

func putTLD(ctx storage.Context, key []byte) {
storage.Put(ctx, key, 0)
}

func makeTLDKey(name string) []byte {
return append([]byte{prefixRoot}, name...)
}
5 changes: 2 additions & 3 deletions tests/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,8 @@ func TestContainerPut(t *testing.T) {
cnt.value[len(cnt.value)-1] = 10
cnt.id = sha256.Sum256(cnt.value)

cNNS.Invoke(t, true, "register",
"cdn", c.CommitteeHash,
"whateveriwant@world.com", int64(0), int64(0), int64(100_000), int64(0))
cNNS.Invoke(t, stackitem.Null{}, "registerTLD",
"cdn", "whateveriwant@world.com", int64(0), int64(0), int64(100_000), int64(0))

cNNS.Invoke(t, true, "register",
"domain.cdn", c.CommitteeHash,
Expand Down
46 changes: 21 additions & 25 deletions tests/nns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,8 @@ func newNNSInvoker(t *testing.T, addRoot bool, tldSet ...string) *neotest.Contra
if addRoot {
// Set expiration big enough to pass all tests.
refresh, retry, expire, ttl := int64(101), int64(102), int64(msPerYear/1000*100), int64(104)
c.Invoke(t, true, "register",
"com", c.CommitteeHash,
"myemail@nspcc.ru", refresh, retry, expire, ttl)
c.Invoke(t, stackitem.Null{}, "registerTLD",
"com", "myemail@nspcc.ru", refresh, retry, expire, ttl)
}
return c
}
Expand All @@ -58,23 +57,19 @@ func TestNNSRegisterTLD(t *testing.T) {

refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)

c.InvokeFail(t, "invalid domain name format", "register",
"0com", c.CommitteeHash,
"email@nspcc.ru", refresh, retry, expire, ttl)
c.InvokeFail(t, "invalid domain name format", "registerTLD",
"0com", "email@nspcc.ru", refresh, retry, expire, ttl)

acc := c.NewAccount(t)
cAcc := c.WithSigners(acc)
cAcc.InvokeFail(t, "not witnessed by committee", "register",
"com", acc.ScriptHash(),
"email@nspcc.ru", refresh, retry, expire, ttl)
cAcc.InvokeFail(t, "not witnessed by committee", "registerTLD",
"com", "email@nspcc.ru", refresh, retry, expire, ttl)

c.Invoke(t, true, "register",
"com", c.CommitteeHash,
"email@nspcc.ru", refresh, retry, expire, ttl)
c.Invoke(t, stackitem.Null{}, "registerTLD",
"com", "email@nspcc.ru", refresh, retry, expire, ttl)

c.InvokeFail(t, "TLD already exists", "register",
"com", c.CommitteeHash,
"email@nspcc.ru", refresh, retry, expire, ttl)
c.InvokeFail(t, "TLD already exists", "registerTLD",
"com", "email@nspcc.ru", refresh, retry, expire, ttl)
}

func TestNNSRegister(t *testing.T) {
Expand All @@ -83,9 +78,8 @@ func TestNNSRegister(t *testing.T) {
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@nspcc.ru", refresh, retry, expire, ttl)
c1.Invoke(t, stackitem.Null{}, "registerTLD",
"com", "myemail@nspcc.ru", refresh, retry, expire, ttl)

acc := c.NewAccount(t)

Expand Down Expand Up @@ -313,9 +307,8 @@ func TestNNSIsAvailable(t *testing.T) {
c.InvokeFail(t, "TLD not found", "isAvailable", "domain.com")

refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
c.Invoke(t, true, "register",
"com", c.CommitteeHash,
"myemail@nspcc.ru", refresh, retry, expire, ttl)
c.Invoke(t, stackitem.Null{}, "registerTLD",
"com", "myemail@nspcc.ru", refresh, retry, expire, ttl)

c.Invoke(t, false, "isAvailable", "com")
c.Invoke(t, true, "isAvailable", "domain.com")
Expand Down Expand Up @@ -374,19 +367,22 @@ func TestNNSRegisterAccess(t *testing.T) {
const email, refresh, retry, expire, ttl = "user@domain.org", 0, 1, 2, 3
const tld = "com"
const registerMethod = "register"
const ownerWitnessFailMsg = "not witnessed by committee"
const registerTLDMethod = "registerTLD"
const tldDeniedFailMsg = "TLD denied"

// TLD
l2OwnerAcc := inv.NewAccount(t)
l2OwnerInv := inv.WithSigners(l2OwnerAcc)

l2OwnerInv.InvokeFail(t, ownerWitnessFailMsg, registerMethod,
l2OwnerInv.InvokeFail(t, tldDeniedFailMsg, registerMethod,
tld, l2OwnerAcc.ScriptHash(), email, refresh, retry, expire, ttl)
l2OwnerInv.InvokeFail(t, ownerWitnessFailMsg, registerMethod,
l2OwnerInv.InvokeFail(t, tldDeniedFailMsg, registerMethod,
tld, nil, email, refresh, retry, expire, ttl)

inv.WithSigners(inv.Committee).Invoke(t, true, registerMethod,
inv.WithSigners(inv.Committee).InvokeFail(t, tldDeniedFailMsg, registerMethod,
tld, nil, email, refresh, retry, expire, ttl)
inv.WithSigners(inv.Committee).Invoke(t, stackitem.Null{}, registerTLDMethod,
tld, email, refresh, retry, expire, ttl)

// L2
const l2 = "l2." + tld
Expand Down

0 comments on commit 5b1f59b

Please sign in to comment.