policy-engine/pkg/morph/policy/policy_contract_storage.go
aarifullin a7bb0e6b8b [#31] morph: Fix container resource name transformer
Signed-off-by: Airat Arifullin <aarifullin@yadro.com>
2023-12-12 17:51:54 +03:00

148 lines
4.1 KiB
Go

package policy
import (
"crypto/sha256"
"errors"
"fmt"
"math/big"
"strings"
"git.frostfs.info/TrueCloudLab/frostfs-contract/policy"
client "git.frostfs.info/TrueCloudLab/frostfs-contract/rpcclient/policy"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
"github.com/mr-tron/base58"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/wallet"
)
var (
ErrEmptyChainID = errors.New("chain id is not set")
ErrEngineTargetTypeUnsupported = errors.New("this target type is not supported yet")
)
// ContractStorage is the interface to manage chain rules within the policy contract.
type ContractStorage struct {
contractInterface *client.Contract
}
var _ engine.MorphRuleChainStorage = (*ContractStorage)(nil)
func NewContractStorage(actor client.Actor, contract util.Uint160) *ContractStorage {
return &ContractStorage{
contractInterface: client.New(actor, contract),
}
}
func NewContractStorageWithSimpleActor(rpcActor actor.RPCActor, acc *wallet.Account, contract util.Uint160) (*ContractStorage, error) {
act, err := actor.NewSimple(rpcActor, acc)
if err != nil {
return nil, fmt.Errorf("failed to create simple actor: %w", err)
}
return NewContractStorage(act, contract), nil
}
func transformNameIfContainer(target engine.Target) (name string) {
name = target.Name
if target.Type == engine.Container {
// Container name can be too long and, thus, cannot be
// used as a key name for policy-contract storage.
encoded := sha256.Sum224([]byte(target.Name))
name = base58.FastBase58Encoding(encoded[:])
}
return
}
func (s *ContractStorage) AddMorphRuleChain(name chain.Name, target engine.Target, c *chain.Chain) (txHash util.Uint256, vub uint32, err error) {
if c.ID == "" {
err = ErrEmptyChainID
return
}
var kind policy.Kind
kind, err = policyKind(target.Type)
if err != nil {
return
}
fullName := prefixedChainName(name, c.ID)
targetName := transformNameIfContainer(target)
txHash, vub, err = s.contractInterface.AddChain(big.NewInt(int64(kind)), targetName, fullName, c.Bytes())
return
}
func (s *ContractStorage) RemoveMorphRuleChain(name chain.Name, target engine.Target, chainID chain.ID) (txHash util.Uint256, vub uint32, err error) {
if chainID == "" {
err = ErrEmptyChainID
return
}
var kind policy.Kind
kind, err = policyKind(target.Type)
if err != nil {
return
}
fullName := prefixedChainName(name, chainID)
targetName := transformNameIfContainer(target)
txHash, vub, err = s.contractInterface.RemoveChain(big.NewInt(int64(kind)), targetName, fullName)
return
}
func (s *ContractStorage) ListMorphRuleChains(name chain.Name, target engine.Target) ([]*chain.Chain, error) {
kind, err := policyKind(target.Type)
if err != nil {
return nil, err
}
targetName := transformNameIfContainer(target)
items, err := s.contractInterface.ListChainsByPrefix(big.NewInt(int64(kind)), targetName, []byte(name))
if err != nil {
return nil, err
}
var chains []*chain.Chain
for _, item := range items {
serialized, err := bytesFromStackItem(item)
if err != nil {
return nil, err
}
c := new(chain.Chain)
if err := c.DecodeBytes(serialized); err != nil {
return nil, err
}
chains = append(chains, c)
}
return chains, nil
}
func bytesFromStackItem(param stackitem.Item) ([]byte, error) {
switch param.Type() {
case stackitem.BufferT, stackitem.ByteArrayT, stackitem.IntegerT:
return param.TryBytes()
case stackitem.AnyT:
if param.Value() == nil {
return nil, nil
}
fallthrough
default:
return nil, fmt.Errorf("chain/client: %s is not a byte array type", param.Type())
}
}
func prefixedChainName(name chain.Name, chainID chain.ID) []byte {
return []byte(strings.ToLower(fmt.Sprintf("%s:%s", name, chainID)))
}
func policyKind(typ engine.TargetType) (policy.Kind, error) {
if typ == engine.Namespace {
return policy.Namespace, nil
} else if typ == engine.Container {
return policy.Container, nil
}
return policy.Kind(0), ErrEngineTargetTypeUnsupported
}