package contract import ( "errors" "strings" "sync" policycontract "git.frostfs.info/TrueCloudLab/frostfs-contract/policy" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/policy" "github.com/nspcc-dev/neo-go/pkg/util" ) type InMemoryContract struct { iamChains *syncedMap containerChains *syncedMap namespaceChains *syncedMap } type syncedMap struct { mu sync.RWMutex data map[string][]byte } var _ policy.Contract = (*InMemoryContract)(nil) var ErrChainNotFound = errors.New("chain not found") // NewInMemoryContract creates new inmemory Policy contract wrapper. func NewInMemoryContract() *InMemoryContract { return &InMemoryContract{ iamChains: &syncedMap{data: map[string][]byte{}}, containerChains: &syncedMap{data: map[string][]byte{}}, namespaceChains: &syncedMap{data: map[string][]byte{}}, } } func (c *InMemoryContract) AddChain(kind policycontract.Kind, entity string, name []byte, chain []byte) (util.Uint256, uint32, error) { syncMap := c.getMap(kind) syncMap.mu.Lock() syncMap.data[entity+string(name)] = chain syncMap.mu.Unlock() return util.Uint256{}, 0, nil } func (c *InMemoryContract) GetChain(kind policycontract.Kind, entity string, name []byte) ([]byte, error) { syncMap := c.getMap(kind) syncMap.mu.RLock() defer syncMap.mu.RUnlock() val, ok := syncMap.data[entity+string(name)] if !ok { return nil, ErrChainNotFound } return val, nil } func (c *InMemoryContract) RemoveChain(kind policycontract.Kind, entity string, name []byte) (util.Uint256, uint32, error) { syncMap := c.getMap(kind) syncMap.mu.Lock() delete(syncMap.data, entity+string(name)) syncMap.mu.Unlock() return util.Uint256{}, 0, nil } func (c *InMemoryContract) ListChains(kind policycontract.Kind, entity string, name []byte) ([][]byte, error) { syncMap := c.getMap(kind) syncMap.mu.RLock() defer syncMap.mu.RUnlock() var res [][]byte for key, val := range syncMap.data { if strings.HasPrefix(key, entity+string(name)) { res = append(res, val) } } return res, nil } 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: return c.iamChains case policycontract.Container: return c.containerChains case policycontract.Namespace: return c.namespaceChains default: 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 }