Skip to content

Commit

Permalink
[#125] Support multiple records of the same type
Browse files Browse the repository at this point in the history
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
  • Loading branch information
KirillovDenis committed Oct 1, 2021
1 parent 5be532c commit 6ea4573
Showing 1 changed file with 49 additions and 27 deletions.
76 changes: 49 additions & 27 deletions nns/nns_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,29 +340,33 @@ func SetRecord(name string, typ RecordType, data string) {
putRecord(ctx, tokenID, name, typ, data)
}

// GetRecord returns domain record of the specified type if it exists or an empty
// GetRecords returns domain record of the specified type if it exists or an empty
// string if not.
func GetRecord(name string, typ RecordType) string {
func GetRecords(name string, typ RecordType) []string {
tokenID := []byte(tokenIDFromName(name))
ctx := storage.GetReadOnlyContext()
_ = getNameState(ctx, tokenID) // ensure not expired
return getRecord(ctx, tokenID, name, typ)
return getRecords(ctx, tokenID, name, typ)
}

// DeleteRecord removes domain record with the specified type.
func DeleteRecord(name string, typ RecordType) {
// DeleteRecords removes domain record with the specified type.
func DeleteRecords(name string, typ RecordType) {
tokenID := []byte(tokenIDFromName(name))
ctx := storage.GetContext()
ns := getNameState(ctx, tokenID)
ns.checkAdmin()
recordKey := getRecordKey(tokenID, name, typ)
storage.Delete(ctx, recordKey)
recordsKey := getRecordKey(tokenID, name, typ)
records := storage.Find(ctx, recordsKey, storage.KeysOnly)
for iterator.Next(records) {
r := iterator.Value(records).(string)
storage.Delete(ctx, r)
}
}

// Resolve resolves given name (not more then three redirects are allowed).
func Resolve(name string, typ RecordType) string {
func Resolve(name string, typ RecordType) []string {
ctx := storage.GetReadOnlyContext()
return resolve(ctx, name, typ, 2)
return resolve(ctx, nil, name, typ, 2)
}

// GetAllRecords returns an Iterator with RecordState items for given name.
Expand Down Expand Up @@ -455,20 +459,29 @@ func putNameStateWithKey(ctx storage.Context, tokenKey []byte, ns NameState) {
storage.Put(ctx, nameKey, nsBytes)
}

// getRecord returns domain record.
func getRecord(ctx storage.Context, tokenId []byte, name string, typ RecordType) string {
recordKey := getRecordKey(tokenId, name, typ)
recBytes := storage.Get(ctx, recordKey)
if recBytes == nil {
return recBytes.(string) // A hack to actually return NULL.
// getRecords returns domain record.
func getRecords(ctx storage.Context, tokenId []byte, name string, typ RecordType) []string {
recordsKey := getRecordKey(tokenId, name, typ)

var result []string
records := storage.Find(ctx, recordsKey, storage.DeserializeValues)
for iterator.Next(records) {
r := iterator.Value(records).(struct {
key string
rs RecordState
})
value := r.rs.Data
rTyp := r.key[len(r.key)-5]
if rTyp == byte(typ) {
result = append(result, value)
}
}
record := std.Deserialize(recBytes.([]byte)).(RecordState)
return record.Data
return result
}

// putRecord stores domain record.
func putRecord(ctx storage.Context, tokenId []byte, name string, typ RecordType, record string) {
recordKey := getRecordKey(tokenId, name, typ)
recordKey := addUniqueBytesToRecordKey(getRecordKey(tokenId, name, typ))
rs := RecordState{
Name: name,
Type: typ,
Expand All @@ -490,6 +503,13 @@ func getRecordKey(tokenId []byte, name string, typ RecordType) []byte {
return append(recordKey, []byte{byte(typ)}...)
}

// addUniqueBytesToRecordKey add to key 4 unique bytes to allow store several records with the same type.
func addUniqueBytesToRecordKey(recordKey []byte) []byte {
tx := runtime.GetScriptContainer()
bb := []byte(tx.Hash)
return append(recordKey, bb[:4]...)
}

// isValid returns true if the provided address is a valid Uint160.
func isValid(address interop.Hash160) bool {
return address != nil && len(address) == 20
Expand Down Expand Up @@ -684,35 +704,37 @@ func tokenIDFromName(name string) string {

// resolve resolves provided name using record with the specified type and given
// maximum redirections constraint.
func resolve(ctx storage.Context, name string, typ RecordType, redirect int) string {
func resolve(ctx storage.Context, res []string, name string, typ RecordType, redirect int) []string {
if redirect < 0 {
panic("invalid redirect")
}
records := getRecords(ctx, name)
records := getAllRecords(ctx, name)
cname := ""
for iterator.Next(records) {
r := iterator.Value(records).(struct {
key string
rs RecordState
})
value := r.rs.Data
rTyp := r.key[len(r.key)-1]
rTyp := r.key[len(r.key)-5]
if rTyp == byte(typ) {
return value
res = append(res, value)
}
if rTyp == byte(CNAME) {
cname = value
}
}
if cname == "" {
return string([]byte(nil))
if cname == "" || typ == CNAME {
return res
}
return resolve(ctx, cname, typ, redirect-1)

res = append(res, cname)
return resolve(ctx, res, cname, typ, redirect-1)
}

// getRecords returns iterator over the set of records corresponded with the
// getAllRecords returns iterator over the set of records corresponded with the
// specified name.
func getRecords(ctx storage.Context, name string) iterator.Iterator {
func getAllRecords(ctx storage.Context, name string) iterator.Iterator {
tokenID := []byte(tokenIDFromName(name))
_ = getNameState(ctx, tokenID)
recordsKey := getRecordsKey(tokenID, name)
Expand Down

0 comments on commit 6ea4573

Please sign in to comment.