frostfs-contract/policy/policy_contract.go
Alexander Chuprov 93781f1149 [#66] policy: Support IteratorChainsByPrefix method
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-01-26 17:41:04 +03:00

158 lines
3.9 KiB
Go

package policy
import (
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
)
// Kind represents the object the chain is attached to.
// Currently only namespace and container are supported.
type Kind byte
const (
Namespace = 'n'
Container = 'c'
IAM = 'i'
)
const (
ownerKeyPrefix = 'o'
)
const (
// ErrNotAuthorized is returned when the none of the transaction signers
// belongs to the list of autorized keys.
ErrNotAuthorized = "none of the signers is authorized to change the contract"
)
// _deploy function sets up initial list of inner ring public keys.
func _deploy(data any, isUpdate bool) {
if isUpdate {
return
}
args := data.(struct {
Admin interop.Hash160
})
ctx := storage.GetContext()
if args.Admin != nil {
if len(args.Admin) != 20 {
panic("invaliad admin hash length")
}
storage.Put(ctx, []byte{ownerKeyPrefix}, args.Admin)
}
}
func checkAuthorization(ctx storage.Context) {
admin := getAdmin(ctx)
if admin != nil && runtime.CheckWitness(admin) {
return
}
if runtime.CheckWitness(common.AlphabetAddress()) {
return
}
panic(ErrNotAuthorized)
}
// Version returns the version of the contract.
func Version() int {
return common.Version
}
func SetAdmin(addr interop.Hash160) {
common.CheckAlphabetWitness()
ctx := storage.GetContext()
storage.Put(ctx, []byte{ownerKeyPrefix}, addr)
}
func GetAdmin() interop.Hash160 {
ctx := storage.GetReadOnlyContext()
return getAdmin(ctx)
}
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...)
return append(key, name...)
}
func AddChain(entity Kind, entityName string, name []byte, chain []byte) {
ctx := storage.GetContext()
checkAuthorization(ctx)
key := storageKey(entity, entityName, name)
storage.Put(ctx, key, chain)
}
func GetChain(entity Kind, entityName string, name []byte) []byte {
ctx := storage.GetReadOnlyContext()
key := storageKey(entity, entityName, name)
data := storage.Get(ctx, key).([]byte)
if data == nil {
panic("not found")
}
return data
}
func RemoveChain(entity Kind, entityName string, name []byte) {
ctx := storage.GetContext()
checkAuthorization(ctx)
key := storageKey(entity, entityName, name)
storage.Delete(ctx, key)
}
func RemoveChainsByPrefix(entity Kind, entityName string, name []byte) {
ctx := storage.GetContext()
checkAuthorization(ctx)
key := storageKey(entity, entityName, name)
it := storage.Find(ctx, key, storage.KeysOnly)
for iterator.Next(it) {
storage.Delete(ctx, iterator.Value(it).([]byte))
}
}
// ListChains lists all chains for the namespace by prefix.
// container may be empty.
func ListChains(namespace, container string, name []byte) [][]byte {
result := ListChainsByPrefix(Namespace, namespace, name)
if container != "" {
result = append(result, ListChainsByPrefix(Container, container, name)...)
}
return result
}
// ListChainsByPrefix list all chains for the provided kind and entity by prefix.
func ListChainsByPrefix(entity Kind, entityName string, prefix []byte) [][]byte {
ctx := storage.GetReadOnlyContext()
result := [][]byte{}
keyPrefix := storageKey(entity, entityName, prefix)
it := storage.Find(ctx, keyPrefix, storage.ValuesOnly)
for iterator.Next(it) {
result = append(result, iterator.Value(it).([]byte))
}
return result
}
func IteratorChainsByPrefix(entity Kind, entityName string, prefix []byte) iterator.Iterator {
ctx := storage.GetReadOnlyContext()
keyPrefix := storageKey(entity, entityName, prefix)
return storage.Find(ctx, keyPrefix, storage.ValuesOnly)
}