Skip to content

Commit

Permalink
nns: Avoid double domain name fragmentation within one method
Browse files Browse the repository at this point in the history
After recent changes, some contract methods pre-calculate fragments of
the requested domain name to process TLDs. In most cases, these methods
call `getNameState` function in subsequent instructions which also
performs fragmentation. In order to avoid resource costs for a
duplicated action, an already calculated partition should be reused.

Add `getFragmentedNameState` function which allows to pass
pre-calculated fragments. The function is implemented with the
possibility of a direct call from `getNameState` while maintaining the
behavior of the latter.

Signed-off-by: Leonard Lyubich <leonard@morphbits.io>
  • Loading branch information
cthulhu-rider committed Jun 16, 2023
1 parent 7156e54 commit ff50af5
Showing 1 changed file with 18 additions and 19 deletions.
37 changes: 18 additions & 19 deletions nns/nns_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,28 +146,26 @@ func TotalSupply() int {
// OwnerOf returns the owner of the specified domain. The tokenID domain MUST
// NOT be a TLD.
func OwnerOf(tokenID []byte) interop.Hash160 {
// TODO: same done in getNameState, don't do twice
fragments := std.StringSplit(string(tokenID), ".")
if len(fragments) == 1 {
panic("token not found")
}

ctx := storage.GetReadOnlyContext()
ns := getNameState(ctx, tokenID)
ns := getFragmentedNameState(ctx, tokenID, fragments)
return ns.Owner
}

// Properties returns a domain name and an expiration date of the specified domain.
// The tokenID MUST NOT be a TLD.
func Properties(tokenID []byte) map[string]interface{} {
// TODO: same done in getNameState, don't do twice
fragments := std.StringSplit(string(tokenID), ".")
if len(fragments) == 1 {
panic("token not found")
}

ctx := storage.GetReadOnlyContext()
ns := getNameState(ctx, tokenID)
ns := getFragmentedNameState(ctx, tokenID, fragments)
return map[string]interface{}{
"name": ns.Name,
"expiration": ns.Expiration,
Expand Down Expand Up @@ -209,7 +207,6 @@ func Transfer(to interop.Hash160, tokenID []byte, data interface{}) bool {
panic(`invalid receiver`)
}

// TODO: same done in getNameState, don't do twice
fragments := std.StringSplit(string(tokenID), ".")
if len(fragments) == 1 {
panic("token not found")
Expand Down Expand Up @@ -449,7 +446,6 @@ func SetAdmin(name string, admin interop.Hash160) {
panic("invalid domain name format")
}

// TODO: same done in getNameState, don't do twice
fragments := std.StringSplit(name, ".")
if len(fragments) == 1 {
panic("token not found")
Expand All @@ -459,7 +455,7 @@ func SetAdmin(name string, admin interop.Hash160) {
panic("not witnessed by admin")
}
ctx := storage.GetContext()
ns := getNameState(ctx, []byte(name))
ns := getFragmentedNameState(ctx, []byte(name), fragments)
common.CheckOwnerWitness(ns.Owner)
ns.Admin = admin
putNameState(ctx, ns)
Expand All @@ -473,14 +469,13 @@ func SetRecord(name string, typ RecordType, id byte, data string) {
panic("invalid record data")
}

// TODO: same done in getNameState, don't do twice
fragments := std.StringSplit(name, ".")
if len(fragments) == 1 {
panic("token not found")
}

ctx := storage.GetContext()
ns := getNameState(ctx, tokenID)
ns := getFragmentedNameState(ctx, tokenID, fragments)
ns.checkAdmin()
putRecord(ctx, tokenID, name, typ, id, data)
updateSoaSerial(ctx, tokenID)
Expand Down Expand Up @@ -509,14 +504,13 @@ func AddRecord(name string, typ RecordType, data string) {
panic("invalid record data")
}

// TODO: same done in getNameState, don't do twice
fragments := std.StringSplit(string(tokenID), ".")
if len(fragments) == 1 {
panic("token not found")
}

ctx := storage.GetContext()
ns := getNameState(ctx, tokenID)
ns := getFragmentedNameState(ctx, tokenID, fragments)
ns.checkAdmin()
addRecord(ctx, tokenID, name, typ, data)
updateSoaSerial(ctx, tokenID)
Expand All @@ -525,15 +519,14 @@ func AddRecord(name string, typ RecordType, data string) {
// GetRecords returns domain record of the specified type if it exists or an empty
// string if not. The name MUST NOT be a TLD.
func GetRecords(name string, typ RecordType) []string {
// TODO: same done in getNameState, don't do twice
fragments := std.StringSplit(name, ".")
if len(fragments) == 1 {
panic("token not found")
}

tokenID := []byte(tokenIDFromName(name))
ctx := storage.GetReadOnlyContext()
_ = getNameState(ctx, tokenID) // ensure not expired
_ = getFragmentedNameState(ctx, tokenID, fragments) // ensure not expired
return getRecordsByType(ctx, tokenID, name, typ)
}

Expand All @@ -544,15 +537,14 @@ func DeleteRecords(name string, typ RecordType) {
panic("you cannot delete soa record")
}

// TODO: same done in getNameState, don't do twice
fragments := std.StringSplit(name, ".")
if len(fragments) == 1 {
panic("token not found")
}

tokenID := []byte(tokenIDFromName(name))
ctx := storage.GetContext()
ns := getNameState(ctx, tokenID)
ns := getFragmentedNameState(ctx, tokenID, fragments)
ns.checkAdmin()
recordsKey := getRecordsKeyByType(tokenID, name, typ)
records := storage.Find(ctx, recordsKey, storage.KeysOnly)
Expand All @@ -566,7 +558,6 @@ func DeleteRecords(name string, typ RecordType) {
// Resolve resolves given name (not more then three redirects are allowed).
// The name MUST NOT be a TLD.
func Resolve(name string, typ RecordType) []string {
// TODO: same done in getNameState, don't do twice
fragments := std.StringSplit(name, ".")
if len(fragments) == 1 {
panic("token not found")
Expand All @@ -579,15 +570,14 @@ func Resolve(name string, typ RecordType) []string {
// GetAllRecords returns an Iterator with RecordState items for the given name.
// The name MUST NOT be a TLD.
func GetAllRecords(name string) iterator.Iterator {
// TODO: same done in getNameState, don't do twice
fragments := std.StringSplit(name, ".")
if len(fragments) == 1 {
panic("token not found")
}

tokenID := []byte(tokenIDFromName(name))
ctx := storage.GetReadOnlyContext()
_ = getNameState(ctx, tokenID) // ensure not expired
_ = getFragmentedNameState(ctx, tokenID, fragments) // ensure not expired
recordsKey := getRecordsKey(tokenID, name)
return storage.Find(ctx, recordsKey, storage.ValuesOnly|storage.DeserializeValues)
}
Expand Down Expand Up @@ -644,9 +634,18 @@ func getTokenKey(tokenID []byte) []byte {

// getNameState returns domain name state by the specified tokenID.
func getNameState(ctx storage.Context, tokenID []byte) NameState {
return getFragmentedNameState(ctx, tokenID, nil)
}

// getFragmentedNameState returns domain name state by the specified tokenID.
// Optional fragments parameter allows to pass pre-calculated elements of the
// domain name path: if nil, getFragmentedNameState splits name on its own.
func getFragmentedNameState(ctx storage.Context, tokenID []byte, fragments []string) NameState {
tokenKey := getTokenKey(tokenID)
ns := getNameStateWithKey(ctx, tokenKey)
fragments := std.StringSplit(string(tokenID), ".")
if fragments == nil {
fragments = std.StringSplit(string(tokenID), ".")
}
if parentExpired(ctx, 1, fragments) {
panic("parent domain has expired")
}
Expand Down

0 comments on commit ff50af5

Please sign in to comment.