[#125] Support multiple records of the same type
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
5be532cf57
commit
b412909f40
1 changed files with 49 additions and 27 deletions
|
@ -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)
|
|
||||||
|
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.
|
// 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)
|
||||||
|
|
Loading…
Reference in a new issue