Skip to content

Commit

Permalink
nns: Allow to register pre-defined TLDs on deployment stage
Browse files Browse the repository at this point in the history
In order to register TLD, committee multi-signature must be gathered.
It is not always easy. For example, on "fresh" network, committee
members may not have a notary role, so they will not be able to use
Notary service to collect a signature. At the same time, Notary service
is the only convenient way to collect a multi-signature in a distributed
mode. To simplify the solution of the described task, it would be
convenient to be able to register pre-known TLDs at the NNS contract
deployment stage (which is performed by the committee).

Support list of (name, e-mail) pairs describing pre-defined TLDs as
optional deployment parameters (passed into `_deploy` callback).

Closes #334.

Signed-off-by: Leonard Lyubich <leonard@morphbits.io>
  • Loading branch information
cthulhu-rider committed Jun 8, 2023
1 parent b692b30 commit 850c3a6
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 16 deletions.
62 changes: 48 additions & 14 deletions nns/nns_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,20 @@ func _deploy(data interface{}, isUpdate bool) {
ctx := storage.GetContext()
storage.Put(ctx, []byte{prefixTotalSupply}, 0)
storage.Put(ctx, []byte{prefixRegisterPrice}, defaultRegisterPrice)

if data != nil { // for backward compatibility
args := data.(struct {
tldSet []struct {
name string
email string
}
})

for i := range args.tldSet {
saveCommitteeDomain(ctx, args.tldSet[i].name, args.tldSet[i].email)
runtime.Log("registered committee domain " + args.tldSet[i].name)
}
}
}

// Symbol returns NeoNameService symbol.
Expand Down Expand Up @@ -273,21 +287,16 @@ func Register(name string, owner interop.Hash160, email string, refresh, retry,
}

l := len(fragments)
tldKey := append([]byte{prefixRoot}, []byte(fragments[l-1])...)
tldKey := makeTLDKey(fragments[l-1])
ctx := storage.GetContext()
tldBytes := storage.Get(ctx, tldKey)
if l == 1 {
checkCommittee()
if tldBytes != nil {
panic("TLD already exists")
}
storage.Put(ctx, tldKey, 0)
putNameStateWithKey(ctx, getTokenKey([]byte(name)), NameState{
Name: name,
// NNS expiration is in milliseconds
Expiration: int64(runtime.GetTime() + expire*1000),
})
putSoaRecord(ctx, name, email, refresh, retry, expire, ttl)
putTLD(ctx, tldKey)
saveDomain(ctx, name, email, refresh, retry, expire, ttl, nil)
return true
}

Expand Down Expand Up @@ -336,17 +345,34 @@ func Register(name string, owner interop.Hash160, email string, refresh, retry,
} else {
updateTotalSupply(ctx, +1)
}
ns := NameState{
saveDomain(ctx, name, email, refresh, retry, expire, ttl, owner)
updateBalance(ctx, []byte(name), owner, +1)
postTransfer(oldOwner, owner, []byte(name), nil)
return true
}

func saveCommitteeDomain(ctx storage.Context, name, email string) {
putTLD(ctx, makeTLDKey(name))

// 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)
}

func saveDomain(ctx storage.Context, name, email string, refresh, retry, expire, ttl int, owner interop.Hash160) {
putNameStateWithKey(ctx, getTokenKey([]byte(name)), NameState{
Owner: owner,
Name: name,
// NNS expiration is in milliseconds
Expiration: int64(runtime.GetTime() + expire*1000),
}
putNameStateWithKey(ctx, tokenKey, ns)
})
putSoaRecord(ctx, name, email, refresh, retry, expire, ttl)
updateBalance(ctx, []byte(name), owner, +1)
postTransfer(oldOwner, owner, []byte(name), nil)
return true
}

// Renew increases domain expiration date.
Expand Down Expand Up @@ -927,3 +953,11 @@ func getAllRecords(ctx storage.Context, name string) iterator.Iterator {
recordsKey := getRecordsKey(tokenID, name)
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...)
}
45 changes: 43 additions & 2 deletions tests/nns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/neotest"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neofs-contract/common"
"github.com/nspcc-dev/neofs-contract/nns"
Expand All @@ -20,10 +21,18 @@ const nnsPath = "../nns"

const msPerYear = 365 * 24 * time.Hour / time.Millisecond

func newNNSInvoker(t *testing.T, addRoot bool) *neotest.ContractInvoker {
func newNNSInvoker(t *testing.T, addRoot bool, tldSet ...string) *neotest.ContractInvoker {
e := newExecutor(t)
ctr := neotest.CompileFile(t, e.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml"))
e.DeployContract(t, ctr, nil)
if len(tldSet) > 0 {
_tldSet := make([]interface{}, len(tldSet))
for i := range tldSet {
_tldSet[i] = []interface{}{tldSet[i], "user@domain.org"}
}
e.DeployContract(t, ctr, []interface{}{_tldSet})
} else {
e.DeployContract(t, ctr, nil)
}

c := e.CommitteeInvoker(ctr.Hash)
if addRoot {
Expand Down Expand Up @@ -420,3 +429,35 @@ func TestNNSRegisterAccess(t *testing.T) {
l2AdminInv.Invoke(t, true, "register",
l3ByL2Admin, l2AdminAcc.ScriptHash(), email, refresh, retry, expire, ttl)
}

func TestPredefinedTLD(t *testing.T) {
const anyTLD1 = "hello"
const anyTLD2 = "world"

inv := newNNSInvoker(t, false, anyTLD1, anyTLD2)

require.Nil(t, getDomainOwner(t, inv, anyTLD1))
require.Nil(t, getDomainOwner(t, inv, anyTLD2))
}

// getDomainOwner reads owner of the domain. Returns nil if domain is owned by the committee.
func getDomainOwner(tb testing.TB, inv *neotest.ContractInvoker, domain string) *util.Uint160 {
stack, err := inv.TestInvoke(tb, "ownerOf", domain)
require.NoError(tb, err)

arr := stack.ToArray()
require.Len(tb, arr, 1)

item := arr[0]
if _, ok := item.(stackitem.Null); ok {
return nil
}

b, err := item.TryBytes()
require.NoError(tb, err)

res, err := util.Uint160DecodeBytesBE(b)
require.NoError(tb, err)

return &res
}

0 comments on commit 850c3a6

Please sign in to comment.