forked from TrueCloudLab/policy-engine
[#62] morph: List morph rules chains by traversing iterator
* Make `ListMorphRuleChains` methods use `commonclient.ReadIteratorItems`. * Introduce `ContractStorageActor` interface. * Iterators are used because listing by `ListChainsByPrefix` may cause stack overflow from neo-go side (len(items) > 1024). Signed-off-by: Airat Arifullin <aarifullin@yadro.com>
This commit is contained in:
parent
04a79f57ef
commit
c539728641
3 changed files with 63 additions and 35 deletions
2
go.mod
2
go.mod
|
@ -3,7 +3,7 @@ module git.frostfs.info/TrueCloudLab/policy-engine
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.0
|
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240409111539-e7a05a49ff45
|
||||||
github.com/google/uuid v1.3.1
|
github.com/google/uuid v1.3.1
|
||||||
github.com/mailru/easyjson v0.7.7
|
github.com/mailru/easyjson v0.7.7
|
||||||
github.com/nspcc-dev/neo-go v0.105.0
|
github.com/nspcc-dev/neo-go v0.105.0
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -1,5 +1,5 @@
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.0 h1:FzurjElUwC7InY9v5rzXReKbfBL5yRJKSWJPq6BKhH0=
|
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240409111539-e7a05a49ff45 h1:Tp4I+XOLp3VCJORfxSamQtj3RZNISbaLM4WD5iIzXxg=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.0/go.mod h1:F/fe1OoIDKr5Bz99q4sriuHDuf3aZefZy9ZsCqEtgxc=
|
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240409111539-e7a05a49ff45/go.mod h1:F/fe1OoIDKr5Bz99q4sriuHDuf3aZefZy9ZsCqEtgxc=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c=
|
github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/commonclient"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/policy"
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/policy"
|
||||||
client "git.frostfs.info/TrueCloudLab/frostfs-contract/rpcclient/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/chain"
|
||||||
|
@ -13,6 +14,7 @@ import (
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
||||||
|
neoinvoker "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
|
@ -26,6 +28,10 @@ var (
|
||||||
|
|
||||||
// ContractStorage is the interface to manage chain rules within Policy contract.
|
// ContractStorage is the interface to manage chain rules within Policy contract.
|
||||||
type ContractStorage struct {
|
type ContractStorage struct {
|
||||||
|
hash util.Uint160
|
||||||
|
|
||||||
|
actor ContractStorageActor
|
||||||
|
|
||||||
contractInterface *client.Contract
|
contractInterface *client.Contract
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,23 +39,49 @@ var _ engine.MorphRuleChainStorage = (*ContractStorage)(nil)
|
||||||
|
|
||||||
// ContractStorageReader is the interface to read data from Policy contract.
|
// ContractStorageReader is the interface to read data from Policy contract.
|
||||||
type ContractStorageReader struct {
|
type ContractStorageReader struct {
|
||||||
|
hash util.Uint160
|
||||||
|
|
||||||
|
invoker ContractStorageInvoker
|
||||||
|
|
||||||
contractReaderInterface *client.ContractReader
|
contractReaderInterface *client.ContractReader
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ContractStorageActor interface {
|
||||||
|
client.Actor
|
||||||
|
GetRPCInvoker() neoinvoker.RPCInvoke
|
||||||
|
}
|
||||||
|
|
||||||
var _ engine.MorphRuleChainStorageReader = (*ContractStorageReader)(nil)
|
var _ engine.MorphRuleChainStorageReader = (*ContractStorageReader)(nil)
|
||||||
|
|
||||||
func NewContractStorage(actor client.Actor, contract util.Uint160) *ContractStorage {
|
func NewContractStorage(actor ContractStorageActor, contract util.Uint160) *ContractStorage {
|
||||||
return &ContractStorage{
|
return &ContractStorage{
|
||||||
|
hash: contract,
|
||||||
|
actor: actor,
|
||||||
contractInterface: client.New(actor, contract),
|
contractInterface: client.New(actor, contract),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type contractStorageActorImpl struct {
|
||||||
|
client.Actor
|
||||||
|
rpcActor actor.RPCActor
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ContractStorageActor = &contractStorageActorImpl{}
|
||||||
|
|
||||||
|
func (c *contractStorageActorImpl) GetRPCInvoker() neoinvoker.RPCInvoke {
|
||||||
|
return c.rpcActor
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContractStorageWithSimpleActor constructs core actor from `rpcActor`.
|
||||||
|
//
|
||||||
|
// Note: NewContractStorageWithSimpleActor is appropriate only for call-only-once cases (for example, in CLIs). Otherwise, it is unsafe,
|
||||||
|
// because core actor may use invalidated `rpcActor` if some connection errors occurred.
|
||||||
func NewContractStorageWithSimpleActor(rpcActor actor.RPCActor, acc *wallet.Account, contract util.Uint160) (*ContractStorage, error) {
|
func NewContractStorageWithSimpleActor(rpcActor actor.RPCActor, acc *wallet.Account, contract util.Uint160) (*ContractStorage, error) {
|
||||||
act, err := actor.NewSimple(rpcActor, acc)
|
act, err := actor.NewSimple(rpcActor, acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create simple actor: %w", err)
|
return nil, fmt.Errorf("failed to create simple actor: %w", err)
|
||||||
}
|
}
|
||||||
return NewContractStorage(act, contract), nil
|
return NewContractStorage(&contractStorageActorImpl{Actor: act, rpcActor: rpcActor}, contract), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ContractStorage) AddMorphRuleChain(name chain.Name, target engine.Target, c *chain.Chain) (txHash util.Uint256, vub uint32, err error) {
|
func (s *ContractStorage) AddMorphRuleChain(name chain.Name, target engine.Target, c *chain.Chain) (txHash util.Uint256, vub uint32, err error) {
|
||||||
|
@ -98,17 +130,26 @@ func (s *ContractStorage) RemoveMorphRuleChainsByTarget(name chain.Name, target
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ContractStorage) ListMorphRuleChains(name chain.Name, target engine.Target) ([]*chain.Chain, error) {
|
func listChains(name chain.Name, target engine.Target, rpcInvoker neoinvoker.RPCInvoke, hash util.Uint160) ([]*chain.Chain, error) {
|
||||||
kind, err := policyKind(target.Type)
|
kind, err := policyKind(target.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
items, err := s.contractInterface.ListChainsByPrefix(big.NewInt(int64(kind)), target.Name, []byte(name))
|
const (
|
||||||
if err != nil {
|
method = "iteratorChainsByPrefix"
|
||||||
return nil, err
|
batchSize = neoinvoker.DefaultIteratorResultItems
|
||||||
|
)
|
||||||
|
|
||||||
|
inv := neoinvoker.New(rpcInvoker, nil)
|
||||||
|
params := []any{
|
||||||
|
big.NewInt(int64(kind)), target.Name, []byte(name),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
items, err := commonclient.ReadIteratorItems(inv, batchSize, hash, method, params...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("read items error: %w", err)
|
||||||
|
}
|
||||||
var chains []*chain.Chain
|
var chains []*chain.Chain
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
serialized, err := bytesFromStackItem(item)
|
serialized, err := bytesFromStackItem(item)
|
||||||
|
@ -121,10 +162,13 @@ func (s *ContractStorage) ListMorphRuleChains(name chain.Name, target engine.Tar
|
||||||
}
|
}
|
||||||
chains = append(chains, c)
|
chains = append(chains, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
return chains, nil
|
return chains, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ContractStorage) ListMorphRuleChains(name chain.Name, target engine.Target) ([]*chain.Chain, error) {
|
||||||
|
return listChains(name, target, s.actor.GetRPCInvoker(), s.hash)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ContractStorage) ListTargetsIterator(targetType engine.TargetType) (uuid.UUID, result.Iterator, error) {
|
func (s *ContractStorage) ListTargetsIterator(targetType engine.TargetType) (uuid.UUID, result.Iterator, error) {
|
||||||
kind, err := policyKind(targetType)
|
kind, err := policyKind(targetType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -141,37 +185,21 @@ func (s *ContractStorage) SetAdmin(addr util.Uint160) (util.Uint256, uint32, err
|
||||||
return s.contractInterface.SetAdmin(addr)
|
return s.contractInterface.SetAdmin(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContractStorageReader(inv client.Invoker, contract util.Uint160) *ContractStorageReader {
|
type ContractStorageInvoker interface {
|
||||||
|
client.Invoker
|
||||||
|
GetRPCInvoker() neoinvoker.RPCInvoke
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewContractStorageReader(inv ContractStorageInvoker, contract util.Uint160) *ContractStorageReader {
|
||||||
return &ContractStorageReader{
|
return &ContractStorageReader{
|
||||||
|
hash: contract,
|
||||||
|
invoker: inv,
|
||||||
contractReaderInterface: client.NewReader(inv, contract),
|
contractReaderInterface: client.NewReader(inv, contract),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ContractStorageReader) ListMorphRuleChains(name chain.Name, target engine.Target) ([]*chain.Chain, error) {
|
func (s *ContractStorageReader) ListMorphRuleChains(name chain.Name, target engine.Target) ([]*chain.Chain, error) {
|
||||||
kind, err := policyKind(target.Type)
|
return listChains(name, target, s.invoker.GetRPCInvoker(), s.hash)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
items, err := s.contractReaderInterface.ListChainsByPrefix(big.NewInt(int64(kind)), target.Name, []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 (s *ContractStorageReader) GetAdmin() (util.Uint160, error) {
|
func (s *ContractStorageReader) GetAdmin() (util.Uint160, error) {
|
||||||
|
|
Loading…
Reference in a new issue