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 |
|
| 'c' + uint16(len(container)) + container | []byte | Namespace chain |
|
||||||
| 'n' + uint16(len(namespace)) + namespace | []byte | Container 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'
|
ownerKeyPrefix = 'o'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
mappingKeyPrefix = 'm'
|
||||||
|
counterKey = "counter"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// ErrNotAuthorized is returned when the none of the transaction signers
|
// ErrNotAuthorized is returned when the none of the transaction signers
|
||||||
// belongs to the list of autorized keys.
|
// 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, []byte{ownerKeyPrefix}, args.Admin)
|
||||||
}
|
}
|
||||||
|
storage.Put(ctx, counterKey, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkAuthorization(ctx storage.Context) {
|
func checkAuthorization(ctx storage.Context) {
|
||||||
|
@ -79,24 +85,61 @@ func getAdmin(ctx storage.Context) interop.Hash160 {
|
||||||
return storage.Get(ctx, []byte{ownerKeyPrefix}).(interop.Hash160)
|
return storage.Get(ctx, []byte{ownerKeyPrefix}).(interop.Hash160)
|
||||||
}
|
}
|
||||||
|
|
||||||
func storageKey(prefix Kind, entityName string, name []byte) []byte {
|
func storageKey(prefix Kind, counter int, name []byte) []byte {
|
||||||
ln := len(entityName)
|
key := append([]byte{byte(prefix)}, common.ToFixedWidth64(counter)...)
|
||||||
key := append([]byte{byte(prefix)}, byte(ln&0xFF), byte(ln>>8))
|
|
||||||
key = append(key, entityName...)
|
|
||||||
return append(key, name...)
|
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) {
|
func AddChain(entity Kind, entityName string, name []byte, chain []byte) {
|
||||||
ctx := storage.GetContext()
|
ctx := storage.GetContext()
|
||||||
checkAuthorization(ctx)
|
checkAuthorization(ctx)
|
||||||
|
|
||||||
key := storageKey(entity, entityName, name)
|
entityNameBytes := mapToNumericCreateIfNotExists(ctx, []byte(entityName))
|
||||||
|
key := storageKey(entity, entityNameBytes, name)
|
||||||
storage.Put(ctx, key, chain)
|
storage.Put(ctx, key, chain)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetChain(entity Kind, entityName string, name []byte) []byte {
|
func GetChain(entity Kind, entityName string, name []byte) []byte {
|
||||||
ctx := storage.GetReadOnlyContext()
|
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)
|
data := storage.Get(ctx, key).([]byte)
|
||||||
if data == nil {
|
if data == nil {
|
||||||
panic("not found")
|
panic("not found")
|
||||||
|
@ -109,7 +152,12 @@ func RemoveChain(entity Kind, entityName string, name []byte) {
|
||||||
ctx := storage.GetContext()
|
ctx := storage.GetContext()
|
||||||
checkAuthorization(ctx)
|
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)
|
storage.Delete(ctx, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +165,12 @@ func RemoveChainsByPrefix(entity Kind, entityName string, name []byte) {
|
||||||
ctx := storage.GetContext()
|
ctx := storage.GetContext()
|
||||||
checkAuthorization(ctx)
|
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)
|
it := storage.Find(ctx, key, storage.KeysOnly)
|
||||||
for iterator.Next(it) {
|
for iterator.Next(it) {
|
||||||
storage.Delete(ctx, iterator.Value(it).([]byte))
|
storage.Delete(ctx, iterator.Value(it).([]byte))
|
||||||
|
@ -142,7 +195,12 @@ func ListChainsByPrefix(entity Kind, entityName string, prefix []byte) [][]byte
|
||||||
|
|
||||||
result := [][]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)
|
it := storage.Find(ctx, keyPrefix, storage.ValuesOnly)
|
||||||
for iterator.Next(it) {
|
for iterator.Next(it) {
|
||||||
result = append(result, iterator.Value(it).([]byte))
|
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