nns: support SOA records

Port 7f1ec13ae5
and f4762c1b56.
This commit is contained in:
Anna Shaleva 2022-09-15 22:27:02 +03:00
parent 8790602f69
commit ea934b8e30
4 changed files with 153 additions and 75 deletions

View file

@ -246,7 +246,7 @@ func parentExpired(ctx storage.Context, first int, fragments []string) bool {
} }
// 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.
func Register(name string, owner interop.Hash160) bool { func Register(name string, owner interop.Hash160, email string, refresh, retry, expire, ttl int) bool {
fragments := splitAndCheck(name, false) fragments := splitAndCheck(name, false)
if fragments == nil { if fragments == nil {
panic("invalid domain name format") panic("invalid domain name format")
@ -303,14 +303,62 @@ func Register(name string, owner interop.Hash160) bool {
ns := NameState{ ns := NameState{
Owner: owner, Owner: owner,
Name: name, Name: name,
Expiration: runtime.GetTime() + millisecondsInYear, Expiration: runtime.GetTime() + expire*1000,
} }
putNameStateWithKey(ctx, tokenKey, ns) putNameStateWithKey(ctx, tokenKey, ns)
putSoaRecord(ctx, name, email, refresh, retry, expire, ttl)
updateBalance(ctx, []byte(name), owner, +1) updateBalance(ctx, []byte(name), owner, +1)
postTransfer(oldOwner, owner, []byte(name), nil) postTransfer(oldOwner, owner, []byte(name), nil)
return true return true
} }
// UpdateSOA updates soa record.
func UpdateSOA(name, email string, refresh, retry, expire, ttl int) {
if len(name) > maxDomainNameLength {
panic("invalid domain name format")
}
ctx := storage.GetContext()
ns := getNameState(ctx, []byte(name))
ns.checkAdmin()
putSoaRecord(ctx, name, email, refresh, retry, expire, ttl)
}
// putSoaRecord stores SOA domain record.
func putSoaRecord(ctx storage.Context, name, email string, refresh, retry, expire, ttl int) {
data := name + " " + email + " " +
std.Itoa(runtime.GetTime(), 10) + " " +
std.Itoa(refresh, 10) + " " +
std.Itoa(retry, 10) + " " +
std.Itoa(expire, 10) + " " +
std.Itoa(ttl, 10)
tokenId := []byte(tokenIDFromName(name))
putRecord(ctx, tokenId, name, SOA, 0, data)
}
// updateSoaSerial updates serial of the corresponding SOA domain record.
func updateSoaSerial(ctx storage.Context, tokenId []byte) {
recordKey := getRecordKey(tokenId, string(tokenId), SOA, 0)
recBytes := storage.Get(ctx, recordKey)
if recBytes == nil {
panic("SOA record not found")
}
rec := std.Deserialize(recBytes.([]byte)).(RecordState)
split := std.StringSplitNonEmpty(rec.Data, " ")
if len(split) != 7 {
panic("corrupted SOA record format")
}
split[2] = std.Itoa(runtime.GetTime(), 10) // update serial
rec.Data = split[0] + " " + split[1] + " " +
split[2] + " " + split[3] + " " +
split[4] + " " + split[5] + " " +
split[6]
recBytes = std.Serialize(rec)
storage.Put(ctx, recordKey, recBytes)
}
// Renew increases domain expiration date. // Renew increases domain expiration date.
func Renew(name string) int { func Renew(name string) int {
if len(name) > maxDomainNameLength { if len(name) > maxDomainNameLength {
@ -351,6 +399,7 @@ func SetRecord(name string, typ RecordType, id byte, data string) {
panic("unknown record") panic("unknown record")
} }
putRecord(ctx, tokenID, name, typ, id, data) putRecord(ctx, tokenID, name, typ, id, data)
updateSoaSerial(ctx, tokenID)
} }
// AddRecord adds new record of the specified type to the provided domain. // AddRecord adds new record of the specified type to the provided domain.
@ -374,6 +423,7 @@ func AddRecord(name string, typ RecordType, data string) {
panic("multiple CNAME records") panic("multiple CNAME records")
} }
putRecord(ctx, tokenID, name, typ, id, data) putRecord(ctx, tokenID, name, typ, id, data)
updateSoaSerial(ctx, tokenID)
} }
// checkRecord performs record validness check and returns token ID. // checkRecord performs record validness check and returns token ID.
@ -411,6 +461,9 @@ func GetRecords(name string, typ RecordType) []string {
// DeleteRecords removes all domain records with the specified type. // DeleteRecords removes all domain records with the specified type.
func DeleteRecords(name string, typ RecordType) { func DeleteRecords(name string, typ RecordType) {
if typ == SOA {
panic("forbidden to delete SOA record")
}
tokenID := []byte(tokenIDFromName(name)) tokenID := []byte(tokenIDFromName(name))
ctx := storage.GetContext() ctx := storage.GetContext()
ns := getNameState(ctx, tokenID) ns := getNameState(ctx, tokenID)
@ -421,6 +474,7 @@ func DeleteRecords(name string, typ RecordType) {
key := iterator.Value(records).(string) key := iterator.Value(records).(string)
storage.Delete(ctx, key) storage.Delete(ctx, key)
} }
updateSoaSerial(ctx, tokenID)
} }
// Resolve resolves given name (not more than three redirects are allowed) to a set // Resolve resolves given name (not more than three redirects are allowed) to a set

View file

@ -17,13 +17,24 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func newNSClient(t *testing.T) *neotest.ContractInvoker { const (
millisecondsInYear = 365 * 24 * 3600 * 1000
maxDomainNameFragmentLength = 63
)
func newNSClient(t *testing.T, registerComTLD bool) *neotest.ContractInvoker {
bc, acc := chain.NewSingle(t) bc, acc := chain.NewSingle(t)
e := neotest.NewExecutor(t, bc, acc, acc) e := neotest.NewExecutor(t, bc, acc, acc)
c := neotest.CompileFile(t, e.CommitteeHash, ".", "nns.yml") ctr := neotest.CompileFile(t, e.CommitteeHash, ".", "nns.yml")
e.DeployContract(t, c, nil) e.DeployContract(t, ctr, nil)
return e.CommitteeInvoker(c.Hash) c := e.CommitteeInvoker(ctr.Hash)
if registerComTLD {
// Set expiration big enough to pass all tests.
mail, refresh, retry, expire, ttl := "sami@nspcc.ru", int64(101), int64(102), int64(millisecondsInYear/1000*100), int64(104)
c.Invoke(t, true, "register", "com", c.CommitteeHash, mail, refresh, retry, expire, ttl)
}
return c
} }
func TestNameService_Price(t *testing.T) { func TestNameService_Price(t *testing.T) {
@ -32,7 +43,7 @@ func TestNameService_Price(t *testing.T) {
maxPrice = int64(10000_00000000) maxPrice = int64(10000_00000000)
) )
c := newNSClient(t) c := newNSClient(t, false)
t.Run("set, not signed by committee", func(t *testing.T) { t.Run("set, not signed by committee", func(t *testing.T) {
acc := c.NewAccount(t) acc := c.NewAccount(t)
@ -65,7 +76,7 @@ func TestNameService_Price(t *testing.T) {
} }
func TestNonfungible(t *testing.T) { func TestNonfungible(t *testing.T) {
c := newNSClient(t) c := newNSClient(t, false)
c.Signers = []neotest.Signer{c.NewAccount(t)} c.Signers = []neotest.Signer{c.NewAccount(t)}
c.Invoke(t, "NNS", "symbol") c.Invoke(t, "NNS", "symbol")
@ -74,38 +85,39 @@ func TestNonfungible(t *testing.T) {
} }
func TestRegisterTLD(t *testing.T) { func TestRegisterTLD(t *testing.T) {
c := newNSClient(t) c := newNSClient(t, false)
mail, refresh, retry, expire, ttl := "sami@nspcc.ru", int64(101), int64(102), int64(millisecondsInYear/1000*100), int64(104)
t.Run("invalid format", func(t *testing.T) { t.Run("invalid format", func(t *testing.T) {
c.InvokeFail(t, "invalid domain name format", "register", "", c.CommitteeHash) c.InvokeFail(t, "invalid domain name format", "register", "", c.CommitteeHash, mail, refresh, retry, expire, ttl)
}) })
t.Run("not signed by committee", func(t *testing.T) { t.Run("not signed by committee", func(t *testing.T) {
acc := c.NewAccount(t) acc := c.NewAccount(t)
c := c.WithSigners(acc) c := c.WithSigners(acc)
c.InvokeFail(t, "not witnessed by committee", "register", "some", c.CommitteeHash) c.InvokeFail(t, "not witnessed by committee", "register", "some", c.CommitteeHash, mail, refresh, retry, expire, ttl)
}) })
c.Invoke(t, true, "register", "some", c.CommitteeHash) c.Invoke(t, true, "register", "some", c.CommitteeHash, mail, refresh, retry, expire, ttl)
t.Run("already exists", func(t *testing.T) { t.Run("already exists", func(t *testing.T) {
c.InvokeFail(t, "TLD already exists", "register", "some", c.CommitteeHash) c.InvokeFail(t, "TLD already exists", "register", "some", c.CommitteeHash, mail, refresh, retry, expire, ttl)
}) })
} }
func TestExpiration(t *testing.T) { func TestExpiration(t *testing.T) {
c := newNSClient(t) c := newNSClient(t, true)
e := c.Executor e := c.Executor
bc := e.Chain bc := e.Chain
mail, refresh, retry, expire, ttl := "sami@nspcc.ru", int64(101), int64(102), int64(millisecondsInYear/1000*100), int64(104)
acc := e.NewAccount(t) acc := e.NewAccount(t)
cAcc := c.WithSigners(acc) cAcc := c.WithSigners(acc)
cAccCommittee := c.WithSigners(acc, c.Committee) // acc + committee signers for ".com"'s subdomains registration cAccCommittee := c.WithSigners(acc, c.Committee) // acc + committee signers for ".com"'s subdomains registration
c.Invoke(t, true, "register", "com", c.CommitteeHash) cAccCommittee.Invoke(t, true, "register", "first.com", acc.ScriptHash(), mail, refresh, retry, expire, ttl)
cAccCommittee.Invoke(t, true, "register", "first.com", acc.ScriptHash())
cAcc.Invoke(t, stackitem.Null{}, "addRecord", "first.com", int64(nns.TXT), "sometext") cAcc.Invoke(t, stackitem.Null{}, "addRecord", "first.com", int64(nns.TXT), "sometext")
b1 := e.TopBlock(t) b1 := e.TopBlock(t)
tx := cAccCommittee.PrepareInvoke(t, "register", "second.com", acc.ScriptHash()) tx := cAccCommittee.PrepareInvoke(t, "register", "second.com", acc.ScriptHash(), mail, refresh, retry, expire, ttl)
b2 := e.NewUnsignedBlock(t, tx) b2 := e.NewUnsignedBlock(t, tx)
b2.Index = b1.Index + 1 b2.Index = b1.Index + 1
b2.PrevHash = b1.Hash() b2.PrevHash = b1.Hash()
@ -117,7 +129,7 @@ func TestExpiration(t *testing.T) {
b3 := e.NewUnsignedBlock(t, tx) b3 := e.NewUnsignedBlock(t, tx)
b3.Index = b2.Index + 1 b3.Index = b2.Index + 1
b3.PrevHash = b2.Hash() b3.PrevHash = b2.Hash()
b3.Timestamp = b1.Timestamp + (millisecondsInYear + 1) b3.Timestamp = b1.Timestamp + (uint64(expire)*1000 + 1)
require.NoError(t, bc.AddBlock(e.SignBlock(b3))) require.NoError(t, bc.AddBlock(e.SignBlock(b3)))
e.CheckHalt(t, tx.Hash(), stackitem.NewBool(true)) // "first.com" has been expired e.CheckHalt(t, tx.Hash(), stackitem.NewBool(true)) // "first.com" has been expired
@ -145,46 +157,42 @@ func TestExpiration(t *testing.T) {
// cAcc.Invoke(t, stackitem.Null{}, "resolve", "first.com", int64(nns.TXT)) // cAcc.Invoke(t, stackitem.Null{}, "resolve", "first.com", int64(nns.TXT))
} }
const (
millisecondsInYear = 365 * 24 * 3600 * 1000
maxDomainNameFragmentLength = 63
)
func TestRegisterAndRenew(t *testing.T) { func TestRegisterAndRenew(t *testing.T) {
c := newNSClient(t) c := newNSClient(t, false)
e := c.Executor e := c.Executor
mail, refresh, retry, expire, ttl := "sami@nspcc.ru", int64(101), int64(102), int64(millisecondsInYear/1000*100), int64(104)
c.InvokeFail(t, "TLD not found", "isAvailable", "neo.com") c.InvokeFail(t, "TLD not found", "isAvailable", "neo.com")
c.Invoke(t, true, "register", "org", c.CommitteeHash) c.Invoke(t, true, "register", "org", c.CommitteeHash, mail, refresh, retry, expire, ttl)
c.InvokeFail(t, "TLD not found", "isAvailable", "neo.com") c.InvokeFail(t, "TLD not found", "isAvailable", "neo.com")
c.Invoke(t, true, "register", "com", c.CommitteeHash) c.Invoke(t, true, "register", "com", c.CommitteeHash, mail, refresh, retry, expire, ttl)
c.Invoke(t, true, "isAvailable", "neo.com") c.Invoke(t, true, "isAvailable", "neo.com")
c.InvokeWithFeeFail(t, "GAS limit exceeded", defaultNameServiceSysfee, "register", "neo.org", e.CommitteeHash) c.InvokeWithFeeFail(t, "GAS limit exceeded", defaultNameServiceSysfee, "register", "neo.org", e.CommitteeHash, mail, refresh, retry, expire, ttl)
c.InvokeFail(t, "invalid domain name format", "register", "docs.neo.org", e.CommitteeHash) c.InvokeFail(t, "invalid domain name format", "register", "docs.neo.org", e.CommitteeHash, mail, refresh, retry, expire, ttl)
c.InvokeFail(t, "invalid domain name format", "register", "\nneo.com'", e.CommitteeHash) c.InvokeFail(t, "invalid domain name format", "register", "\nneo.com'", e.CommitteeHash, mail, refresh, retry, expire, ttl)
c.InvokeFail(t, "invalid domain name format", "register", "neo.com\n", e.CommitteeHash) c.InvokeFail(t, "invalid domain name format", "register", "neo.com\n", e.CommitteeHash, mail, refresh, retry, expire, ttl)
c.InvokeWithFeeFail(t, "GAS limit exceeded", defaultNameServiceSysfee, "register", "neo.org", e.CommitteeHash) c.InvokeWithFeeFail(t, "GAS limit exceeded", defaultNameServiceSysfee, "register", "neo.org", e.CommitteeHash, mail, refresh, retry, expire, ttl)
c.InvokeWithFeeFail(t, "GAS limit exceeded", defaultNameServiceDomainPrice, "register", "neo.com", e.CommitteeHash) c.InvokeWithFeeFail(t, "GAS limit exceeded", defaultNameServiceDomainPrice, "register", "neo.com", e.CommitteeHash, mail, refresh, retry, expire, ttl)
var maxLenFragment string var maxLenFragment string
for i := 0; i < maxDomainNameFragmentLength; i++ { for i := 0; i < maxDomainNameFragmentLength; i++ {
maxLenFragment += "q" maxLenFragment += "q"
} }
c.Invoke(t, true, "isAvailable", maxLenFragment+".com") c.Invoke(t, true, "isAvailable", maxLenFragment+".com")
c.Invoke(t, true, "register", maxLenFragment+".com", e.CommitteeHash) c.Invoke(t, true, "register", maxLenFragment+".com", e.CommitteeHash, mail, refresh, retry, expire, ttl)
c.InvokeFail(t, "invalid domain name format", "register", maxLenFragment+"q.com", e.CommitteeHash) c.InvokeFail(t, "invalid domain name format", "register", maxLenFragment+"q.com", e.CommitteeHash, mail, refresh, retry, expire, ttl)
c.Invoke(t, true, "isAvailable", "neo.com") c.Invoke(t, true, "isAvailable", "neo.com")
c.Invoke(t, 3, "balanceOf", e.CommitteeHash) // org, com, qqq...qqq.com c.Invoke(t, 3, "balanceOf", e.CommitteeHash) // org, com, qqq...qqq.com
c.Invoke(t, true, "register", "neo.com", e.CommitteeHash) c.Invoke(t, true, "register", "neo.com", e.CommitteeHash, mail, refresh, retry, expire, ttl)
topBlock := e.TopBlock(t) topBlock := e.TopBlock(t)
expectedExpiration := topBlock.Timestamp + millisecondsInYear expectedExpiration := topBlock.Timestamp + uint64(expire*1000)
c.Invoke(t, false, "register", "neo.com", e.CommitteeHash) c.Invoke(t, false, "register", "neo.com", e.CommitteeHash, mail, refresh, retry, expire, ttl)
c.Invoke(t, false, "isAvailable", "neo.com") c.Invoke(t, false, "isAvailable", "neo.com")
t.Run("domain names with hyphen", func(t *testing.T) { t.Run("domain names with hyphen", func(t *testing.T) {
c.InvokeFail(t, "invalid domain name format", "register", "-testdomain.com", e.CommitteeHash) c.InvokeFail(t, "invalid domain name format", "register", "-testdomain.com", e.CommitteeHash, mail, refresh, retry, expire, ttl)
c.InvokeFail(t, "invalid domain name format", "register", "testdomain-.com", e.CommitteeHash) c.InvokeFail(t, "invalid domain name format", "register", "testdomain-.com", e.CommitteeHash, mail, refresh, retry, expire, ttl)
c.Invoke(t, true, "register", "test-domain.com", e.CommitteeHash) c.Invoke(t, true, "register", "test-domain.com", e.CommitteeHash, mail, refresh, retry, expire, ttl)
}) })
props := stackitem.NewMap() props := stackitem.NewMap()
@ -211,17 +219,17 @@ func TestRegisterAndRenew(t *testing.T) {
} }
func TestSetAddGetRecord(t *testing.T) { func TestSetAddGetRecord(t *testing.T) {
c := newNSClient(t) c := newNSClient(t, true)
e := c.Executor e := c.Executor
mail, refresh, retry, expire, ttl := "sami@nspcc.ru", int64(101), int64(102), int64(millisecondsInYear/1000*100), int64(104)
acc := e.NewAccount(t) acc := e.NewAccount(t)
cAcc := c.WithSigners(acc) cAcc := c.WithSigners(acc)
c.Invoke(t, true, "register", "com", c.CommitteeHash)
t.Run("set before register", func(t *testing.T) { t.Run("set before register", func(t *testing.T) {
c.InvokeFail(t, "token not found", "addRecord", "neo.com", int64(nns.TXT), "sometext") c.InvokeFail(t, "token not found", "addRecord", "neo.com", int64(nns.TXT), "sometext")
}) })
c.Invoke(t, true, "register", "neo.com", e.CommitteeHash) c.Invoke(t, true, "register", "neo.com", e.CommitteeHash, mail, refresh, retry, expire, ttl)
t.Run("invalid parameters", func(t *testing.T) { t.Run("invalid parameters", func(t *testing.T) {
c.InvokeFail(t, "unsupported record type", "addRecord", "neo.com", int64(0xFF), "1.2.3.4") c.InvokeFail(t, "unsupported record type", "addRecord", "neo.com", int64(0xFF), "1.2.3.4")
c.InvokeFail(t, "invalid record", "addRecord", "neo.com", int64(nns.A), "not.an.ip.address") c.InvokeFail(t, "invalid record", "addRecord", "neo.com", int64(nns.A), "not.an.ip.address")
@ -333,8 +341,9 @@ func TestSetAddGetRecord(t *testing.T) {
} }
func TestSetAdmin(t *testing.T) { func TestSetAdmin(t *testing.T) {
c := newNSClient(t) c := newNSClient(t, true)
e := c.Executor e := c.Executor
mail, refresh, retry, expire, ttl := "sami@nspcc.ru", int64(101), int64(102), int64(millisecondsInYear/1000*100), int64(104)
owner := e.NewAccount(t) owner := e.NewAccount(t)
cOwner := c.WithSigners(owner) cOwner := c.WithSigners(owner)
@ -344,11 +353,9 @@ func TestSetAdmin(t *testing.T) {
guest := e.NewAccount(t) guest := e.NewAccount(t)
cGuest := c.WithSigners(guest) cGuest := c.WithSigners(guest)
c.Invoke(t, true, "register", "com", c.CommitteeHash) cOwner.InvokeFail(t, "not witnessed by admin", "register", "neo.com", owner.ScriptHash(), mail, refresh, retry, expire, ttl) // admin is committee
cOwnerCommittee.Invoke(t, true, "register", "neo.com", owner.ScriptHash(), mail, refresh, retry, expire, ttl)
cOwner.InvokeFail(t, "not witnessed by admin", "register", "neo.com", owner.ScriptHash()) // admin is committee expectedExpiration := e.TopBlock(t).Timestamp + uint64(expire)*1000
cOwnerCommittee.Invoke(t, true, "register", "neo.com", owner.ScriptHash())
expectedExpiration := e.TopBlock(t).Timestamp + millisecondsInYear
cGuest.InvokeFail(t, "not witnessed", "setAdmin", "neo.com", admin.ScriptHash()) cGuest.InvokeFail(t, "not witnessed", "setAdmin", "neo.com", admin.ScriptHash())
// Must be witnessed by both owner and admin. // Must be witnessed by both owner and admin.
@ -376,8 +383,9 @@ func TestSetAdmin(t *testing.T) {
} }
func TestTransfer(t *testing.T) { func TestTransfer(t *testing.T) {
c := newNSClient(t) c := newNSClient(t, true)
e := c.Executor e := c.Executor
mail, refresh, retry, expire, ttl := "sami@nspcc.ru", int64(101), int64(102), int64(millisecondsInYear/1000*100), int64(104)
from := e.NewAccount(t) from := e.NewAccount(t)
cFrom := c.WithSigners(from) cFrom := c.WithSigners(from)
@ -385,8 +393,7 @@ func TestTransfer(t *testing.T) {
to := e.NewAccount(t) to := e.NewAccount(t)
cTo := c.WithSigners(to) cTo := c.WithSigners(to)
c.Invoke(t, true, "register", "com", c.CommitteeHash) cFromCommittee.Invoke(t, true, "register", "neo.com", from.ScriptHash(), mail, refresh, retry, expire, ttl)
cFromCommittee.Invoke(t, true, "register", "neo.com", from.ScriptHash())
cFrom.Invoke(t, stackitem.Null{}, "addRecord", "neo.com", int64(nns.A), "1.2.3.4") cFrom.Invoke(t, stackitem.Null{}, "addRecord", "neo.com", int64(nns.A), "1.2.3.4")
cFrom.InvokeFail(t, "token not found", "transfer", to.ScriptHash(), "not.exists", nil) cFrom.InvokeFail(t, "token not found", "transfer", to.ScriptHash(), "not.exists", nil)
c.Invoke(t, false, "transfer", to.ScriptHash(), "neo.com", nil) c.Invoke(t, false, "transfer", to.ScriptHash(), "neo.com", nil)
@ -415,8 +422,9 @@ func TestTransfer(t *testing.T) {
} }
func TestTokensOf(t *testing.T) { func TestTokensOf(t *testing.T) {
c := newNSClient(t) c := newNSClient(t, false)
e := c.Executor e := c.Executor
mail, refresh, retry, expire, ttl := "sami@nspcc.ru", int64(101), int64(102), int64(millisecondsInYear/1000*100), int64(104)
acc1 := e.NewAccount(t) acc1 := e.NewAccount(t)
cAcc1Committee := c.WithSigners(acc1, c.Committee) cAcc1Committee := c.WithSigners(acc1, c.Committee)
@ -424,9 +432,9 @@ func TestTokensOf(t *testing.T) {
cAcc2Committee := c.WithSigners(acc2, c.Committee) cAcc2Committee := c.WithSigners(acc2, c.Committee)
tld := []byte("com") tld := []byte("com")
c.Invoke(t, true, "register", tld, c.CommitteeHash) c.Invoke(t, true, "register", tld, c.CommitteeHash, mail, refresh, retry, expire, ttl)
cAcc1Committee.Invoke(t, true, "register", "neo.com", acc1.ScriptHash()) cAcc1Committee.Invoke(t, true, "register", "neo.com", acc1.ScriptHash(), mail, refresh, retry, expire, ttl)
cAcc2Committee.Invoke(t, true, "register", "nspcc.com", acc2.ScriptHash()) cAcc2Committee.Invoke(t, true, "register", "nspcc.com", acc2.ScriptHash(), mail, refresh, retry, expire, ttl)
testTokensOf(t, c, tld, [][]byte{[]byte("neo.com")}, acc1.ScriptHash().BytesBE()) testTokensOf(t, c, tld, [][]byte{[]byte("neo.com")}, acc1.ScriptHash().BytesBE())
testTokensOf(t, c, tld, [][]byte{[]byte("nspcc.com")}, acc2.ScriptHash().BytesBE()) testTokensOf(t, c, tld, [][]byte{[]byte("nspcc.com")}, acc2.ScriptHash().BytesBE())
@ -461,23 +469,23 @@ func testTokensOf(t *testing.T, c *neotest.ContractInvoker, tld []byte, result [
} }
func TestResolve(t *testing.T) { func TestResolve(t *testing.T) {
c := newNSClient(t) c := newNSClient(t, true)
e := c.Executor e := c.Executor
mail, refresh, retry, expire, ttl := "sami@nspcc.ru", int64(101), int64(102), int64(millisecondsInYear/1000*100), int64(104)
acc := e.NewAccount(t) acc := e.NewAccount(t)
cAcc := c.WithSigners(acc) cAcc := c.WithSigners(acc)
cAccCommittee := c.WithSigners(acc, c.Committee) cAccCommittee := c.WithSigners(acc, c.Committee)
c.Invoke(t, true, "register", "com", c.CommitteeHash) cAccCommittee.Invoke(t, true, "register", "neo.com", acc.ScriptHash(), mail, refresh, retry, expire, ttl)
cAccCommittee.Invoke(t, true, "register", "neo.com", acc.ScriptHash())
cAcc.Invoke(t, stackitem.Null{}, "addRecord", "neo.com", int64(nns.A), "1.2.3.4") cAcc.Invoke(t, stackitem.Null{}, "addRecord", "neo.com", int64(nns.A), "1.2.3.4")
cAcc.Invoke(t, stackitem.Null{}, "addRecord", "neo.com", int64(nns.CNAME), "alias.com") cAcc.Invoke(t, stackitem.Null{}, "addRecord", "neo.com", int64(nns.CNAME), "alias.com")
cAccCommittee.Invoke(t, true, "register", "alias.com", acc.ScriptHash()) cAccCommittee.Invoke(t, true, "register", "alias.com", acc.ScriptHash(), mail, refresh, retry, expire, ttl)
cAcc.Invoke(t, stackitem.Null{}, "addRecord", "alias.com", int64(nns.TXT), "sometxt from alias1") cAcc.Invoke(t, stackitem.Null{}, "addRecord", "alias.com", int64(nns.TXT), "sometxt from alias1")
cAcc.Invoke(t, stackitem.Null{}, "addRecord", "alias.com", int64(nns.CNAME), "alias2.com") cAcc.Invoke(t, stackitem.Null{}, "addRecord", "alias.com", int64(nns.CNAME), "alias2.com")
cAccCommittee.Invoke(t, true, "register", "alias2.com", acc.ScriptHash()) cAccCommittee.Invoke(t, true, "register", "alias2.com", acc.ScriptHash(), mail, refresh, retry, expire, ttl)
cAcc.Invoke(t, stackitem.Null{}, "addRecord", "alias2.com", int64(nns.TXT), "sometxt from alias2") cAcc.Invoke(t, stackitem.Null{}, "addRecord", "alias2.com", int64(nns.TXT), "sometxt from alias2")
c.Invoke(t, stackitem.NewArray([]stackitem.Item{stackitem.Make("1.2.3.4")}), "resolve", "neo.com", int64(nns.A)) c.Invoke(t, stackitem.NewArray([]stackitem.Item{stackitem.Make("1.2.3.4")}), "resolve", "neo.com", int64(nns.A))
@ -494,22 +502,23 @@ func TestResolve(t *testing.T) {
} }
func TestGetAllRecords(t *testing.T) { func TestGetAllRecords(t *testing.T) {
c := newNSClient(t) c := newNSClient(t, true)
e := c.Executor e := c.Executor
mail, refresh, retry, expire, ttl := "sami@nspcc.ru", int64(101), int64(102), int64(millisecondsInYear/1000*100), int64(104)
acc := e.NewAccount(t) acc := e.NewAccount(t)
cAcc := c.WithSigners(acc) cAcc := c.WithSigners(acc)
cAccCommittee := c.WithSigners(acc, c.Committee) cAccCommittee := c.WithSigners(acc, c.Committee)
c.Invoke(t, true, "register", "com", c.CommitteeHash) cAccCommittee.Invoke(t, true, "register", "neo.com", acc.ScriptHash(), mail, refresh, retry, expire, ttl)
cAccCommittee.Invoke(t, true, "register", "neo.com", acc.ScriptHash())
cAcc.Invoke(t, stackitem.Null{}, "addRecord", "neo.com", int64(nns.A), "1.2.3.4") cAcc.Invoke(t, stackitem.Null{}, "addRecord", "neo.com", int64(nns.A), "1.2.3.4")
cAcc.Invoke(t, stackitem.Null{}, "addRecord", "neo.com", int64(nns.CNAME), "alias.com") cAcc.Invoke(t, stackitem.Null{}, "addRecord", "neo.com", int64(nns.CNAME), "alias.com")
cAcc.Invoke(t, stackitem.Null{}, "addRecord", "neo.com", int64(nns.TXT), "bla0") cAcc.Invoke(t, stackitem.Null{}, "addRecord", "neo.com", int64(nns.TXT), "bla0")
cAcc.Invoke(t, stackitem.Null{}, "setRecord", "neo.com", int64(nns.TXT), 0, "bla1") // overwrite cAcc.Invoke(t, stackitem.Null{}, "setRecord", "neo.com", int64(nns.TXT), 0, "bla1") // overwrite
time := e.TopBlock(t).Timestamp
// Add some arbitrary data. // Add some arbitrary data.
cAccCommittee.Invoke(t, true, "register", "alias.com", acc.ScriptHash()) cAccCommittee.Invoke(t, true, "register", "alias.com", acc.ScriptHash(), mail, refresh, retry, expire, ttl)
cAcc.Invoke(t, stackitem.Null{}, "addRecord", "alias.com", int64(nns.TXT), "sometxt") cAcc.Invoke(t, stackitem.Null{}, "addRecord", "alias.com", int64(nns.TXT), "sometxt")
script, err := smartcontract.CreateCallAndUnwrapIteratorScript(c.Hash, "getAllRecords", 10, "neo.com") script, err := smartcontract.CreateCallAndUnwrapIteratorScript(c.Hash, "getAllRecords", 10, "neo.com")
@ -528,6 +537,15 @@ func TestGetAllRecords(t *testing.T) {
stackitem.NewByteArray([]byte("alias.com")), stackitem.NewByteArray([]byte("alias.com")),
stackitem.NewBigInteger(big.NewInt(0)), stackitem.NewBigInteger(big.NewInt(0)),
}), }),
stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte("neo.com")),
stackitem.Make(nns.SOA),
stackitem.NewBuffer([]byte("neo.com" + " " + mail + " " +
strconv.Itoa(int(time)) + " " + strconv.Itoa(int(refresh)) + " " +
strconv.Itoa(int(retry)) + " " + strconv.Itoa(int(expire)) + " " +
strconv.Itoa(int(ttl)))),
stackitem.NewBigInteger(big.NewInt(0)),
}),
stackitem.NewStruct([]stackitem.Item{ stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte("neo.com")), stackitem.NewByteArray([]byte("neo.com")),
stackitem.Make(nns.TXT), stackitem.Make(nns.TXT),
@ -538,20 +556,20 @@ func TestGetAllRecords(t *testing.T) {
} }
func TestGetRecords(t *testing.T) { func TestGetRecords(t *testing.T) {
c := newNSClient(t) c := newNSClient(t, true)
e := c.Executor e := c.Executor
mail, refresh, retry, expire, ttl := "sami@nspcc.ru", int64(101), int64(102), int64(millisecondsInYear/1000*100), int64(104)
acc := e.NewAccount(t) acc := e.NewAccount(t)
cAcc := c.WithSigners(acc) cAcc := c.WithSigners(acc)
cAccCommittee := c.WithSigners(acc, c.Committee) cAccCommittee := c.WithSigners(acc, c.Committee)
c.Invoke(t, true, "register", "com", c.CommitteeHash) cAccCommittee.Invoke(t, true, "register", "neo.com", acc.ScriptHash(), mail, refresh, retry, expire, ttl)
cAccCommittee.Invoke(t, true, "register", "neo.com", acc.ScriptHash())
cAcc.Invoke(t, stackitem.Null{}, "addRecord", "neo.com", int64(nns.A), "1.2.3.4") cAcc.Invoke(t, stackitem.Null{}, "addRecord", "neo.com", int64(nns.A), "1.2.3.4")
cAcc.Invoke(t, stackitem.Null{}, "addRecord", "neo.com", int64(nns.CNAME), "alias.com") cAcc.Invoke(t, stackitem.Null{}, "addRecord", "neo.com", int64(nns.CNAME), "alias.com")
// Add some arbitrary data. // Add some arbitrary data.
cAccCommittee.Invoke(t, true, "register", "alias.com", acc.ScriptHash()) cAccCommittee.Invoke(t, true, "register", "alias.com", acc.ScriptHash(), mail, refresh, retry, expire, ttl)
cAcc.Invoke(t, stackitem.Null{}, "addRecord", "alias.com", int64(nns.TXT), "sometxt") cAcc.Invoke(t, stackitem.Null{}, "addRecord", "alias.com", int64(nns.TXT), "sometxt")
c.Invoke(t, stackitem.NewArray([]stackitem.Item{stackitem.Make("1.2.3.4")}), "getRecords", "neo.com", int64(nns.A)) c.Invoke(t, stackitem.NewArray([]stackitem.Item{stackitem.Make("1.2.3.4")}), "getRecords", "neo.com", int64(nns.A))
@ -560,11 +578,11 @@ func TestGetRecords(t *testing.T) {
} }
func TestNNSAddRecord(t *testing.T) { func TestNNSAddRecord(t *testing.T) {
c := newNSClient(t) c := newNSClient(t, true)
cAccCommittee := c.WithSigners(c.Committee) cAccCommittee := c.WithSigners(c.Committee)
mail, refresh, retry, expire, ttl := "sami@nspcc.ru", int64(101), int64(102), int64(millisecondsInYear/1000*100), int64(104)
c.Invoke(t, true, "register", "com", c.CommitteeHash) cAccCommittee.Invoke(t, true, "register", "neo.com", c.CommitteeHash, mail, refresh, retry, expire, ttl)
cAccCommittee.Invoke(t, true, "register", "neo.com", c.CommitteeHash)
for i := 0; i <= maxRecordID+1; i++ { for i := 0; i <= maxRecordID+1; i++ {
if i == maxRecordID+1 { if i == maxRecordID+1 {

View file

@ -9,6 +9,8 @@ const (
A RecordType = 1 A RecordType = 1
// CNAME represents canonical name record type. // CNAME represents canonical name record type.
CNAME RecordType = 5 CNAME RecordType = 5
// SOA represents start of authority record type.
SOA RecordType = 6
// TXT represents text record type. // TXT represents text record type.
TXT RecordType = 16 TXT RecordType = 16
) )

View file

@ -21,7 +21,10 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
const neoAmount = 99999000 const (
neoAmount = 99999000
millisecondsInYear = 365 * 24 * 3600 * 1000
)
// Init pushes some predefined set of transactions into the given chain, it needs a path to // Init pushes some predefined set of transactions into the given chain, it needs a path to
// the root project directory. // the root project directory.
@ -165,11 +168,12 @@ func Init(t *testing.T, rootpath string, e *neotest.Executor) {
e.Validator.ScriptHash(), e.Committee.ScriptHash(), 1000_00000000, nil) // block #12 e.Validator.ScriptHash(), e.Committee.ScriptHash(), 1000_00000000, nil) // block #12
// Block #13: add `.com` root to NNS. // Block #13: add `.com` root to NNS.
nsCommitteeInvoker.Invoke(t, true, "register", "com", nsCommitteeInvoker.CommitteeHash) // block #13 mail, refresh, retry, expire, ttl := "sami@nspcc.ru", int64(101), int64(102), int64(millisecondsInYear/1000*100), int64(104)
nsCommitteeInvoker.Invoke(t, true, "register", "com", nsCommitteeInvoker.CommitteeHash, mail, refresh, retry, expire, ttl) // block #13
// Block #14: register `neo.com` via NNS. // Block #14: register `neo.com` via NNS.
registerTxH := nsPriv0CommitteeInvoker.Invoke(t, true, "register", registerTxH := nsPriv0CommitteeInvoker.Invoke(t, true, "register",
"neo.com", priv0ScriptHash) // block #14 "neo.com", priv0ScriptHash, mail, refresh, retry, expire, ttl) // block #14
res := e.GetTxExecResult(t, registerTxH) res := e.GetTxExecResult(t, registerTxH)
require.Equal(t, 1, len(res.Events)) // transfer require.Equal(t, 1, len(res.Events)) // transfer
tokenID, err := res.Events[0].Item.Value().([]stackitem.Item)[3].TryBytes() tokenID, err := res.Events[0].Item.Value().([]stackitem.Item)[3].TryBytes()