policy: Shorten policy contract keys #71
2 changed files with 69 additions and 9 deletions
|
@ -6,6 +6,8 @@
|
|||
|------------------------------------------|--------|-----------------------------------|
|
||||
| 'c' + uint16(len(container)) + container | []byte | Namespace chain |
|
||||
| 'n' + uint16(len(namespace)) + namespace | []byte | Container chain |
|
||||
| 'm' + entity name (namespace/container) | []byte | Mapped name to an encoded number |
|
||||
| 'counter' | uint64 | Integer counter used for mapping |
|
||||
|
||||
*/
|
||||
|
||||
|
|
|
@ -22,6 +22,11 @@ const (
|
|||
ownerKeyPrefix = 'o'
|
||||
)
|
||||
|
||||
const (
|
||||
mappingKeyPrefix = 'm'
|
||||
counterKey = "counter"
|
||||
)
|
||||
|
||||
const (
|
||||
// ErrNotAuthorized is returned when the none of the transaction signers
|
||||
// belongs to the list of autorized keys.
|
||||
|
@ -44,6 +49,7 @@ func _deploy(data any, isUpdate bool) {
|
|||
}
|
||||
storage.Put(ctx, []byte{ownerKeyPrefix}, args.Admin)
|
||||
}
|
||||
storage.Put(ctx, counterKey, 0)
|
||||
}
|
||||
|
||||
func checkAuthorization(ctx storage.Context) {
|
||||
|
@ -79,24 +85,61 @@ func getAdmin(ctx storage.Context) interop.Hash160 {
|
|||
return storage.Get(ctx, []byte{ownerKeyPrefix}).(interop.Hash160)
|
||||
}
|
||||
|
||||
func storageKey(prefix Kind, entityName string, name []byte) []byte {
|
||||
ln := len(entityName)
|
||||
key := append([]byte{byte(prefix)}, byte(ln&0xFF), byte(ln>>8))
|
||||
key = append(key, entityName...)
|
||||
func storageKey(prefix Kind, counter int, name []byte) []byte {
|
||||
key := append([]byte{byte(prefix)}, common.ToFixedWidth64(counter)...)
|
||||
return append(key, name...)
|
||||
}
|
||||
dkirillov marked this conversation as resolved
|
||||
|
||||
// mapToNumeric maps a name to a number. That allows to keep more space in
|
||||
// a storage key shortening long names. Short entity
|
||||
// names are also mapped to prevent collisions in the map.
|
||||
func mapToNumeric(ctx storage.Context, name []byte) (mapped int, mappingExists bool) {
|
||||
mKey := append([]byte{mappingKeyPrefix}, name...)
|
||||
numericID := storage.Get(ctx, mKey)
|
||||
if numericID == nil {
|
||||
return
|
||||
}
|
||||
mapped = numericID.(int)
|
||||
mappingExists = true
|
||||
return
|
||||
}
|
||||
|
||||
// mapToNumericCreateIfNotExists maps a name to a number. That allows to keep
|
||||
// more space in a storage key shortening long names. Short entity
|
||||
// names are also mapped to prevent collisions in the map.
|
||||
// If a mapping cannot be found, then the method creates and returns it.
|
||||
// mapToNumericCreateIfNotExists is NOT applicable for a read-only context.
|
||||
func mapToNumericCreateIfNotExists(ctx storage.Context, name []byte) int {
|
||||
mKey := append([]byte{mappingKeyPrefix}, name...)
|
||||
numericID := storage.Get(ctx, mKey)
|
||||
if numericID == nil {
|
||||
counter := storage.Get(ctx, counterKey).(int)
|
||||
counter++
|
||||
storage.Put(ctx, counterKey, counter)
|
||||
storage.Put(ctx, mKey, counter)
|
||||
return counter
|
||||
}
|
||||
return numericID.(int)
|
||||
}
|
||||
|
||||
func AddChain(entity Kind, entityName string, name []byte, chain []byte) {
|
||||
ctx := storage.GetContext()
|
||||
checkAuthorization(ctx)
|
||||
|
||||
key := storageKey(entity, entityName, name)
|
||||
entityNameBytes := mapToNumericCreateIfNotExists(ctx, []byte(entityName))
|
||||
key := storageKey(entity, entityNameBytes, name)
|
||||
storage.Put(ctx, key, chain)
|
||||
}
|
||||
|
||||
func GetChain(entity Kind, entityName string, name []byte) []byte {
|
||||
ctx := storage.GetReadOnlyContext()
|
||||
key := storageKey(entity, entityName, name)
|
||||
|
||||
entityNameBytes, exists := mapToNumeric(ctx, []byte(entityName))
|
||||
if !exists {
|
||||
panic("not found")
|
||||
}
|
||||
|
||||
key := storageKey(entity, entityNameBytes, name)
|
||||
data := storage.Get(ctx, key).([]byte)
|
||||
if data == nil {
|
||||
panic("not found")
|
||||
|
@ -109,7 +152,12 @@ func RemoveChain(entity Kind, entityName string, name []byte) {
|
|||
ctx := storage.GetContext()
|
||||
checkAuthorization(ctx)
|
||||
|
||||
key := storageKey(entity, entityName, name)
|
||||
entityNameBytes, exists := mapToNumeric(ctx, []byte(entityName))
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
|
||||
key := storageKey(entity, entityNameBytes, name)
|
||||
storage.Delete(ctx, key)
|
||||
}
|
||||
|
||||
|
@ -117,7 +165,12 @@ func RemoveChainsByPrefix(entity Kind, entityName string, name []byte) {
|
|||
ctx := storage.GetContext()
|
||||
checkAuthorization(ctx)
|
||||
|
||||
key := storageKey(entity, entityName, name)
|
||||
entityNameBytes, exists := mapToNumeric(ctx, []byte(entityName))
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
|
||||
key := storageKey(entity, entityNameBytes, name)
|
||||
it := storage.Find(ctx, key, storage.KeysOnly)
|
||||
for iterator.Next(it) {
|
||||
storage.Delete(ctx, iterator.Value(it).([]byte))
|
||||
|
@ -142,7 +195,12 @@ func ListChainsByPrefix(entity Kind, entityName string, prefix []byte) [][]byte
|
|||
|
||||
result := [][]byte{}
|
||||
|
||||
keyPrefix := storageKey(entity, entityName, prefix)
|
||||
entityNameBytes, exists := mapToNumeric(ctx, []byte(entityName))
|
||||
if !exists {
|
||||
return result
|
||||
}
|
||||
|
||||
keyPrefix := storageKey(entity, entityNameBytes, prefix)
|
||||
it := storage.Find(ctx, keyPrefix, storage.ValuesOnly)
|
||||
for iterator.Next(it) {
|
||||
result = append(result, iterator.Value(it).([]byte))
|
||||
|
|
Loading…
Reference in a new issue
Shouldn't we simplify this to
key := append([]byte{byte(prefix)}, entityName...)
Appending
entityName
's length wasn't idea of mine (it has been introduced by @fyrchik), but I think it is fine because it helps to determine where a chain ID starts in a storage key.entityName
now always 8 bytes long. So we can save two bytes here if I understand it correctlyAlright. I've removed this append