[#306] Reduce number of policy contract invocations

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
pull/310/head
Denis Kirillov 2024-02-13 11:50:11 +03:00
parent 499a202d28
commit c452d58ce2
11 changed files with 235 additions and 159 deletions

View File

@ -27,7 +27,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
engineiam "git.frostfs.info/TrueCloudLab/policy-engine/iam"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"go.uber.org/zap"
)
@ -439,13 +438,9 @@ func (h *handler) putBucketACLAPEHandler(w http.ResponseWriter, r *http.Request,
}
chainRules := bucketCannedACLToAPERules(cannedACL, reqInfo, key, bktInfo.CID)
target := engine.NamespaceTarget(reqInfo.Namespace)
for _, chainPolicy := range chainRules {
if err = h.ape.AddChain(target, chainPolicy); err != nil {
h.logAndSendError(w, "failed to add morph rule chain", reqInfo, err, zap.String("chain_id", string(chainPolicy.ID)))
return
}
if err = h.ape.SaveACLChains(reqInfo.Namespace, chainRules); err != nil {
h.logAndSendError(w, "failed to add morph rule chains", reqInfo, err)
return
}
settings.CannedACL = cannedACL
@ -654,7 +649,7 @@ func (h *handler) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request)
return
}
jsonPolicy, err := h.ape.GetPolicy(reqInfo.Namespace, bktInfo.CID)
jsonPolicy, err := h.ape.GetBucketPolicy(reqInfo.Namespace, bktInfo.CID)
if err != nil {
if strings.Contains(err.Error(), "not found") {
err = fmt.Errorf("%w: %s", errors.GetAPIError(errors.ErrNoSuchBucketPolicy), err.Error())
@ -680,14 +675,7 @@ func (h *handler) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Reque
return
}
target := engine.NamespaceTarget(reqInfo.Namespace)
chainID := getBucketChainID(bktInfo)
if err = h.ape.RemoveChain(target, chainID); err != nil {
h.logAndSendError(w, "failed to remove morph rule chain", reqInfo, err)
return
}
if err = h.ape.DeletePolicy(reqInfo.Namespace, bktInfo.CID); err != nil {
if err = h.ape.DeleteBucketPolicy(reqInfo.Namespace, bktInfo.CID, getBucketChainID(bktInfo)); err != nil {
h.logAndSendError(w, "failed to delete policy from storage", reqInfo, err)
return
}
@ -743,14 +731,8 @@ func (h *handler) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request)
}
}
target := engine.NamespaceTarget(reqInfo.Namespace)
if err = h.ape.AddChain(target, s3Chain); err != nil {
h.logAndSendError(w, "failed to add morph rule chain", reqInfo, err)
return
}
if err = h.ape.PutPolicy(reqInfo.Namespace, bktInfo.CID, jsonPolicy); err != nil {
h.logAndSendError(w, "failed to save policy to storage", reqInfo, err)
if err = h.ape.PutBucketPolicy(reqInfo.Namespace, bktInfo.CID, jsonPolicy, s3Chain); err != nil {
h.logAndSendError(w, "failed to update policy in contract", reqInfo, err)
return
}
}

View File

@ -1325,7 +1325,7 @@ func TestPutBucketAPE(t *testing.T) {
_, err := hc.tp.ContainerEACL(hc.Context(), info.BktInfo.CID)
require.ErrorContains(t, err, "not found")
chains, err := hc.h.ape.ListChains(engine.NamespaceTarget(""))
chains, err := hc.h.ape.(*apeMock).ListChains(engine.NamespaceTarget(""))
require.NoError(t, err)
require.Len(t, chains, 2)
}

View File

@ -15,7 +15,6 @@ import (
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
"go.uber.org/zap"
)
@ -55,23 +54,10 @@ type (
// APE is Access Policy Engine that needs to save policy and acl info to different places.
APE interface {
MorphRuleChainStorage
PolicyStorage
}
// MorphRuleChainStorage is a similar to engine.MorphRuleChainStorage
// but doesn't know anything about tx.
MorphRuleChainStorage interface {
AddChain(target engine.Target, c *chain.Chain) error
RemoveChain(target engine.Target, chainID chain.ID) error
ListChains(target engine.Target) ([]*chain.Chain, error)
}
// PolicyStorage is interface to save intact initial user provided policy.
PolicyStorage interface {
PutPolicy(namespace string, cnrID cid.ID, policy []byte) error
GetPolicy(namespace string, cnrID cid.ID) ([]byte, error)
DeletePolicy(namespace string, cnrID cid.ID) error
PutBucketPolicy(ns string, cnrID cid.ID, policy []byte, s3Chain *chain.Chain) error
DeleteBucketPolicy(ns string, cnrID cid.ID, chainID chain.ID) error
GetBucketPolicy(ns string, cnrID cid.ID) ([]byte, error)
SaveACLChains(ns string, chains []*chain.Chain) error
}
frostfsIDDisabled struct{}

View File

@ -251,8 +251,27 @@ func (a *apeMock) PutPolicy(namespace string, cnrID cid.ID, policy []byte) error
return nil
}
func (a *apeMock) GetPolicy(namespace string, cnrID cid.ID) ([]byte, error) {
policy, ok := a.policyMap[namespace+cnrID.EncodeToString()]
func (a *apeMock) DeletePolicy(namespace string, cnrID cid.ID) error {
delete(a.policyMap, namespace+cnrID.EncodeToString())
return nil
}
func (a *apeMock) PutBucketPolicy(ns string, cnrID cid.ID, policy []byte, s3Chain *chain.Chain) error {
if err := a.PutPolicy(ns, cnrID, policy); err != nil {
return err
}
return a.AddChain(engine.NamespaceTarget(ns), s3Chain)
}
func (a *apeMock) DeleteBucketPolicy(ns string, cnrID cid.ID, chainID chain.ID) error {
if err := a.DeletePolicy(ns, cnrID); err != nil {
return err
}
return a.RemoveChain(engine.NamespaceTarget(ns), chainID)
}
func (a *apeMock) GetBucketPolicy(ns string, cnrID cid.ID) ([]byte, error) {
policy, ok := a.policyMap[ns+cnrID.EncodeToString()]
if !ok {
return nil, errors.New("not found")
}
@ -260,8 +279,13 @@ func (a *apeMock) GetPolicy(namespace string, cnrID cid.ID) ([]byte, error) {
return policy, nil
}
func (a *apeMock) DeletePolicy(namespace string, cnrID cid.ID) error {
delete(a.policyMap, namespace+cnrID.EncodeToString())
func (a *apeMock) SaveACLChains(ns string, chains []*chain.Chain) error {
for i := range chains {
if err := a.AddChain(engine.NamespaceTarget(ns), chains[i]); err != nil {
return err
}
}
return nil
}

View File

@ -29,7 +29,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
"git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
"git.frostfs.info/TrueCloudLab/policy-engine/schema/s3"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
@ -822,14 +821,10 @@ func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
}
h.reqLogger(ctx).Info(logs.BucketIsCreated, zap.Stringer("container_id", bktInfo.CID))
chainRules := bucketCannedACLToAPERules(cannedACL, reqInfo, key, bktInfo.CID)
target := engine.NamespaceTarget(reqInfo.Namespace)
for _, chainPolicy := range chainRules {
if err = h.ape.AddChain(target, chainPolicy); err != nil {
h.logAndSendError(w, "failed to add morph rule chain", reqInfo, err, zap.String("chain_id", string(chainPolicy.ID)))
return
}
chains := bucketCannedACLToAPERules(cannedACL, reqInfo, key, bktInfo.CID)
if err = h.ape.SaveACLChains(reqInfo.Namespace, chains); err != nil {
h.logAndSendError(w, "failed to add morph rule chain", reqInfo, err)
return
}
sp := &layer.PutSettingsParams{

View File

@ -2,9 +2,11 @@ package contract
import (
"context"
"errors"
"fmt"
"math/big"
"git.frostfs.info/TrueCloudLab/frostfs-contract/commonclient"
policycontract "git.frostfs.info/TrueCloudLab/frostfs-contract/policy"
policyclient "git.frostfs.info/TrueCloudLab/frostfs-contract/rpcclient/policy"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/policy"
@ -21,6 +23,7 @@ import (
type Client struct {
actor *actor.Actor
policyContract *policyclient.Contract
contractHash util.Uint160
}
type Config struct {
@ -72,6 +75,7 @@ func New(ctx context.Context, cfg Config) (*Client, error) {
return &Client{
actor: act,
policyContract: policyclient.New(act, contractHash),
contractHash: contractHash,
}, nil
}
@ -130,3 +134,87 @@ func (c *Client) Wait(tx util.Uint256, vub uint32, err error) error {
_, err = c.actor.Wait(tx, vub, err)
return err
}
type multiTX struct {
contractHash util.Uint160
txs []*commonclient.Transaction
err error
}
func (m *multiTX) AddChain(entity policycontract.Kind, entityName string, name []byte, chain []byte) {
m.wrapCall("addChain", []any{big.NewInt(int64(entity)), entityName, name, chain})
}
func (m *multiTX) RemoveChain(entity policycontract.Kind, entityName string, name []byte) {
m.wrapCall("removeChain", []any{big.NewInt(int64(entity)), entityName, name})
}
func (m *multiTX) Scripts() ([][]byte, error) {
if m.err != nil {
return nil, m.err
}
if len(m.txs) == 0 {
return nil, errors.New("tx isn't initialized")
}
res := make([][]byte, 0, len(m.txs))
for _, tx := range m.txs {
script, err := tx.Bytes()
if err != nil {
return nil, err
}
res = append(res, script)
}
return res, nil
}
func (m *multiTX) wrapCall(method string, args []any) {
if m.err != nil {
return
}
if len(m.txs) == 0 {
m.err = errors.New("multi tx isn't initialized")
return
}
err := m.txs[len(m.txs)-1].WrapCall(method, args)
if err == nil {
return
}
if !errors.Is(commonclient.ErrTransactionTooLarge, err) {
m.err = err
return
}
tx := commonclient.NewTransaction(m.contractHash)
m.err = tx.WrapCall(method, args)
if m.err == nil {
m.txs = append(m.txs, tx)
}
}
func (c *Client) StartTx() policy.MultiTransaction {
return &multiTX{
txs: []*commonclient.Transaction{commonclient.NewTransaction(c.contractHash)},
contractHash: c.contractHash,
}
}
func (c *Client) SendTx(mtx policy.MultiTransaction) error {
var err error
scripts, err := mtx.Scripts()
if err != nil {
return err
}
for i := range scripts {
if _, err = c.actor.Wait(c.actor.SendRun(scripts[i])); err != nil {
return err
}
}
return nil
}

View File

@ -83,6 +83,17 @@ func (c *InMemoryContract) Wait(_ util.Uint256, _ uint32, err error) error {
return err
}
func (c *InMemoryContract) StartTx() policy.MultiTransaction {
return &inMemoryTx{operations: make([]func(*InMemoryContract), 0)}
}
func (c *InMemoryContract) SendTx(tx policy.MultiTransaction) error {
for _, operation := range tx.(*inMemoryTx).operations {
operation(c)
}
return nil
}
func (c *InMemoryContract) getMap(kind policycontract.Kind) *syncedMap {
switch kind {
case policycontract.IAM:
@ -95,3 +106,23 @@ func (c *InMemoryContract) getMap(kind policycontract.Kind) *syncedMap {
return &syncedMap{data: map[string][]byte{}}
}
}
type inMemoryTx struct {
operations []func(contract *InMemoryContract)
}
func (t *inMemoryTx) AddChain(kind policycontract.Kind, entity string, name []byte, chain []byte) {
t.operations = append(t.operations, func(c *InMemoryContract) {
_, _, _ = c.AddChain(kind, entity, name, chain)
})
}
func (t *inMemoryTx) RemoveChain(kind policycontract.Kind, entity string, name []byte) {
t.operations = append(t.operations, func(c *InMemoryContract) {
_, _, _ = c.RemoveChain(kind, entity, name)
})
}
func (t *inMemoryTx) Scripts() ([][]byte, error) {
return nil, nil
}

View File

@ -1,46 +0,0 @@
package policy
import (
policycontract "git.frostfs.info/TrueCloudLab/frostfs-contract/policy"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/handler"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"go.uber.org/zap"
)
type MorphPolicyStorage struct {
contract Contract
}
type MorphPolicyStorageConfig struct {
Contract Contract
Log *zap.Logger
}
var _ handler.PolicyStorage = (*MorphPolicyStorage)(nil)
const policyStoragePrefix = 'b'
func NewMorphPolicyStorage(config *MorphPolicyStorageConfig) *MorphPolicyStorage {
return &MorphPolicyStorage{
contract: config.Contract,
}
}
func (c *MorphPolicyStorage) PutPolicy(namespace string, cnrID cid.ID, policy []byte) error {
name := getPolicyStorageName(cnrID)
return c.contract.Wait(c.contract.AddChain(policycontract.IAM, namespace, name, policy))
}
func (c *MorphPolicyStorage) GetPolicy(namespace string, cnrID cid.ID) ([]byte, error) {
name := getPolicyStorageName(cnrID)
return c.contract.GetChain(policycontract.IAM, namespace, name)
}
func (c *MorphPolicyStorage) DeletePolicy(namespace string, cnrID cid.ID) error {
name := getPolicyStorageName(cnrID)
return c.contract.Wait(c.contract.RemoveChain(policycontract.IAM, namespace, name))
}
func getPolicyStorageName(cnrID cid.ID) []byte {
return append([]byte{policyStoragePrefix}, cnrID[:]...)
}

View File

@ -5,8 +5,8 @@ import (
policycontract "git.frostfs.info/TrueCloudLab/frostfs-contract/policy"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/cache"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/handler"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
"github.com/nspcc-dev/neo-go/pkg/util"
@ -25,10 +25,9 @@ type MorphRuleChainStorageConfig struct {
Log *zap.Logger
}
var (
_ engine.MorphRuleChainStorage = (*MorphRuleChainStorage)(nil)
_ handler.MorphRuleChainStorage = (*MorphRuleChainStorage)(nil)
)
var _ engine.MorphRuleChainStorage = (*MorphRuleChainStorage)(nil)
const bucketPolicyPrefix = 'b'
func NewMorphRuleChainStorage(config *MorphRuleChainStorageConfig) *MorphRuleChainStorage {
return &MorphRuleChainStorage{
@ -38,26 +37,12 @@ func NewMorphRuleChainStorage(config *MorphRuleChainStorageConfig) *MorphRuleCha
}
}
func (c *MorphRuleChainStorage) AddChain(target engine.Target, policyChain *chain.Chain) error {
return c.contract.Wait(c.AddMorphRuleChain(chain.S3, target, policyChain))
func (c *MorphRuleChainStorage) AddMorphRuleChain(chain.Name, engine.Target, *chain.Chain) (util.Uint256, uint32, error) {
panic("should never be called")
}
func (c *MorphRuleChainStorage) RemoveChain(target engine.Target, chainID chain.ID) error {
return c.contract.Wait(c.RemoveMorphRuleChain(chain.S3, target, chainID))
}
func (c *MorphRuleChainStorage) ListChains(target engine.Target) ([]*chain.Chain, error) {
return c.ListMorphRuleChains(chain.S3, target)
}
func (c *MorphRuleChainStorage) AddMorphRuleChain(name chain.Name, target engine.Target, policyChain *chain.Chain) (util.Uint256, uint32, error) {
c.cache.Delete(cache.MorphPolicyCacheKey{Target: target, Name: name})
return c.contract.AddChain(getKind(target), target.Name, getName(name, policyChain.ID), policyChain.Bytes())
}
func (c *MorphRuleChainStorage) RemoveMorphRuleChain(name chain.Name, target engine.Target, chainID chain.ID) (util.Uint256, uint32, error) {
c.cache.Delete(cache.MorphPolicyCacheKey{Target: target, Name: name})
return c.contract.RemoveChain(getKind(target), target.Name, getName(name, chainID))
func (c *MorphRuleChainStorage) RemoveMorphRuleChain(chain.Name, engine.Target, chain.ID) (util.Uint256, uint32, error) {
panic("should never be called")
}
func (c *MorphRuleChainStorage) ListMorphRuleChains(name chain.Name, target engine.Target) ([]*chain.Chain, error) {
@ -88,6 +73,42 @@ func (c *MorphRuleChainStorage) ListMorphRuleChains(name chain.Name, target engi
return list, nil
}
func (c *MorphRuleChainStorage) PutBucketPolicy(ns string, cnrID cid.ID, policy []byte, s3Chain *chain.Chain) error {
c.cache.Delete(cache.MorphPolicyCacheKey{Target: engine.NamespaceTarget(ns), Name: chain.S3})
tx := c.contract.StartTx()
tx.AddChain(policycontract.Namespace, ns, s3Chain.ID, s3Chain.Bytes())
tx.AddChain(policycontract.IAM, ns, getBucketPolicyName(cnrID), policy)
return c.contract.SendTx(tx)
}
func (c *MorphRuleChainStorage) DeleteBucketPolicy(ns string, cnrID cid.ID, chainID chain.ID) error {
c.cache.Delete(cache.MorphPolicyCacheKey{Target: engine.NamespaceTarget(ns), Name: chain.S3})
tx := c.contract.StartTx()
tx.RemoveChain(policycontract.Namespace, ns, chainID)
tx.RemoveChain(policycontract.IAM, ns, getBucketPolicyName(cnrID))
return c.contract.SendTx(tx)
}
func (c *MorphRuleChainStorage) GetBucketPolicy(ns string, cnrID cid.ID) ([]byte, error) {
return c.contract.GetChain(policycontract.IAM, ns, getBucketPolicyName(cnrID))
}
func (c *MorphRuleChainStorage) SaveACLChains(ns string, chains []*chain.Chain) error {
c.cache.Delete(cache.MorphPolicyCacheKey{Target: engine.NamespaceTarget(ns), Name: chain.S3})
c.cache.Delete(cache.MorphPolicyCacheKey{Target: engine.NamespaceTarget(ns), Name: chain.Ingress})
tx := c.contract.StartTx()
for i := range chains {
tx.AddChain(policycontract.Namespace, ns, chains[i].ID, chains[i].Bytes())
}
return c.contract.SendTx(tx)
}
func getKind(target engine.Target) policycontract.Kind {
var kind policycontract.Kind = policycontract.Container
if target.Type != engine.Container {
@ -96,6 +117,7 @@ func getKind(target engine.Target) policycontract.Kind {
return kind
}
func getName(name chain.Name, chainID chain.ID) []byte {
return append([]byte(name), []byte(chainID)...)
func getBucketPolicyName(cnrID cid.ID) []byte {
return append([]byte{bucketPolicyPrefix}, cnrID[:]...)
}

View File

@ -9,18 +9,15 @@ import (
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine/inmemory"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource"
"github.com/nspcc-dev/neo-go/pkg/util"
"go.uber.org/zap"
)
type Storage struct {
router engine.ChainRouter
morph handler.MorphRuleChainStorage
morph *MorphRuleChainStorage
local engine.LocalOverrideStorage
policy handler.PolicyStorage
}
type StorageConfig struct {
@ -29,18 +26,23 @@ type StorageConfig struct {
Log *zap.Logger
}
type MultiTransaction interface {
AddChain(entity policycontract.Kind, entityName string, name []byte, chain []byte)
RemoveChain(entity policycontract.Kind, entityName string, name []byte)
Scripts() ([][]byte, error)
}
type Contract interface {
AddChain(kind policycontract.Kind, entity string, name []byte, chain []byte) (util.Uint256, uint32, error)
GetChain(kind policycontract.Kind, entity string, name []byte) ([]byte, error)
RemoveChain(kind policycontract.Kind, entity string, name []byte) (util.Uint256, uint32, error)
ListChains(kind policycontract.Kind, entity string, name []byte) ([][]byte, error)
Wait(tx util.Uint256, vub uint32, err error) error
GetChain(entity policycontract.Kind, entityName string, name []byte) ([]byte, error)
ListChains(entity policycontract.Kind, entityName string, prefix []byte) ([][]byte, error)
StartTx() MultiTransaction
SendTx(transaction MultiTransaction) error
}
var _ handler.APE = (*Storage)(nil)
func NewStorage(cfg StorageConfig) *Storage {
// todo use thread safe inmemory https://git.frostfs.info/TrueCloudLab/policy-engine/issues/35
local := inmemory.NewInmemoryLocalStorage()
morph := NewMorphRuleChainStorage(&MorphRuleChainStorageConfig{
@ -49,16 +51,10 @@ func NewStorage(cfg StorageConfig) *Storage {
Log: cfg.Log,
})
policyStorage := NewMorphPolicyStorage(&MorphPolicyStorageConfig{
Contract: cfg.Contract,
Log: cfg.Log,
})
return &Storage{
router: engine.NewDefaultChainRouterWithLocalOverrides(morph, local),
morph: morph,
local: local,
policy: policyStorage,
}
}
@ -70,26 +66,23 @@ func (s *Storage) LocalStorage() engine.LocalOverrideStorage {
return s.local
}
func (s *Storage) AddChain(target engine.Target, policyChain *chain.Chain) error {
return s.morph.AddChain(target, policyChain)
func (s *Storage) PutBucketPolicy(ns string, cnrID cid.ID, policy []byte, s3Chain *chain.Chain) error {
return s.morph.PutBucketPolicy(ns, cnrID, policy, s3Chain)
}
func (s *Storage) RemoveChain(target engine.Target, chainID chain.ID) error {
return s.morph.RemoveChain(target, chainID)
func (s *Storage) DeleteBucketPolicy(ns string, cnrID cid.ID, chainID chain.ID) error {
return s.morph.DeleteBucketPolicy(ns, cnrID, chainID)
}
func (s *Storage) ListChains(target engine.Target) ([]*chain.Chain, error) {
return s.morph.ListChains(target)
func (s *Storage) GetBucketPolicy(ns string, cnrID cid.ID) ([]byte, error) {
return s.morph.GetBucketPolicy(ns, cnrID)
}
func (s *Storage) PutPolicy(namespace string, cnrID cid.ID, policy []byte) error {
return s.policy.PutPolicy(namespace, cnrID, policy)
func (s *Storage) SaveACLChains(ns string, chains []*chain.Chain) error {
return s.morph.SaveACLChains(ns, chains)
}
func (s *Storage) GetPolicy(namespace string, cnrID cid.ID) ([]byte, error) {
return s.policy.GetPolicy(namespace, cnrID)
}
func (s *Storage) DeletePolicy(namespace string, cnrID cid.ID) error {
return s.policy.DeletePolicy(namespace, cnrID)
}
//
//func (s *Storage) ListChains(target engine.Target) ([]*chain.Chain, error) {
// return s.morph.ListMorphRuleChains(target)
//}

View File

@ -145,4 +145,5 @@ const (
CouldNotCloseRequestBody = "could not close request body"
BucketOwnerKeyIsMissing = "bucket owner key is missing"
SettingsNodeInvalidOwnerKey = "settings node: invalid owner key"
FailedToSendTransaction = "failed to send transaction"
)