From 49e5270f673e228b6b0dc33e1ff41ea7ef7e08b6 Mon Sep 17 00:00:00 2001 From: Ekaterina Lebedeva Date: Tue, 18 Jun 2024 16:50:09 +0300 Subject: [PATCH] [#92] nns: Mention domain in panic messages Signed-off-by: Ekaterina Lebedeva --- nns/nns_contract.go | 44 +++++++++++++++++++------------------------- tests/nns_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/nns/nns_contract.go b/nns/nns_contract.go index 38e2ae9..2eb53c3 100644 --- a/nns/nns_contract.go +++ b/nns/nns_contract.go @@ -65,6 +65,8 @@ const ( defaultRegisterPrice = 10_0000_0000 // millisecondsInYear is amount of milliseconds per year. millisecondsInYear = int64(365 * 24 * 3600 * 1000) + // errInvalidDomainName is an error message for invalid domain name format. + errInvalidDomainName = "invalid domain name format" ) // RecordState is a type that registered entities are saved to. @@ -220,9 +222,6 @@ func GetPrice() int { // IsAvailable checks whether the provided domain name is available. func IsAvailable(name string) bool { fragments := splitAndCheck(name) - if fragments == nil { - panic("invalid domain name format") - } ctx := storage.GetReadOnlyContext() l := len(fragments) if storage.Get(ctx, append([]byte{prefixRoot}, []byte(fragments[l-1])...)) == nil { @@ -267,10 +266,6 @@ func parentExpired(ctx storage.Context, fragments []string) string { // Register registers a new domain with the specified owner and name if it's available. func Register(name string, owner interop.Hash160, email string, refresh, retry, expire, ttl int) bool { fragments := splitAndCheck(name) - if fragments == nil { - panic("invalid domain name format") - } - l := len(fragments) tldKey := append([]byte{prefixRoot}, []byte(fragments[l-1])...) ctx := storage.GetContext() @@ -339,9 +334,7 @@ func Register(name string, owner interop.Hash160, email string, refresh, retry, // Renew increases domain expiration date. func Renew(name string) int64 { - if len(name) > maxDomainNameLength { - panic("invalid domain name format") - } + checkDomainNameLength(name) runtime.BurnGas(GetPrice()) ctx := storage.GetContext() ns := getNameState(ctx, []byte(name)) @@ -353,9 +346,7 @@ func Renew(name string) int64 { // UpdateSOA updates soa record. func UpdateSOA(name, email string, refresh, retry, expire, ttl int) { - if len(name) > maxDomainNameLength { - panic("invalid domain name format") - } + checkDomainNameLength(name) ctx := storage.GetContext() ns := getNameState(ctx, []byte(name)) ns.checkAdmin() @@ -364,9 +355,7 @@ func UpdateSOA(name, email string, refresh, retry, expire, ttl int) { // SetAdmin updates domain admin. func SetAdmin(name string, admin interop.Hash160) { - if len(name) > maxDomainNameLength { - panic("invalid domain name format") - } + checkDomainNameLength(name) if admin != nil && !runtime.CheckWitness(admin) { panic("not witnessed by admin") } @@ -722,20 +711,28 @@ func isAlNum(c uint8) bool { // splitAndCheck splits domain name into parts and validates it. func splitAndCheck(name string) []string { - l := len(name) - if l < minDomainNameLength || maxDomainNameLength < l { - return nil - } + checkDomainNameLength(name) fragments := std.StringSplit(name, ".") - l = len(fragments) + l := len(fragments) for i := 0; i < l; i++ { if !checkFragment(fragments[i], i == l-1) { - return nil + panic(errInvalidDomainName + " '" + name + "': invalid fragment '" + fragments[i] + "'") } } return fragments } +// checkDomainNameLength panics if domain name length is out of boundaries. +func checkDomainNameLength(name string) { + l := len(name) + if l > maxDomainNameLength { + panic(errInvalidDomainName + " '" + name + "': domain name too long: got = " + std.Itoa(l, 10) + ", max = " + std.Itoa(maxDomainNameLength, 10)) + } + if l < minDomainNameLength { + panic(errInvalidDomainName + " '" + name + "': domain name too short: got = " + std.Itoa(l, 10) + ", min = " + std.Itoa(minDomainNameLength, 10)) + } +} + // checkIPv4 checks record on IPv4 compliance. func checkIPv4(data string) bool { l := len(data) @@ -849,9 +846,6 @@ func checkIPv6(data string) bool { // tokenIDFromName returns token ID (domain.root) from the provided name. func tokenIDFromName(name string) string { fragments := splitAndCheck(name) - if fragments == nil { - panic("invalid domain name format") - } ctx := storage.GetReadOnlyContext() sum := 0 diff --git a/tests/nns_test.go b/tests/nns_test.go index 9cd16aa..c27d0a1 100644 --- a/tests/nns_test.go +++ b/tests/nns_test.go @@ -54,6 +54,9 @@ func TestNNSRegisterTLD(t *testing.T) { c.InvokeFail(t, "invalid domain name format", "register", "0com", c.CommitteeHash, "email@frostfs.info", refresh, retry, expire, ttl) + c.InvokeFail(t, "invalid fragment '0com'", "register", + "0com", c.CommitteeHash, + "email@frostfs.info", refresh, retry, expire, ttl) acc := c.NewAccount(t) cAcc := c.WithSigners(acc) @@ -69,6 +72,12 @@ func TestNNSRegisterTLD(t *testing.T) { c.InvokeFail(t, "invalid domain name format", "register", "x", c.CommitteeHash, "email@frostfs.info", refresh, retry, expire, ttl) + c.InvokeFail(t, "domain name too short", "register", + "x", c.CommitteeHash, + "email@frostfs.info", refresh, retry, expire, ttl) + c.InvokeFail(t, "domain name too long", "register", + getTooLongDomainName(255), c.CommitteeHash, + "email@frostfs.info", refresh, retry, expire, ttl) }) c.Invoke(t, true, "register", @@ -101,9 +110,17 @@ func TestNNSRegister(t *testing.T) { c3.InvokeFail(t, "invalid domain name format", "register", "-testdomain.com", acc.ScriptHash(), "myemail@frostfs.info", refresh, retry, expire, ttl) + c3.InvokeFail(t, "invalid fragment '-testdomain'", "register", + "-testdomain.com", acc.ScriptHash(), + "myemail@frostfs.info", refresh, retry, expire, ttl) + c3.InvokeFail(t, "invalid domain name format", "register", "testdomain-.com", acc.ScriptHash(), "myemail@frostfs.info", refresh, retry, expire, ttl) + c3.InvokeFail(t, "invalid fragment 'testdomain-'", "register", + "testdomain-.com", acc.ScriptHash(), + "myemail@frostfs.info", refresh, retry, expire, ttl) + c3.Invoke(t, true, "register", "test-domain.com", acc.ScriptHash(), "myemail@frostfs.info", refresh, retry, expire, ttl) @@ -358,6 +375,8 @@ func TestNNSIsAvailable(t *testing.T) { "myemail@frostfs.info", refresh, retry, expire, ttl) c.Invoke(t, false, "isAvailable", "dom.domain.com") c.Invoke(t, true, "isAvailable", "dom.dom.domain.com") + + c.InvokeFail(t, "domain name too long", "isAvailable", getTooLongDomainName(255)) } func TestNNSRenew(t *testing.T) { @@ -382,6 +401,7 @@ func TestNNSRenew(t *testing.T) { {Key: stackitem.Make("expiration"), Value: stackitem.Make(ts)}, }) cAcc.Invoke(t, expected, "properties", "testdomain.com") + c.InvokeFail(t, "domain name too long", "renew", getTooLongDomainName(255)) } func TestNNSResolve(t *testing.T) { @@ -431,3 +451,11 @@ func TestNNSAndProxy(t *testing.T) { checkBalance(t, c.CommitteeHash, 1) }) } + +func getTooLongDomainName(max int) (res string) { + for len(res) < max { + res += "dom." + } + res += "com" + return res +}