[#139] nns: replace root with TLD

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2021-10-04 13:26:23 +03:00 committed by Alex Vanin
parent edbd137340
commit e0dbd07f21
3 changed files with 136 additions and 58 deletions

View file

@ -64,6 +64,12 @@ const (
// NotFoundError is returned if container is missing. // NotFoundError is returned if container is missing.
NotFoundError = "container does not exist" NotFoundError = "container does not exist"
// default SOA record field values
defaultRefresh = 3600 // 1 hour
defaultRetry = 600 // 10 min
defaultExpire = 604800 // 1 week
defaultTTL = 3600 // 1 hour
) )
var ( var (
@ -114,14 +120,18 @@ func _deploy(data interface{}, isUpdate bool) {
} }
func registerNiceNameTLD(addrNNS interop.Hash160, nnsRoot string) { func registerNiceNameTLD(addrNNS interop.Hash160, nnsRoot string) {
iter := contract.Call(addrNNS, "roots", contract.ReadStates).(iterator.Iterator) isAvail := contract.Call(addrNNS, "isAvailable", contract.AllowCall|contract.ReadStates,
for iterator.Next(iter) { "container").(bool)
if iterator.Value(iter).(string) == nnsRoot { if !isAvail {
runtime.Log("NNS root is already registered: " + nnsRoot)
return return
} }
res := contract.Call(addrNNS, "register", contract.All,
nnsRoot, common.CommitteeAddress(), "ops@nspcc.ru",
defaultRefresh, defaultRetry, defaultExpire, defaultTTL).(bool)
if !res {
panic("can't register NNS TLD")
} }
contract.Call(addrNNS, "addRoot", contract.All, nnsRoot)
} }
// Update method updates contract source code and manifest. Can be invoked // Update method updates contract source code and manifest. Can be invoked
@ -237,12 +247,6 @@ func PutNamed(container []byte, signature interop.Signature,
if name != "" { if name != "" {
if needRegister { if needRegister {
const (
defaultRefresh = 3600 // 1 hour
defaultRetry = 600 // 10 min
defaultExpire = 604800 // 1 week
defaultTTL = 3600 // 1 hour
)
res := contract.Call(nnsContractAddr, "register", contract.All, res := contract.Call(nnsContractAddr, "register", contract.All,
domain, runtime.GetExecutingScriptHash(), "ops@nspcc.ru", domain, runtime.GetExecutingScriptHash(), "ops@nspcc.ru",
defaultRefresh, defaultRetry, defaultExpire, defaultTTL).(bool) defaultRefresh, defaultRetry, defaultExpire, defaultTTL).(bool)

View file

@ -218,22 +218,6 @@ func Transfer(to interop.Hash160, tokenID []byte, data interface{}) bool {
return true return true
} }
// AddRoot registers new root.
func AddRoot(root string) {
checkCommittee()
if !checkFragment(root, true) {
panic("invalid root format")
}
var (
ctx = storage.GetContext()
rootKey = append([]byte{prefixRoot}, []byte(root)...)
)
if storage.Get(ctx, rootKey) != nil {
panic("root already exists")
}
storage.Put(ctx, rootKey, 0)
}
// Roots returns iterator over a set of NameService roots. // Roots returns iterator over a set of NameService roots.
func Roots() iterator.Iterator { func Roots() iterator.Iterator {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
@ -263,15 +247,36 @@ func IsAvailable(name string) bool {
panic("invalid domain name format") panic("invalid domain name format")
} }
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
if storage.Get(ctx, append([]byte{prefixRoot}, []byte(fragments[1])...)) == nil { l := len(fragments)
panic("root not found") if storage.Get(ctx, append([]byte{prefixRoot}, []byte(fragments[l-1])...)) == nil {
if l != 1 {
panic("TLD not found")
}
return true
}
return parentExpired(ctx, 0, fragments)
}
// parentExpired returns true if any domain from fragments doesn't exist or expired.
// first denotes the deepest subdomain to check.
func parentExpired(ctx storage.Context, first int, fragments []string) bool {
now := runtime.GetTime()
last := len(fragments) - 1
name := fragments[last]
for i := last; i >= first; i-- {
if i != last {
name = fragments[i] + "." + name
} }
nsBytes := storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(name))...)) nsBytes := storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(name))...))
if nsBytes == nil { if nsBytes == nil {
return true return true
} }
ns := std.Deserialize(nsBytes.([]byte)).(NameState) ns := std.Deserialize(nsBytes.([]byte)).(NameState)
return runtime.GetTime() >= ns.Expiration if now >= ns.Expiration {
return true
}
}
return false
} }
// Register registers new domain with the specified owner and name if it's available. // Register registers new domain with the specified owner and name if it's available.
@ -280,9 +285,24 @@ func Register(name string, owner interop.Hash160, email string, refresh, retry,
if fragments == nil { if fragments == nil {
panic("invalid domain name format") panic("invalid domain name format")
} }
l := len(fragments)
tldKey := append([]byte{prefixRoot}, []byte(fragments[l-1])...)
ctx := storage.GetContext() ctx := storage.GetContext()
if storage.Get(ctx, append([]byte{prefixRoot}, []byte(fragments[1])...)) == nil { tldBytes := storage.Get(ctx, tldKey)
panic("root not found") if l == 1 {
checkCommittee()
if tldBytes != nil {
panic("TLD already exists")
}
storage.Put(ctx, tldKey, 0)
} else {
if tldBytes == nil {
panic("TLD not found")
}
if parentExpired(ctx, 1, fragments) {
panic("one of the parent domains has expired")
}
} }
if !isValid(owner) { if !isValid(owner) {
@ -701,9 +721,6 @@ func splitAndCheck(name string, allowMultipleFragments bool) []string {
} }
fragments := std.StringSplit(name, ".") fragments := std.StringSplit(name, ".")
l = len(fragments) l = len(fragments)
if l < 2 {
return nil
}
if l > 2 && !allowMultipleFragments { if l > 2 && !allowMultipleFragments {
return nil return nil
} }
@ -832,6 +849,9 @@ func tokenIDFromName(name string) string {
panic("invalid domain name format") panic("invalid domain name format")
} }
l := len(fragments) l := len(fragments)
if l == 1 {
return name
}
return name[len(name)-(len(fragments[l-1])+len(fragments[l-2])+1):] return name[len(name)-(len(fragments[l-1])+len(fragments[l-2])+1):]
} }

View file

@ -3,6 +3,7 @@ package tests
import ( import (
"fmt" "fmt"
"math/big" "math/big"
"strings"
"testing" "testing"
"time" "time"
@ -29,37 +30,48 @@ func TestNNSGeneric(t *testing.T) {
CheckTestInvoke(t, bc, tx, 0) CheckTestInvoke(t, bc, tx, 0)
} }
func TestNNSAddRoot(t *testing.T) { func TestNNSRegisterTLD(t *testing.T) {
bc := NewChain(t) bc := NewChain(t)
h := DeployContract(t, bc, nnsPath, nil) h := DeployContract(t, bc, nnsPath, nil)
tx := PrepareInvoke(t, bc, CommitteeAcc, h, "addRoot", "0com") refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
tx := PrepareInvoke(t, bc, CommitteeAcc, h, "register",
"0com", CommitteeAcc.Contract.ScriptHash(),
"email@nspcc.ru", refresh, retry, expire, ttl)
AddBlock(t, bc, tx) AddBlock(t, bc, tx)
CheckFault(t, bc, tx.Hash(), "invalid root format") CheckFault(t, bc, tx.Hash(), "invalid domain name format")
acc := NewAccount(t, bc) acc := NewAccount(t, bc)
tx = PrepareInvoke(t, bc, acc, h, "addRoot", "com") tx = PrepareInvoke(t, bc, acc, h, "register",
"com", acc.Contract.ScriptHash(),
"email@nspcc.ru", refresh, retry, expire, ttl)
AddBlock(t, bc, tx) AddBlock(t, bc, tx)
CheckFault(t, bc, tx.Hash(), "not witnessed by committee") CheckFault(t, bc, tx.Hash(), "not witnessed by committee")
tx = PrepareInvoke(t, bc, CommitteeAcc, h, "addRoot", "com") tx = PrepareInvoke(t, bc, CommitteeAcc, h, "register",
"com", CommitteeAcc.Contract.ScriptHash(),
"email@nspcc.ru", refresh, retry, expire, ttl)
AddBlock(t, bc, tx) AddBlock(t, bc, tx)
CheckHalt(t, bc, tx.Hash()) CheckHalt(t, bc, tx.Hash())
tx = PrepareInvoke(t, bc, CommitteeAcc, h, "addRoot", "com") tx = PrepareInvoke(t, bc, CommitteeAcc, h, "register",
"com", CommitteeAcc.Contract.ScriptHash(),
"email@nspcc.ru", refresh, retry, expire, ttl)
AddBlock(t, bc, tx) AddBlock(t, bc, tx)
CheckFault(t, bc, tx.Hash(), "root already exists") CheckFault(t, bc, tx.Hash(), "TLD already exists")
} }
func TestNNSRegister(t *testing.T) { func TestNNSRegister(t *testing.T) {
bc := NewChain(t) bc := NewChain(t)
h := DeployContract(t, bc, nnsPath, nil) h := DeployContract(t, bc, nnsPath, nil)
tx := PrepareInvoke(t, bc, CommitteeAcc, h, "addRoot", "com") refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
tx := PrepareInvoke(t, bc, CommitteeAcc, h, "register",
"com", CommitteeAcc.Contract.ScriptHash(),
"myemail@nspcc.ru", refresh, retry, expire, ttl)
AddBlockCheckHalt(t, bc, tx) AddBlockCheckHalt(t, bc, tx)
acc := NewAccount(t, bc) acc := NewAccount(t, bc)
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
tx = PrepareInvoke(t, bc, []*wallet.Account{CommitteeAcc, acc}, h, "register", tx = PrepareInvoke(t, bc, []*wallet.Account{CommitteeAcc, acc}, h, "register",
"testdomain.com", acc.Contract.ScriptHash(), "testdomain.com", acc.Contract.ScriptHash(),
"myemail@nspcc.ru", refresh, retry, expire, ttl) "myemail@nspcc.ru", refresh, retry, expire, ttl)
@ -97,10 +109,12 @@ func TestNNSUpdateSOA(t *testing.T) {
bc := NewChain(t) bc := NewChain(t)
h := DeployContract(t, bc, nnsPath, nil) h := DeployContract(t, bc, nnsPath, nil)
tx := PrepareInvoke(t, bc, CommitteeAcc, h, "addRoot", "com") refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
tx := PrepareInvoke(t, bc, CommitteeAcc, h, "register",
"com", CommitteeAcc.Contract.ScriptHash(),
"myemail@nspcc.ru", refresh, retry, expire, ttl)
AddBlockCheckHalt(t, bc, tx) AddBlockCheckHalt(t, bc, tx)
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
tx = PrepareInvoke(t, bc, CommitteeAcc, h, "register", tx = PrepareInvoke(t, bc, CommitteeAcc, h, "register",
"testdomain.com", CommitteeAcc.Contract.ScriptHash(), "testdomain.com", CommitteeAcc.Contract.ScriptHash(),
"myemail@nspcc.ru", refresh, retry, expire, ttl) "myemail@nspcc.ru", refresh, retry, expire, ttl)
@ -124,10 +138,12 @@ func TestNNSGetAllRecords(t *testing.T) {
bc := NewChain(t) bc := NewChain(t)
h := DeployContract(t, bc, nnsPath, nil) h := DeployContract(t, bc, nnsPath, nil)
tx := PrepareInvoke(t, bc, CommitteeAcc, h, "addRoot", "com") refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
tx := PrepareInvoke(t, bc, CommitteeAcc, h, "register",
"com", CommitteeAcc.Contract.ScriptHash(),
"myemail@nspcc.ru", refresh, retry, expire, ttl)
AddBlockCheckHalt(t, bc, tx) AddBlockCheckHalt(t, bc, tx)
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
tx = PrepareInvoke(t, bc, CommitteeAcc, h, "register", tx = PrepareInvoke(t, bc, CommitteeAcc, h, "register",
"testdomain.com", CommitteeAcc.Contract.ScriptHash(), "testdomain.com", CommitteeAcc.Contract.ScriptHash(),
"myemail@nspcc.ru", refresh, retry, expire, ttl) "myemail@nspcc.ru", refresh, retry, expire, ttl)
@ -173,10 +189,12 @@ func TestNNSSetAdmin(t *testing.T) {
bc := NewChain(t) bc := NewChain(t)
h := DeployContract(t, bc, nnsPath, nil) h := DeployContract(t, bc, nnsPath, nil)
tx := PrepareInvoke(t, bc, CommitteeAcc, h, "addRoot", "com") refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
tx := PrepareInvoke(t, bc, CommitteeAcc, h, "register",
"com", CommitteeAcc.Contract.ScriptHash(),
"myemail@nspcc.ru", refresh, retry, expire, ttl)
AddBlockCheckHalt(t, bc, tx) AddBlockCheckHalt(t, bc, tx)
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
tx = PrepareInvoke(t, bc, CommitteeAcc, h, "register", tx = PrepareInvoke(t, bc, CommitteeAcc, h, "register",
"testdomain.com", CommitteeAcc.Contract.ScriptHash(), "testdomain.com", CommitteeAcc.Contract.ScriptHash(),
"myemail@nspcc.ru", refresh, retry, expire, ttl) "myemail@nspcc.ru", refresh, retry, expire, ttl)
@ -198,15 +216,51 @@ func TestNNSSetAdmin(t *testing.T) {
AddBlockCheckHalt(t, bc, tx) AddBlockCheckHalt(t, bc, tx)
} }
func TestNNSIsAvailable(t *testing.T) {
bc := NewChain(t)
h := DeployContract(t, bc, nnsPath, nil)
tx := PrepareInvoke(t, bc, CommitteeAcc, h, "isAvailable", "com")
CheckTestInvoke(t, bc, tx, true)
tx = PrepareInvoke(t, bc, CommitteeAcc, h, "isAvailable", "domain.com")
_, err := TestInvoke(bc, tx)
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), "TLD not found"))
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
tx = PrepareInvoke(t, bc, CommitteeAcc, h, "register",
"com", CommitteeAcc.Contract.ScriptHash(),
"myemail@nspcc.ru", refresh, retry, expire, ttl)
AddBlockCheckHalt(t, bc, tx)
tx = PrepareInvoke(t, bc, CommitteeAcc, h, "isAvailable", "com")
CheckTestInvoke(t, bc, tx, false)
tx = PrepareInvoke(t, bc, CommitteeAcc, h, "isAvailable", "domain.com")
CheckTestInvoke(t, bc, tx, true)
acc := NewAccount(t, bc)
tx = PrepareInvoke(t, bc, []*wallet.Account{CommitteeAcc, acc}, h, "register",
"domain.com", acc.Contract.ScriptHash(),
"myemail@nspcc.ru", refresh, retry, expire, ttl)
AddBlockCheckHalt(t, bc, tx)
tx = PrepareInvoke(t, bc, CommitteeAcc, h, "isAvailable", "domain.com")
CheckTestInvoke(t, bc, tx, false)
}
func TestNNSRenew(t *testing.T) { func TestNNSRenew(t *testing.T) {
bc := NewChain(t) bc := NewChain(t)
h := DeployContract(t, bc, nnsPath, nil) h := DeployContract(t, bc, nnsPath, nil)
tx := PrepareInvoke(t, bc, CommitteeAcc, h, "addRoot", "com") refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
tx := PrepareInvoke(t, bc, CommitteeAcc, h, "register",
"com", CommitteeAcc.Contract.ScriptHash(),
"myemail@nspcc.ru", refresh, retry, expire, ttl)
AddBlockCheckHalt(t, bc, tx) AddBlockCheckHalt(t, bc, tx)
acc := NewAccount(t, bc) acc := NewAccount(t, bc)
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
tx = PrepareInvoke(t, bc, []*wallet.Account{CommitteeAcc, acc}, h, "register", tx = PrepareInvoke(t, bc, []*wallet.Account{CommitteeAcc, acc}, h, "register",
"testdomain.com", acc.Contract.ScriptHash(), "testdomain.com", acc.Contract.ScriptHash(),
"myemail@nspcc.ru", refresh, retry, expire, ttl) "myemail@nspcc.ru", refresh, retry, expire, ttl)