refactor: share LNS name normalization

This commit is contained in:
Virgil 2026-04-02 06:04:25 +00:00
parent f03fc28ae0
commit f987dde471
3 changed files with 80 additions and 91 deletions

68
internal/nameutil/name.go Normal file
View file

@ -0,0 +1,68 @@
// SPDX-License-Identifier: EUPL-1.2
package nameutil
import core "dappco.re/go/core"
// CatalogLabel converts string and byte inputs into a raw catalog label.
//
// It accepts the same input types as the public service helpers and preserves
// the bytes exactly, without applying canonical name suffix trimming.
func CatalogLabel(name any) (string, bool) {
switch v := name.(type) {
case string:
return v, true
case []byte:
return string(v), true
default:
return "", false
}
}
// Canonicalize normalizes string and byte inputs into a lowercased LNS label.
//
// The helper rejects non-ASCII input, lowercases ASCII letters, and strips the
// optional trailing `.lthn` or trailing dot suffix used by service callers.
func Canonicalize(name any) (string, bool) {
var value []byte
switch v := name.(type) {
case string:
value = []byte(v)
case []byte:
value = append([]byte(nil), v...)
default:
return "", false
}
if len(value) == 0 {
return "", false
}
for i := range value {
if value[i]&0x80 != 0 {
return "", false
}
if value[i] >= 'A' && value[i] <= 'Z' {
value[i] += 'a' - 'A'
}
}
str := string(value)
switch {
case core.HasSuffix(str, ".lthn."):
str = core.TrimSuffix(str, ".lthn.")
case core.HasSuffix(str, ".lthn"):
str = core.TrimSuffix(str, ".lthn")
case core.HasSuffix(str, "."):
str = core.TrimSuffix(str, ".")
}
if len(str) == 0 {
return "", false
}
return str, true
}

68
lns.go
View file

@ -14,6 +14,7 @@ package lns
import (
core "dappco.re/go/core"
"dappco.re/go/lns/internal/nameutil"
"dappco.re/go/lns/pkg/covenant"
"dappco.re/go/lns/pkg/primitives"
)
@ -136,7 +137,7 @@ func (s *Service) HashByBinary(name []byte) (primitives.Hash, error) {
// ResolveString returns the canonical hash for a .lthn name supplied as a string.
func (s *Service) ResolveString(name string) (primitives.Hash, error) {
normalized, ok := canonicalizeName(name)
normalized, ok := nameutil.Canonicalize(name)
if !ok {
return primitives.Hash{}, core.E("lns.Service.ResolveString", "invalid name", nil)
}
@ -146,7 +147,7 @@ func (s *Service) ResolveString(name string) (primitives.Hash, error) {
// ResolveBinary returns the canonical hash for a .lthn name supplied as bytes.
func (s *Service) ResolveBinary(name []byte) (primitives.Hash, error) {
normalized, ok := canonicalizeName(name)
normalized, ok := nameutil.Canonicalize(name)
if !ok {
return primitives.Hash{}, core.E("lns.Service.ResolveBinary", "invalid name", nil)
}
@ -212,7 +213,7 @@ func (s *Service) VerifyByName(name any) bool {
//
// ok := svc.VerifyString("example.lthn")
func (s *Service) VerifyString(name string) bool {
normalized, ok := canonicalizeName(name)
normalized, ok := nameutil.Canonicalize(name)
if !ok {
return false
}
@ -224,7 +225,7 @@ func (s *Service) VerifyString(name string) bool {
//
// ok := svc.VerifyBinary([]byte("example.lthn"))
func (s *Service) VerifyBinary(name []byte) bool {
normalized, ok := canonicalizeName(name)
normalized, ok := nameutil.Canonicalize(name)
if !ok {
return false
}
@ -270,7 +271,7 @@ func (s *Service) GetReserved(name any) (covenant.ReservedName, bool) {
// svc := c.Service("lns").(*lns.Service)
// item, ok := svc.GetReservedName("reserved")
func (s *Service) GetReservedName(name any) (covenant.ReservedName, bool) {
if label, ok := catalogLabel(name); ok {
if label, ok := nameutil.CatalogLabel(name); ok {
if item, ok := covenant.GetReservedName(label); ok {
return item, true
}
@ -433,7 +434,7 @@ func (s *Service) GetLocked(name any) (covenant.LockedName, bool) {
// svc := c.Service("lns").(*lns.Service)
// item, ok := svc.GetLockedName("nec")
func (s *Service) GetLockedName(name any) (covenant.LockedName, bool) {
if label, ok := catalogLabel(name); ok {
if label, ok := nameutil.CatalogLabel(name); ok {
if item, ok := covenant.GetLockedName(label); ok {
return item, true
}
@ -791,58 +792,3 @@ func (s *Service) LockedValues() []covenant.LockedName {
func (s *Service) GetLockedValues() []covenant.LockedName {
return s.LockedValues()
}
func catalogLabel(name any) (string, bool) {
switch v := name.(type) {
case string:
return v, true
case []byte:
return string(v), true
default:
return "", false
}
}
func canonicalizeName(name any) (string, bool) {
var value []byte
switch v := name.(type) {
case string:
value = []byte(v)
case []byte:
value = append([]byte(nil), v...)
default:
return "", false
}
if len(value) == 0 {
return "", false
}
for i := range value {
if value[i]&0x80 != 0 {
return "", false
}
if value[i] >= 'A' && value[i] <= 'Z' {
value[i] += 'a' - 'A'
}
}
str := string(value)
switch {
case core.HasSuffix(str, ".lthn."):
str = core.TrimSuffix(str, ".lthn.")
case core.HasSuffix(str, ".lthn"):
str = core.TrimSuffix(str, ".lthn")
case core.HasSuffix(str, "."):
str = core.TrimSuffix(str, ".")
}
if len(str) == 0 {
return "", false
}
return str, true
}

View file

@ -12,6 +12,7 @@ package dns
import (
core "dappco.re/go/core"
"dappco.re/go/lns/internal/nameutil"
"dappco.re/go/lns/pkg/covenant"
"dappco.re/go/lns/pkg/primitives"
)
@ -60,7 +61,7 @@ func WithCore(c *core.Core) ServiceOption {
// svc := dns.NewService()
// hash, err := svc.Resolve("example.lthn")
func (s *Service) Resolve(name any) (primitives.Hash, error) {
normalized, ok := canonicalizeName(name)
normalized, ok := nameutil.Canonicalize(name)
if !ok {
return primitives.Hash{}, core.E("dns.Service.Resolve", "invalid name type", nil)
}
@ -87,7 +88,7 @@ func (s *Service) Hash(name any) (primitives.Hash, error) {
// svc := dns.NewService()
// ok := svc.Verify("example.lthn")
func (s *Service) Verify(name any) bool {
normalized, ok := canonicalizeName(name)
normalized, ok := nameutil.Canonicalize(name)
if !ok {
return false
}
@ -150,7 +151,7 @@ func (s *Service) ResolveString(name string) (primitives.Hash, error) {
// svc := dns.NewService()
// hash, err := svc.ResolveBinary([]byte("example.lthn"))
func (s *Service) ResolveBinary(name []byte) (primitives.Hash, error) {
normalized, ok := canonicalizeName(name)
normalized, ok := nameutil.Canonicalize(name)
if !ok {
return primitives.Hash{}, core.E("dns.Service.ResolveBinary", "invalid name type", nil)
}
@ -188,7 +189,7 @@ func (s *Service) VerifyString(name string) bool {
// svc := dns.NewService()
// ok := svc.VerifyBinary([]byte("example.lthn"))
func (s *Service) VerifyBinary(name []byte) bool {
normalized, ok := canonicalizeName(name)
normalized, ok := nameutil.Canonicalize(name)
if !ok {
return false
}
@ -240,29 +241,3 @@ func (s *Service) HashByName(name any) (primitives.Hash, error) {
func (s *Service) VerifyByName(name any) bool {
return s.VerifyName(name)
}
func canonicalizeName(name any) (string, bool) {
var value string
switch v := name.(type) {
case string:
value = v
case []byte:
value = string(v)
default:
return "", false
}
value = core.Lower(value)
switch {
case core.HasSuffix(value, ".lthn."):
value = core.TrimSuffix(value, ".lthn.")
case core.HasSuffix(value, ".lthn"):
value = core.TrimSuffix(value, ".lthn")
case core.HasSuffix(value, "."):
value = core.TrimSuffix(value, ".")
}
return value, true
}