[#125] Support multiple records of the same type

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
enable-notary-in-public-chains
Denis Kirillov 2021-09-28 11:54:55 +03:00 committed by Alex Vanin
parent 5be532cf57
commit b412909f40
1 changed files with 49 additions and 27 deletions

View File

@ -340,29 +340,33 @@ func SetRecord(name string, typ RecordType, data string) {
putRecord(ctx, tokenID, name, typ, data) 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. // string if not.
func GetRecord(name string, typ RecordType) string { func GetRecords(name string, typ RecordType) []string {
tokenID := []byte(tokenIDFromName(name)) tokenID := []byte(tokenIDFromName(name))
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
_ = getNameState(ctx, tokenID) // ensure not expired _ = 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. // DeleteRecords removes domain record with the specified type.
func DeleteRecord(name string, typ RecordType) { func DeleteRecords(name string, typ RecordType) {
tokenID := []byte(tokenIDFromName(name)) tokenID := []byte(tokenIDFromName(name))
ctx := storage.GetContext() ctx := storage.GetContext()
ns := getNameState(ctx, tokenID) ns := getNameState(ctx, tokenID)
ns.checkAdmin() ns.checkAdmin()
recordKey := getRecordKey(tokenID, name, typ) recordsKey := getRecordKey(tokenID, name, typ)
storage.Delete(ctx, recordKey) 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). // 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() 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. // GetAllRecords returns an Iterator with RecordState items for given name.
@ -455,20 +459,29 @@ func putNameStateWithKey(ctx storage.Context, tokenKey []byte, ns NameState) {
storage.Put(ctx, nameKey, nsBytes) storage.Put(ctx, nameKey, nsBytes)
} }
// getRecord returns domain record. // getRecords returns domain record.
func getRecord(ctx storage.Context, tokenId []byte, name string, typ RecordType) string { func getRecords(ctx storage.Context, tokenId []byte, name string, typ RecordType) []string {
recordKey := getRecordKey(tokenId, name, typ) recordsKey := getRecordKey(tokenId, name, typ)
recBytes := storage.Get(ctx, recordKey)
if recBytes == nil { var result []string
return recBytes.(string) // A hack to actually return NULL. 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. // putRecord stores domain record.
func putRecord(ctx storage.Context, tokenId []byte, name string, typ RecordType, record string) { 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{ rs := RecordState{
Name: name, Name: name,
Type: typ, Type: typ,
@ -490,6 +503,13 @@ func getRecordKey(tokenId []byte, name string, typ RecordType) []byte {
return append(recordKey, []byte{byte(typ)}...) 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. // isValid returns true if the provided address is a valid Uint160.
func isValid(address interop.Hash160) bool { func isValid(address interop.Hash160) bool {
return address != nil && len(address) == 20 return address != nil && len(address) == 20
@ -684,11 +704,11 @@ func tokenIDFromName(name string) string {
// resolve resolves provided name using record with the specified type and given // resolve resolves provided name using record with the specified type and given
// maximum redirections constraint. // 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 { if redirect < 0 {
panic("invalid redirect") panic("invalid redirect")
} }
records := getRecords(ctx, name) records := getAllRecords(ctx, name)
cname := "" cname := ""
for iterator.Next(records) { for iterator.Next(records) {
r := iterator.Value(records).(struct { r := iterator.Value(records).(struct {
@ -696,23 +716,25 @@ func resolve(ctx storage.Context, name string, typ RecordType, redirect int) str
rs RecordState rs RecordState
}) })
value := r.rs.Data value := r.rs.Data
rTyp := r.key[len(r.key)-1] rTyp := r.key[len(r.key)-5]
if rTyp == byte(typ) { if rTyp == byte(typ) {
return value res = append(res, value)
} }
if rTyp == byte(CNAME) { if rTyp == byte(CNAME) {
cname = value cname = value
} }
} }
if cname == "" { if cname == "" || typ == CNAME {
return string([]byte(nil)) return res
}
return resolve(ctx, cname, typ, redirect-1)
} }
// getRecords returns iterator over the set of records corresponded with the res = append(res, cname)
return resolve(ctx, res, cname, typ, redirect-1)
}
// getAllRecords returns iterator over the set of records corresponded with the
// specified name. // specified name.
func getRecords(ctx storage.Context, name string) iterator.Iterator { func getAllRecords(ctx storage.Context, name string) iterator.Iterator {
tokenID := []byte(tokenIDFromName(name)) tokenID := []byte(tokenIDFromName(name))
_ = getNameState(ctx, tokenID) _ = getNameState(ctx, tokenID)
recordsKey := getRecordsKey(tokenID, name) recordsKey := getRecordsKey(tokenID, name)