[#249] node: Drop subnet from IR and morph

Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
This commit is contained in:
Dmitrii Stepanov 2023-04-14 13:47:08 +03:00
parent d757d881d0
commit f07d4158f5
41 changed files with 6 additions and 1903 deletions

View file

@ -142,7 +142,6 @@ func setWorkersDefaults(cfg *viper.Viper) {
cfg.SetDefault("workers.frostfs", "10")
cfg.SetDefault("workers.container", "10")
cfg.SetDefault("workers.alphabet", "10")
cfg.SetDefault("workers.subnet", "10")
}
func setTimersDefaults(cfg *viper.Viper) {
@ -163,7 +162,6 @@ func setContractsDefaults(cfg *viper.Viper) {
cfg.SetDefault("contracts.audit", "")
cfg.SetDefault("contracts.proxy", "")
cfg.SetDefault("contracts.processing", "")
cfg.SetDefault("contracts.subnet", "")
cfg.SetDefault("contracts.proxy", "")
}

View file

@ -49,7 +49,6 @@ FROSTFS_IR_WORKERS_BALANCE=10
FROSTFS_IR_WORKERS_CONTAINER=10
FROSTFS_IR_WORKERS_NEOFS=10
FROSTFS_IR_WORKERS_NETMAP=10
FROSTFS_IR_WORKERS_SUBNET=10
FROSTFS_IR_AUDIT_TIMEOUT_GET=5s
FROSTFS_IR_AUDIT_TIMEOUT_HEAD=5s
@ -74,7 +73,6 @@ FROSTFS_IR_CONTRACTS_CONTAINER=ed4a7a66fe3f9bfe50f214b49be8f215a3c886b6
FROSTFS_IR_CONTRACTS_NEOFSID=9f5866decbc751a099e74c7c7bc89f609201755a
FROSTFS_IR_CONTRACTS_NETMAP=83c600c81d47a1b1b7cf58eb49ae7ee7240dc742
FROSTFS_IR_CONTRACTS_PROXY=abc8794bb40a21f2db5f21ae62741eb46c8cad1c
FROSTFS_IR_CONTRACTS_SUBNET=e9266864d3c562c6e17f2bb9cb1392aaa293d93a
FROSTFS_IR_CONTRACTS_ALPHABET_AMOUNT=7
FROSTFS_IR_CONTRACTS_ALPHABET_AZ=c1d211fceeb4b1dc76b8e4054d11fdf887e418ea
FROSTFS_IR_CONTRACTS_ALPHABET_BUKY=e2ba789320899658b100f331bdebb74474757920

View file

@ -83,7 +83,6 @@ workers:
container: 10 # Number of workers to process events from container contract in parallel
frostfs: 10 # Number of workers to process events from frostfs contracts in parallel
netmap: 10 # Number of workers to process events from netmap contract in parallel
subnet: 10 # Number of workers to process events from subnet contract in parallel
audit:
timeout:
@ -116,7 +115,6 @@ contracts:
frostfsid: 9f5866decbc751a099e74c7c7bc89f609201755a # Optional: override address of frostfsid contract in sidechain
netmap: 83c600c81d47a1b1b7cf58eb49ae7ee7240dc742 # Optional: override address of netmap contract in sidechain
proxy: abc8794bb40a21f2db5f21ae62741eb46c8cad1c # Optional: override address of proxy contract in sidechain; ignore if notary is disabled in sidechain
subnet: e9266864d3c562c6e17f2bb9cb1392aaa293d93a # Optional: override address of subnet contract in sidechain
alphabet:
amount: 7 # Optional: override amount of alphabet contracts
az: c1d211fceeb4b1dc76b8e4054d11fdf887e418ea # Optional: override address of az alphabet contract in sidechain

View file

@ -18,7 +18,6 @@ type contracts struct {
audit util.Uint160 // in morph
proxy util.Uint160 // in morph
processing util.Uint160 // in mainnet
subnet util.Uint160 // in morph
frostfsID util.Uint160 // in morph
alphabet alphabetContracts // in morph
@ -60,7 +59,6 @@ func parseContracts(cfg *viper.Viper, morph *client.Client, withoutMainNet, with
{"contracts.balance", client.NNSBalanceContractName, &result.balance},
{"contracts.container", client.NNSContainerContractName, &result.container},
{"contracts.audit", client.NNSAuditContractName, &result.audit},
{"contracts.subnet", client.NNSSubnetworkContractName, &result.subnet},
{"contracts.frostfsid", client.NNSFrostFSIDContractName, &result.frostfsID},
}

View file

@ -17,7 +17,6 @@ import (
nodevalidator "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/processors/netmap/nodevalidation"
addrvalidator "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/processors/netmap/nodevalidation/maddress"
statevalidation "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/processors/netmap/nodevalidation/state"
subnetvalidator "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/processors/netmap/nodevalidation/subnet"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/processors/settlement"
auditSettlement "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/processors/settlement/audit"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/metrics"
@ -28,7 +27,6 @@ import (
frostfsClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/frostfs"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/frostfsid"
nmClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
morphsubnet "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/subnet"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
audittask "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/audit/taskmanager"
control "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir"
@ -46,7 +44,6 @@ import (
func (s *Server) initNetmapProcessor(cfg *viper.Viper,
cnrClient *container.Client,
alphaSync event.Handler,
subnetClient *morphsubnet.Client,
auditProcessor *audit.Processor,
settlementProcessor *settlement.Processor) error {
locodeValidator, err := s.newLocodeValidator(cfg)
@ -54,11 +51,6 @@ func (s *Server) initNetmapProcessor(cfg *viper.Viper,
return err
}
subnetValidator, err := subnetvalidator.New(
subnetvalidator.Prm{
SubnetClient: subnetClient,
},
)
if err != nil {
return err
}
@ -94,10 +86,8 @@ func (s *Server) initNetmapProcessor(cfg *viper.Viper,
&netMapCandidateStateValidator,
addrvalidator.New(),
locodeValidator,
subnetValidator,
),
NotaryDisabled: s.sideNotaryConfig.disabled,
SubnetContract: &s.contracts.subnet,
NodeStateSettings: netSettings,
})
@ -349,27 +339,6 @@ func (s *Server) initTimers(cfg *viper.Viper, processors *serverProcessors, morp
s.addBlockTimer(emissionTimer)
}
func (s *Server) createMorphSubnetClient() (*morphsubnet.Client, error) {
// initialize morph client of Subnet contract
clientMode := morphsubnet.NotaryAlphabet
if s.sideNotaryConfig.disabled {
clientMode = morphsubnet.NonNotary
}
subnetInitPrm := morphsubnet.InitPrm{}
subnetInitPrm.SetBaseClient(s.morphClient)
subnetInitPrm.SetContractAddress(s.contracts.subnet)
subnetInitPrm.SetMode(clientMode)
subnetClient := &morphsubnet.Client{}
err := subnetClient.Init(subnetInitPrm)
if err != nil {
return nil, fmt.Errorf("could not initialize subnet client: %w", err)
}
return subnetClient, nil
}
func (s *Server) initAlphabetProcessor(cfg *viper.Viper) (*alphabet.Processor, error) {
parsedWallets, err := parseWalletAddressesFromStrings(cfg.GetStringSlice("emit.extra_wallets"))
if err != nil {
@ -400,7 +369,7 @@ func (s *Server) initAlphabetProcessor(cfg *viper.Viper) (*alphabet.Processor, e
}
func (s *Server) initContainerProcessor(cfg *viper.Viper, cnrClient *container.Client,
frostfsIDClient *frostfsid.Client, subnetClient *morphsubnet.Client) error {
frostfsIDClient *frostfsid.Client) error {
// container processor
containerProcessor, err := cont.New(&cont.Params{
Log: s.log,
@ -410,7 +379,6 @@ func (s *Server) initContainerProcessor(cfg *viper.Viper, cnrClient *container.C
FrostFSIDClient: frostfsIDClient,
NetworkState: s.netmapClient,
NotaryDisabled: s.sideNotaryConfig.disabled,
SubnetClient: subnetClient,
})
if err != nil {
return err
@ -515,10 +483,9 @@ func (s *Server) initGRPCServer(cfg *viper.Viper) error {
}
type serverMorphClients struct {
CnrClient *container.Client
FrostFSIDClient *frostfsid.Client
FrostFSClient *frostfsClient.Client
MorphSubnetClient *morphsubnet.Client
CnrClient *container.Client
FrostFSIDClient *frostfsid.Client
FrostFSClient *frostfsClient.Client
}
func (s *Server) initClientsFromMorph() (*serverMorphClients, error) {
@ -574,11 +541,6 @@ func (s *Server) initClientsFromMorph() (*serverMorphClients, error) {
return nil, err
}
result.MorphSubnetClient, err = s.createMorphSubnetClient()
if err != nil {
return nil, err
}
return result, nil
}
@ -624,12 +586,12 @@ func (s *Server) initProcessors(cfg *viper.Viper, morphClients *serverMorphClien
return nil, err
}
err = s.initNetmapProcessor(cfg, morphClients.CnrClient, alphaSync, morphClients.MorphSubnetClient, auditProcessor, result.SettlementProcessor)
err = s.initNetmapProcessor(cfg, morphClients.CnrClient, alphaSync, auditProcessor, result.SettlementProcessor)
if err != nil {
return nil, err
}
err = s.initContainerProcessor(cfg, morphClients.CnrClient, morphClients.FrostFSIDClient, morphClients.MorphSubnetClient)
err = s.initContainerProcessor(cfg, morphClients.CnrClient, morphClients.FrostFSIDClient)
if err != nil {
return nil, err
}

View file

@ -100,8 +100,6 @@ type (
// should report start errors
// to the application.
runners []func(chan<- error) error
subnetHandler
}
chainParams struct {
@ -400,10 +398,6 @@ func New(ctx context.Context, log *logger.Logger, cfg *viper.Viper, errChan chan
return nil, err
}
server.initSubnet(subnetConfig{
queueSize: cfg.GetUint32("workers.subnet"),
})
server.initMetrics(cfg)
return server, nil

View file

@ -5,13 +5,11 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
cntClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
morphsubnet "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/subnet"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
containerEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/container"
containerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
"github.com/nspcc-dev/neo-go/pkg/network/payload"
"go.uber.org/zap"
)
@ -77,12 +75,6 @@ func (cp *Processor) checkPutContainer(ctx *putContainerContext) error {
return fmt.Errorf("auth container creation: %w", err)
}
// check owner allowance in the subnetwork
err = checkSubnet(cp.subnetClient, cnr)
if err != nil {
return fmt.Errorf("incorrect subnetwork: %w", err)
}
// check homomorphic hashing setting
err = checkHomomorphicHashing(cp.netState, cnr)
if err != nil {
@ -222,29 +214,6 @@ func checkNNS(ctx *putContainerContext, cnr containerSDK.Container) error {
return nil
}
func checkSubnet(subCli *morphsubnet.Client, cnr containerSDK.Container) error {
prm := morphsubnet.UserAllowedPrm{}
subID := cnr.PlacementPolicy().Subnet()
if subnetid.IsZero(subID) {
return nil
}
prm.SetID(subID.Marshal())
prm.SetClient(cnr.Owner().WalletBytes())
res, err := subCli.UserAllowed(prm)
if err != nil {
return fmt.Errorf("could not check user in contract: %w", err)
}
if !res.Allowed() {
return fmt.Errorf("user is not allowed to create containers in %v subnetwork", subID)
}
return nil
}
func checkHomomorphicHashing(ns NetworkState, cnr containerSDK.Container) error {
netSetting, err := ns.HomomorphicHashDisabled()
if err != nil {

View file

@ -7,7 +7,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/frostfsid"
morphsubnet "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/subnet"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
containerEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/container"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
@ -29,7 +28,6 @@ type (
alphabetState AlphabetState
cnrClient *container.Client // notary must be enabled
idClient *frostfsid.Client
subnetClient *morphsubnet.Client
netState NetworkState
notaryDisabled bool
}
@ -41,7 +39,6 @@ type (
AlphabetState AlphabetState
ContainerClient *container.Client
FrostFSIDClient *frostfsid.Client
SubnetClient *morphsubnet.Client
NetworkState NetworkState
NotaryDisabled bool
}
@ -85,8 +82,6 @@ func New(p *Params) (*Processor, error) {
return nil, errors.New("ir/container: FrostFS ID client is not set")
case p.NetworkState == nil:
return nil, errors.New("ir/container: network state is not set")
case p.SubnetClient == nil:
return nil, errors.New("ir/container: subnet client is not set")
}
p.Log.Debug(logs.ContainerContainerWorkerPool, zap.Int("size", p.PoolSize))
@ -104,7 +99,6 @@ func New(p *Params) (*Processor, error) {
idClient: p.FrostFSIDClient,
netState: p.NetworkState,
notaryDisabled: p.NotaryDisabled,
subnetClient: p.SubnetClient,
}, nil
}

View file

@ -7,7 +7,6 @@ import (
timerEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/timers"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
netmapEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/netmap"
subnetevents "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/subnet"
"go.uber.org/zap"
)
@ -101,22 +100,3 @@ func (np *Processor) handleCleanupTick(ev event.Event) {
zap.Int("capacity", np.pool.Cap()))
}
}
func (np *Processor) handleRemoveNode(ev event.Event) {
removeNode := ev.(subnetevents.RemoveNode)
np.log.Info(logs.Notification,
zap.String("type", "remove node from subnet"),
zap.String("subnetID", hex.EncodeToString(removeNode.SubnetworkID())),
zap.String("key", hex.EncodeToString(removeNode.Node())),
)
err := np.pool.Submit(func() {
np.processRemoveSubnetNode(removeNode)
})
if err != nil {
// there system can be moved into controlled degradation stage
np.log.Warn(logs.NetmapNetmapWorkerPoolDrained,
zap.Int("capacity", np.pool.Cap()))
}
}

View file

@ -1,42 +0,0 @@
package subnet
import (
"fmt"
morphsubnet "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/subnet"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
)
// VerifyAndUpdate calls subnet contract's `NodeAllowed` method.
// Removes subnets that have not been approved by the contract.
func (v *Validator) VerifyAndUpdate(n *netmap.NodeInfo) error {
prm := morphsubnet.NodeAllowedPrm{}
err := n.IterateSubnets(func(id subnetid.ID) error {
// every node can be bootstrapped
// to the zero subnetwork
if subnetid.IsZero(id) {
return nil
}
prm.SetID(id.Marshal())
prm.SetNode(n.PublicKey())
res, err := v.subnetClient.NodeAllowed(prm)
if err != nil {
return fmt.Errorf("could not call `NodeAllowed` contract method: %w", err)
}
if !res.Allowed() {
return netmap.ErrRemoveSubnet
}
return nil
})
if err != nil {
return fmt.Errorf("could not verify subnet entrance of the node: %w", err)
}
return nil
}

View file

@ -1,41 +0,0 @@
package subnet
import (
"errors"
morphsubnet "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/subnet"
)
// Validator is an utility that verifies node subnet
// allowance.
//
// For correct operation, Validator must be created
// using the constructor (New). After successful creation,
// the Validator is immediately ready to work through API.
type Validator struct {
subnetClient *morphsubnet.Client
}
// Prm groups the required parameters of the Validator's constructor.
//
// All values must comply with the requirements imposed on them.
// Passing incorrect parameter values will result in constructor
// failure (error or panic depending on the implementation).
type Prm struct {
SubnetClient *morphsubnet.Client
}
// New creates a new instance of the Validator.
//
// The created Validator does not require additional
// initialization and is completely ready for work.
func New(prm Prm) (*Validator, error) {
switch {
case prm.SubnetClient == nil:
return nil, errors.New("ir/nodeValidator: subnet client is not set")
}
return &Validator{
subnetClient: prm.SubnetClient,
}, nil
}

View file

@ -1,15 +1,12 @@
package netmap
import (
"bytes"
"encoding/hex"
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
netmapclient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
netmapEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/netmap"
subnetEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/subnet"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
"go.uber.org/zap"
)
@ -139,74 +136,3 @@ func (np *Processor) processUpdatePeer(ev netmapEvent.UpdatePeer) {
np.log.Error(logs.NetmapCantInvokeNetmapUpdatePeer, zap.Error(err))
}
}
func (np *Processor) processRemoveSubnetNode(ev subnetEvent.RemoveNode) {
if !np.alphabetState.IsAlphabet() {
np.log.Info(logs.NetmapNonAlphabetModeIgnoreRemoveNodeFromSubnetNotification)
return
}
candidates, err := np.netmapClient.GetCandidates()
if err != nil {
np.log.Warn(logs.NetmapCouldNotGetNetworkMapCandidates,
zap.Error(err),
)
return
}
rawSubnet := ev.SubnetworkID()
var subnetToRemoveFrom subnetid.ID
err = subnetToRemoveFrom.Unmarshal(rawSubnet)
if err != nil {
np.log.Warn(logs.NetmapCouldNotUnmarshalSubnetID,
zap.Error(err),
)
return
}
if subnetid.IsZero(subnetToRemoveFrom) {
np.log.Warn(logs.NetmapGotZeroSubnetInRemoveNodeNotification)
return
}
for i := range candidates {
if !bytes.Equal(candidates[i].PublicKey(), ev.Node()) {
continue
}
err = candidates[i].IterateSubnets(func(subNetID subnetid.ID) error {
if subNetID.Equals(subnetToRemoveFrom) {
return netmap.ErrRemoveSubnet
}
return nil
})
if err != nil {
np.log.Warn(logs.NetmapCouldNotIterateOverSubnetworksOfTheNode, zap.Error(err))
np.log.Info(logs.NetmapVoteToRemoveNodeFromNetmap, zap.String("key", hex.EncodeToString(ev.Node())))
prm := netmapclient.UpdatePeerPrm{}
prm.SetKey(ev.Node())
prm.SetHash(ev.TxHash())
err = np.netmapClient.UpdatePeerState(prm)
if err != nil {
np.log.Error(logs.NetmapCouldNotInvokeNetmapUpdateState, zap.Error(err))
return
}
} else {
prm := netmapclient.AddPeerPrm{}
prm.SetNodeInfo(candidates[i])
prm.SetHash(ev.TxHash())
err = np.netmapClient.AddPeer(prm)
if err != nil {
np.log.Error(logs.NetmapCouldNotInvokeNetmapAddPeer, zap.Error(err))
return
}
}
break
}
}

View file

@ -10,11 +10,9 @@ import (
nmClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
netmapEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/netmap"
subnetEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/subnet"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"github.com/nspcc-dev/neo-go/pkg/core/mempoolevent"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/panjf2000/ants/v2"
"go.uber.org/zap"
)
@ -65,8 +63,6 @@ type (
netmapClient *nmClient.Client
containerWrp *container.Client
subnetContract util.Uint160
netmapSnapshot cleanupTable
handleNewAudit event.Handler
@ -92,7 +88,6 @@ type (
CleanupEnabled bool
CleanupThreshold uint64 // in epochs
ContainerWrapper *container.Client
SubnetContract *util.Uint160
HandleAudit event.Handler
AuditSettlementsHandler event.Handler
@ -111,7 +106,6 @@ const (
newEpochNotification = "NewEpoch"
addPeerNotification = "AddPeer"
updatePeerStateNotification = "UpdateState"
removeNodeNotification = "RemoveNode"
)
// New creates network map contract processor instance.
@ -137,8 +131,6 @@ func New(p *Params) (*Processor, error) {
return nil, errors.New("ir/netmap: container contract wrapper is not set")
case p.NodeValidator == nil:
return nil, errors.New("ir/netmap: node validator is not set")
case p.SubnetContract == nil:
return nil, errors.New("ir/netmap: subnet contract script hash is not set")
case p.NodeStateSettings == nil:
return nil, errors.New("ir/netmap: node state settings is not set")
}
@ -160,7 +152,6 @@ func New(p *Params) (*Processor, error) {
containerWrp: p.ContainerWrapper,
netmapSnapshot: newCleanupTable(p.CleanupEnabled, p.CleanupThreshold),
handleNewAudit: p.HandleAudit,
subnetContract: *p.SubnetContract,
handleAuditSettlements: p.AuditSettlementsHandler,
@ -182,13 +173,6 @@ func (np *Processor) ListenerNotificationParsers() []event.NotificationParserInf
var p event.NotificationParserInfo
// remove node from subnetwork event
p.SetScriptHash(np.subnetContract)
p.SetType(removeNodeNotification)
p.SetParser(subnetEvent.ParseRemoveNode)
parsers = append(parsers, p)
p.SetScriptHash(np.netmapClient.ContractAddress())
// new epoch event
@ -219,13 +203,6 @@ func (np *Processor) ListenerNotificationHandlers() []event.NotificationHandlerI
var i event.NotificationHandlerInfo
// remove node from subnetwork event
i.SetScriptHash(np.subnetContract)
i.SetType(removeNodeNotification)
i.SetHandler(np.handleRemoveNode)
handlers = append(handlers, i)
i.SetScriptHash(np.netmapClient.ContractAddress())
// new epoch handler

View file

@ -1,22 +0,0 @@
package subnetevents
import (
"fmt"
subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
)
// common interface of subnet notifications with subnet ID.
type eventWithID interface {
// ReadID reads identifier of the subnet.
ReadID(*subnetid.ID) error
}
// an error which is returned on zero subnet operation attempt.
type zeroSubnetOp struct {
op string
}
func (x zeroSubnetOp) Error() string {
return fmt.Sprintf("zero subnet %s", x.op)
}

View file

@ -1,19 +0,0 @@
package subnetevents
import subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
type idEvent struct {
id subnetid.ID
idErr error
}
func (x idEvent) ReadID(id *subnetid.ID) error {
if x.idErr != nil {
return x.idErr
}
*id = x.id
return nil
}

View file

@ -1,82 +0,0 @@
package subnetevents
import (
"errors"
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet"
subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
)
// Put represents a notification about FrostFS subnet creation.
// Generated by a contract when intending to create a subnet.
type Put interface {
// Contains the ID of the subnet to be created.
eventWithID
// ReadCreator reads the user ID of the subnet creator.
// Returns an error if the ID is missing.
ReadCreator(id *user.ID) error
// ReadInfo reads information about a subnet to be created.
ReadInfo(info *subnet.Info) error
}
// PutValidator asserts intent to create a subnet.
type PutValidator struct{}
// errDiffOwner is returned when the subnet owners differ.
var errDiffOwner = errors.New("diff subnet owners")
// errDiffID is returned when the subnet IDs differ.
var errDiffID = errors.New("diff subnet IDs")
// Assert processes the attempt to create a subnet. It approves the creation through nil return.
//
// All read errors of Put are forwarded.
//
// It returns an error on:
// - zero subnet creation;
// - empty ID or different from the one wired into info;
// - empty owner ID or different from the one wired into info.
func (x PutValidator) Assert(event Put) error {
var err error
// read ID
var id subnetid.ID
if err = event.ReadID(&id); err != nil {
return fmt.Errorf("read ID: %w", err)
}
// prevent zero subnet creation
if subnetid.IsZero(id) {
return zeroSubnetOp{
op: "creation",
}
}
// read creator's user ID in FrostFS system
var creator user.ID
if err = event.ReadCreator(&creator); err != nil {
return fmt.Errorf("read creator: %w", err)
}
// read information about the subnet
var info subnet.Info
if err = event.ReadInfo(&info); err != nil {
return fmt.Errorf("read info: %w", err)
}
// check if the explicit ID equals to the one from info
if !subnet.AssertReference(info, id) {
return errDiffID
}
// check if the explicit creator equals to the one from info
if !subnet.AssertOwnership(info, creator) {
return errDiffOwner
}
return nil
}

View file

@ -1,113 +0,0 @@
package subnetevents
import (
"errors"
"testing"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
"github.com/stretchr/testify/require"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet"
subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
)
type put struct {
idEvent
creator user.ID
creatorErr error
info subnet.Info
infoErr error
}
func (x put) ReadCreator(id *user.ID) error {
if x.creatorErr != nil {
return x.creatorErr
}
*id = x.creator
return nil
}
func (x put) ReadInfo(info *subnet.Info) error {
if x.infoErr != nil {
return x.infoErr
}
*info = x.info
return nil
}
func TestPutValidator_Assert(t *testing.T) {
var (
v PutValidator
e put
err error
)
// read ID error
e.idErr = errors.New("id err")
err = v.Assert(e)
require.ErrorIs(t, err, e.idErr)
e.idErr = nil
// zero subnet ID
subnetid.MakeZero(&e.id)
err = v.Assert(e)
require.ErrorAs(t, err, new(zeroSubnetOp))
const idNum = 13
e.id.SetNumeric(idNum)
// read creator error
e.creatorErr = errors.New("creator err")
err = v.Assert(e)
require.ErrorIs(t, err, e.creatorErr)
e.creatorErr = nil
// read info error
e.infoErr = errors.New("info err")
err = v.Assert(e)
require.ErrorIs(t, err, e.infoErr)
e.infoErr = nil
// diff explicit ID and the one in info
var id2 subnetid.ID
id2.SetNumeric(idNum + 1)
e.info.SetID(id2)
err = v.Assert(e)
require.ErrorIs(t, err, errDiffID)
e.info.SetID(e.id)
// diff explicit creator and the one in info
creator2 := *usertest.ID()
e.info.SetOwner(creator2)
err = v.Assert(e)
require.ErrorIs(t, err, errDiffOwner)
e.info.SetOwner(e.creator)
err = v.Assert(e)
require.NoError(t, err)
}

View file

@ -1,355 +0,0 @@
package innerring
import (
"crypto/ecdsa"
"crypto/elliptic"
"errors"
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
irsubnet "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/processors/subnet"
netmapclient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
morphsubnet "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/subnet"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
subnetevents "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/subnet"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet"
subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"github.com/nspcc-dev/neo-go/pkg/core/mempoolevent"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
neogoutil "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/panjf2000/ants/v2"
"go.uber.org/zap"
)
// IR server's component to handle Subnet contract notifications.
type subnetHandler struct {
workerPool util.WorkerPool
morphClient morphsubnet.Client
putValidator irsubnet.PutValidator
}
// configuration of subnet component.
type subnetConfig struct {
queueSize uint32
}
// makes IR server to catch Subnet notifications from the sidechain listener,
// and to release the corresponding processing queue on stop.
func (s *Server) initSubnet(cfg subnetConfig) {
s.registerStarter(func() error {
var err error
// initialize queue for processing of the events from Subnet contract
s.subnetHandler.workerPool, err = ants.NewPool(int(cfg.queueSize), ants.WithNonblocking(true))
if err != nil {
return fmt.Errorf("subnet queue initialization: %w", err)
}
// initialize morph client of Subnet contract
clientMode := morphsubnet.NotaryAlphabet
if s.sideNotaryConfig.disabled {
clientMode = morphsubnet.NonNotary
}
var initPrm morphsubnet.InitPrm
initPrm.SetBaseClient(s.morphClient)
initPrm.SetContractAddress(s.contracts.subnet)
initPrm.SetMode(clientMode)
err = s.subnetHandler.morphClient.Init(initPrm)
if err != nil {
return fmt.Errorf("init morph subnet client: %w", err)
}
s.listenSubnet()
return nil
})
s.registerCloser(func() error {
s.stopSubnet()
return nil
})
}
// releases the Subnet contract notification processing queue.
func (s *Server) stopSubnet() {
s.workerPool.Release()
}
// names of listened notification events from Subnet contract.
const (
subnetCreateEvName = "Put"
subnetRemoveEvName = "Delete"
notarySubnetCreateEvName = "put"
)
// makes the IR server to listen to notifications of Subnet contract.
// All required resources must be initialized before (initSubnet).
// It works in one of two modes (configured): notary and non-notary.
//
// All handlers are executed only if the local node is an alphabet one.
//
// Events (notary):
// - put (parser: subnetevents.ParseNotaryPut, handler: catchSubnetCreation);
// - Delete (parser: subnetevents.ParseDelete, handler: catchSubnetCreation).
//
// Events (non-notary):
// - Put (parser: subnetevents.ParsePut, handler: catchSubnetCreation);
// - Delete (parser: subnetevents.ParseDelete, handler: catchSubnetCreation).
func (s *Server) listenSubnet() {
if s.sideNotaryConfig.disabled {
s.listenSubnetWithoutNotary()
return
}
var (
parserInfo event.NotaryParserInfo
handlerInfo event.NotaryHandlerInfo
)
parserInfo.SetScriptHash(s.contracts.subnet)
handlerInfo.SetScriptHash(s.contracts.subnet)
listenNotaryEvent := func(notifyName string, parser event.NotaryParser, handler event.Handler) {
notifyTyp := event.NotaryTypeFromString(notifyName)
parserInfo.SetMempoolType(mempoolevent.TransactionAdded)
handlerInfo.SetMempoolType(mempoolevent.TransactionAdded)
parserInfo.SetParser(parser)
handlerInfo.SetHandler(handler)
parserInfo.SetRequestType(notifyTyp)
handlerInfo.SetRequestType(notifyTyp)
s.morphListener.SetNotaryParser(parserInfo)
s.morphListener.RegisterNotaryHandler(handlerInfo)
}
// subnet creation
listenNotaryEvent(notarySubnetCreateEvName, subnetevents.ParseNotaryPut, s.onlyAlphabetEventHandler(s.catchSubnetCreation))
// subnet removal
listenNotifySubnetEvent(s, subnetRemoveEvName, subnetevents.ParseDelete, s.onlyAlphabetEventHandler(s.catchSubnetRemoval))
}
func (s *Server) listenSubnetWithoutNotary() {
// subnet creation
listenNotifySubnetEvent(s, subnetCreateEvName, subnetevents.ParsePut, s.onlyAlphabetEventHandler(s.catchSubnetCreation))
// subnet removal
listenNotifySubnetEvent(s, subnetRemoveEvName, subnetevents.ParseDelete, s.onlyAlphabetEventHandler(s.catchSubnetRemoval))
}
func listenNotifySubnetEvent(s *Server, notifyName string, parser event.NotificationParser, handler event.Handler) {
var (
parserInfo event.NotificationParserInfo
handlerInfo event.NotificationHandlerInfo
)
parserInfo.SetScriptHash(s.contracts.subnet)
handlerInfo.SetScriptHash(s.contracts.subnet)
notifyTyp := event.TypeFromString(notifyName)
parserInfo.SetType(notifyTyp)
handlerInfo.SetType(notifyTyp)
parserInfo.SetParser(parser)
handlerInfo.SetHandler(handler)
s.morphListener.SetNotificationParser(parserInfo)
s.morphListener.RegisterNotificationHandler(handlerInfo)
}
// catchSubnetCreation catches event of subnet creation from listener and queues the processing.
func (s *Server) catchSubnetCreation(e event.Event) {
err := s.subnetHandler.workerPool.Submit(func() {
s.handleSubnetCreation(e)
})
if err != nil {
s.log.Error(logs.InnerringSubnetCreationQueueFailure,
zap.String("error", err.Error()),
)
}
}
// implements irsubnet.Put event interface required by irsubnet.PutValidator.
type putSubnetEvent struct {
ev subnetevents.Put
}
// ReadID unmarshals the subnet ID from a binary FrostFS API protocol's format.
func (x putSubnetEvent) ReadID(id *subnetid.ID) error {
return id.Unmarshal(x.ev.ID())
}
var errMissingSubnetOwner = errors.New("missing subnet owner")
// ReadCreator unmarshals the subnet creator from a binary FrostFS API protocol's format.
// Returns an error if the byte array is empty.
func (x putSubnetEvent) ReadCreator(id *user.ID) error {
data := x.ev.Owner()
if len(data) == 0 {
return errMissingSubnetOwner
}
key, err := keys.NewPublicKeyFromBytes(data, elliptic.P256())
if err != nil {
return err
}
user.IDFromKey(id, (ecdsa.PublicKey)(*key))
return nil
}
// ReadInfo unmarshal the subnet info from a binary FrostFS API protocol's format.
func (x putSubnetEvent) ReadInfo(info *subnet.Info) error {
return info.Unmarshal(x.ev.Info())
}
// handleSubnetCreation handles an event of subnet creation parsed via subnetevents.ParsePut.
//
// Validates the event using irsubnet.PutValidator. Logs message about (dis)agreement.
func (s *Server) handleSubnetCreation(e event.Event) {
putEv := e.(subnetevents.Put) // panic occurs only if we registered handler incorrectly
err := s.subnetHandler.putValidator.Assert(putSubnetEvent{
ev: putEv,
})
if err != nil {
s.log.Info(logs.InnerringDiscardSubnetCreation,
zap.String("reason", err.Error()),
)
return
}
notaryMainTx := putEv.NotaryMainTx()
isNotary := notaryMainTx != nil
if isNotary {
// re-sign notary request
err = s.morphClient.NotarySignAndInvokeTX(notaryMainTx)
} else {
// send new transaction
var prm morphsubnet.PutPrm
prm.SetID(putEv.ID())
prm.SetOwner(putEv.Owner())
prm.SetInfo(putEv.Info())
prm.SetTxHash(putEv.TxHash())
_, err = s.subnetHandler.morphClient.Put(prm)
}
if err != nil {
s.log.Error(logs.InnerringApproveSubnetCreation,
zap.Bool("notary", isNotary),
zap.String("error", err.Error()),
)
return
}
}
// catchSubnetRemoval catches an event of subnet removal from listener and queues the processing.
func (s *Server) catchSubnetRemoval(e event.Event) {
err := s.subnetHandler.workerPool.Submit(func() {
s.handleSubnetRemoval(e)
})
if err != nil {
s.log.Error(logs.InnerringSubnetRemovalHandlingFailure,
zap.String("error", err.Error()),
)
}
}
// handleSubnetRemoval handles event of subnet removal parsed via subnetevents.ParseDelete.
func (s *Server) handleSubnetRemoval(e event.Event) {
delEv := e.(subnetevents.Delete) // panic occurs only if we registered handler incorrectly
// handle subnet changes in netmap
candidates, err := s.netmapClient.GetCandidates()
if err != nil {
s.log.Error(logs.InnerringGettingNetmapCandidates,
zap.Error(err),
)
return
}
var removedID subnetid.ID
err = removedID.Unmarshal(delEv.ID())
if err != nil {
s.log.Error(logs.InnerringUnmarshallingRemovedSubnetID,
zap.String("error", err.Error()),
)
return
}
for i := range candidates {
s.processCandidate(delEv.TxHash(), removedID, candidates[i])
}
}
func (s *Server) processCandidate(txHash neogoutil.Uint256, removedID subnetid.ID, c netmap.NodeInfo) {
removeSubnet := false
log := s.log.With(
zap.String("public_key", netmap.StringifyPublicKey(c)),
zap.String("removed_subnet", removedID.String()),
)
err := c.IterateSubnets(func(id subnetid.ID) error {
if removedID.Equals(id) {
removeSubnet = true
return netmap.ErrRemoveSubnet
}
return nil
})
if err != nil {
log.Error(logs.InnerringIteratingNodesSubnets, zap.Error(err))
log.Debug(logs.InnerringRemovingNodeFromNetmapCandidates)
var updateStatePrm netmapclient.UpdatePeerPrm
updateStatePrm.SetKey(c.PublicKey())
updateStatePrm.SetHash(txHash)
err = s.netmapClient.UpdatePeerState(updateStatePrm)
if err != nil {
log.Error(logs.InnerringRemovingNodeFromCandidates,
zap.Error(err),
)
}
return
}
// remove subnet from node's information
// if it contains removed subnet
if removeSubnet {
log.Debug(logs.InnerringRemovingSubnetFromTheNode)
var addPeerPrm netmapclient.AddPeerPrm
addPeerPrm.SetNodeInfo(c)
addPeerPrm.SetHash(txHash)
err = s.netmapClient.AddPeer(addPeerPrm)
if err != nil {
log.Error(logs.InnerringUpdatingSubnetInfo,
zap.Error(err),
)
}
}
}

View file

@ -32,8 +32,6 @@ const (
NNSNetmapContractName = "netmap.frostfs"
// NNSProxyContractName is a name of the proxy contract in NNS.
NNSProxyContractName = "proxy.frostfs"
// NNSSubnetworkContractName is a name of the subnet contract in NNS.
NNSSubnetworkContractName = "subnet.frostfs"
// NNSGroupKeyName is a name for the FrostFS group key record in NNS.
NNSGroupKeyName = "group.frostfs"
)

View file

@ -1,87 +0,0 @@
package morphsubnet
import "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
// ManageAdminsPrm groups parameters of administer methods of Subnet contract.
//
// Zero value adds node admin. Subnet, key and group must be specified via setters.
type ManageAdminsPrm struct {
// remove or add admin
rm bool
// client or node admin
client bool
subnet []byte
admin []byte
group []byte
}
// SetRemove marks admin to be removed. By default, admin is added.
func (x *ManageAdminsPrm) SetRemove() {
x.rm = true
}
// SetClient switches to client admin. By default, node admin is modified.
func (x *ManageAdminsPrm) SetClient() {
x.client = true
}
// SetSubnet sets identifier of the subnet in a binary FrostFS API protocol format.
func (x *ManageAdminsPrm) SetSubnet(id []byte) {
x.subnet = id
}
// SetAdmin sets admin's public key in a binary format.
func (x *ManageAdminsPrm) SetAdmin(key []byte) {
x.admin = key
}
// SetGroup sets identifier of the client group in a binary FrostFS API protocol format.
// Makes sense only for client admins (see ManageAdminsPrm.SetClient).
func (x *ManageAdminsPrm) SetGroup(id []byte) {
x.group = id
}
// ManageAdminsRes groups the resulting values of node administer methods of Subnet contract.
type ManageAdminsRes struct{}
// ManageAdmins manages admin list of the FrostFS subnet through Subnet contract calls.
func (x Client) ManageAdmins(prm ManageAdminsPrm) (*ManageAdminsPrm, error) {
var method string
args := make([]any, 1, 3)
args[0] = prm.subnet
if prm.client {
args = append(args, prm.group, prm.admin)
if prm.rm {
method = removeClientAdminMethod
} else {
method = addClientAdminMethod
}
} else {
args = append(args, prm.admin)
if prm.rm {
method = removeNodeAdminMethod
} else {
method = addNodeAdminMethod
}
}
var prmInvoke client.InvokePrm
prmInvoke.SetMethod(method)
prmInvoke.SetArgs(args...)
err := x.client.Invoke(prmInvoke)
if err != nil {
return nil, err
}
return new(ManageAdminsPrm), nil
}

View file

@ -1,108 +0,0 @@
package morphsubnet
import (
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// Client represents Subnet contract client.
//
// Client should be preliminary initialized (see Init method).
type Client struct {
client *client.StaticClient
}
// InitPrm groups parameters of Client's initialization.
type InitPrm struct {
base *client.Client
addr util.Uint160
modeSet bool
mode Mode
}
const (
deleteMethod = "delete"
getMethod = "get"
putMethod = "put"
removeClientAdminMethod = "removeClientAdmin"
addClientAdminMethod = "addClientAdmin"
userAllowedMethod = "userAllowed"
removeUserMethod = "removeUser"
addUserMethod = "addUser"
removeNodeAdminMethod = "removeNodeAdmin"
addNodeAdminMethod = "addNodeAdmin"
nodeAllowedMethod = "nodeAllowed"
removeNodeMethod = "removeNode"
addNodeMethod = "addNode"
)
// SetBaseClient sets basic morph client.
func (x *InitPrm) SetBaseClient(base *client.Client) {
x.base = base
}
// SetContractAddress sets address of Subnet contract in FrostFS sidechain.
func (x *InitPrm) SetContractAddress(addr util.Uint160) {
x.addr = addr
}
// Mode regulates client work mode.
type Mode uint8
const (
_ Mode = iota
// NonNotary makes client to work in non-notary environment.
NonNotary
// NotaryAlphabet makes client to use its internal key for signing the notary requests.
NotaryAlphabet
// NotaryNonAlphabet makes client to not use its internal key for signing the notary requests.
NotaryNonAlphabet
)
// SetMode makes client to work with non-notary sidechain.
// By default, NonNotary is used.
func (x *InitPrm) SetMode(mode Mode) {
x.modeSet = true
x.mode = mode
}
// Init initializes client with specified parameters.
//
// Base client must be set.
func (x *Client) Init(prm InitPrm) error {
if prm.base == nil {
panic("missing base morph client")
}
if !prm.modeSet {
prm.mode = NonNotary
}
var opts []client.StaticClientOption
switch prm.mode {
default:
panic(fmt.Sprintf("invalid work mode %d", prm.mode))
case NonNotary:
case NotaryNonAlphabet:
opts = []client.StaticClientOption{client.TryNotary()}
case NotaryAlphabet:
opts = []client.StaticClientOption{client.TryNotary(), client.AsAlphabet()}
}
var err error
x.client, err = client.NewStatic(prm.base, prm.addr, 0, opts...)
return err
}

View file

@ -1,114 +0,0 @@
package morphsubnet
import (
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
)
// UserAllowedPrm groups parameters of UserAllowed method of Subnet contract.
type UserAllowedPrm struct {
args [2]any
}
// SetID sets identifier of the subnet in a binary FrostFS API protocol format.
func (x *UserAllowedPrm) SetID(id []byte) {
x.args[0] = id
}
// SetClient sets owner ID of the client that is being checked in a binary FrostFS API protocol format.
func (x *UserAllowedPrm) SetClient(id []byte) {
x.args[1] = id
}
// UserAllowedRes groups the resulting values of UserAllowed method of Subnet contract.
type UserAllowedRes struct {
result bool
}
// Allowed returns true iff the client is allowed to create containers in the subnet.
func (x UserAllowedRes) Allowed() bool {
return x.result
}
// UserAllowed checks if the user has access to the subnetwork.
func (x *Client) UserAllowed(prm UserAllowedPrm) (*UserAllowedRes, error) {
args := client.TestInvokePrm{}
args.SetMethod(userAllowedMethod)
args.SetArgs(prm.args[:]...)
res, err := x.client.TestInvoke(args)
if err != nil {
return nil, fmt.Errorf("could not make test invoke: %w", err)
}
if len(res) == 0 {
return nil, errEmptyResponse
}
result, err := client.BoolFromStackItem(res[0])
if err != nil {
return nil, err
}
return &UserAllowedRes{
result: result,
}, nil
}
// ManageClientsPrm groups parameters of client management in Subnet contract.
//
// Zero value adds subnet client. Subnet, group and client ID must be specified via setters.
type ManageClientsPrm struct {
// remove or add client
rm bool
args [3]any
}
// SetRemove marks client to be removed. By default, client is added.
func (x *ManageClientsPrm) SetRemove() {
x.rm = true
}
// SetSubnet sets identifier of the subnet in a binary FrostFS API protocol format.
func (x *ManageClientsPrm) SetSubnet(id []byte) {
x.args[0] = id
}
// SetGroup sets identifier of the client group in a binary FrostFS API protocol format.
func (x *ManageClientsPrm) SetGroup(id []byte) {
x.args[1] = id
}
// SetClient sets client's user ID in a binary FrostFS API protocol format.
func (x *ManageClientsPrm) SetClient(id []byte) {
x.args[2] = id
}
// ManageClientsRes groups the resulting values of client management methods of Subnet contract.
type ManageClientsRes struct{}
// ManageClients manages client list of the FrostFS subnet through Subnet contract calls.
func (x Client) ManageClients(prm ManageClientsPrm) (*ManageClientsRes, error) {
var method string
if prm.rm {
method = removeUserMethod
} else {
method = addUserMethod
}
var prmInvoke client.InvokePrm
prmInvoke.SetMethod(method)
prmInvoke.SetArgs(prm.args[:]...)
err := x.client.Invoke(prmInvoke)
if err != nil {
return nil, err
}
return new(ManageClientsRes), nil
}

View file

@ -1,40 +0,0 @@
package morphsubnet
import (
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// DeletePrm groups parameters of Delete method of Subnet contract.
type DeletePrm struct {
cliPrm client.InvokePrm
args [1]any
}
// SetTxHash sets hash of the transaction which spawned the notification.
// Ignore this parameter for new requests.
func (x *DeletePrm) SetTxHash(hash util.Uint256) {
x.cliPrm.SetHash(hash)
}
// SetID sets identifier of the subnet to be removed in a binary FrostFS API protocol format.
func (x *DeletePrm) SetID(id []byte) {
x.args[0] = id
}
// DeleteRes groups the resulting values of Delete method of Subnet contract.
type DeleteRes struct{}
// Delete removes subnet though the call of the corresponding method of the Subnet contract.
func (x Client) Delete(prm DeletePrm) (*DeleteRes, error) {
prm.cliPrm.SetMethod(deleteMethod)
prm.cliPrm.SetArgs(prm.args[:]...)
err := x.client.Invoke(prm.cliPrm)
if err != nil {
return nil, err
}
return new(DeleteRes), nil
}

View file

@ -1,55 +0,0 @@
package morphsubnet
import (
"errors"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
)
// GetPrm groups parameters of Get method of Subnet contract.
type GetPrm struct {
args [1]any
}
// SetID sets identifier of the subnet to be read in a binary FrostFS API protocol format.
func (x *GetPrm) SetID(id []byte) {
x.args[0] = id
}
// GetRes groups the resulting values of Get method of Subnet contract.
type GetRes struct {
info []byte
}
// Info returns information about the subnet in a binary format of FrostFS API protocol.
func (x GetRes) Info() []byte {
return x.info
}
var errEmptyResponse = errors.New("empty response")
// Get reads the subnet through the call of the corresponding method of the Subnet contract.
func (x *Client) Get(prm GetPrm) (*GetRes, error) {
var prmGet client.TestInvokePrm
prmGet.SetMethod(getMethod)
prmGet.SetArgs(prm.args[:]...)
res, err := x.client.TestInvoke(prmGet)
if err != nil {
return nil, err
}
if len(res) == 0 {
return nil, errEmptyResponse
}
data, err := client.BytesFromStackItem(res[0])
if err != nil {
return nil, err
}
return &GetRes{
info: data,
}, nil
}

View file

@ -1,58 +0,0 @@
package morphsubnet
import (
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
)
// NodeAllowedPrm groups parameters of NodeAllowed method of Subnet contract.
type NodeAllowedPrm struct {
cliPrm client.TestInvokePrm
args [2]any
}
// SetID sets identifier of the subnet of the node in a binary FrostFS API protocol format.
func (x *NodeAllowedPrm) SetID(id []byte) {
x.args[0] = id
}
// SetNode sets public key of the node that is being checked.
func (x *NodeAllowedPrm) SetNode(id []byte) {
x.args[1] = id
}
// NodeAllowedRes groups the resulting values of NodeAllowed method of Subnet contract.
type NodeAllowedRes struct {
result bool
}
// Allowed returns true iff the node is allowed to enter the subnet.
func (x NodeAllowedRes) Allowed() bool {
return x.result
}
// NodeAllowed checks if the node is included in the subnetwork.
func (x *Client) NodeAllowed(prm NodeAllowedPrm) (*NodeAllowedRes, error) {
prm.cliPrm.SetMethod(nodeAllowedMethod)
prm.cliPrm.SetArgs(prm.args[:]...)
res, err := x.client.TestInvoke(prm.cliPrm)
if err != nil {
return nil, fmt.Errorf("could not make test invoke: %w", err)
}
if len(res) == 0 {
return nil, errEmptyResponse
}
result, err := client.BoolFromStackItem(res[0])
if err != nil {
return nil, err
}
return &NodeAllowedRes{
result: result,
}, nil
}

View file

@ -1,54 +0,0 @@
package morphsubnet
import "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
// ManageNodesPrm groups parameters of node management in Subnet contract.
//
// Zero value adds node to subnet. Subnet and node IDs must be specified via setters.
type ManageNodesPrm struct {
// remove or add node
rm bool
args [2]any
}
// SetRemove marks node to be removed. By default, node is added.
func (x *ManageNodesPrm) SetRemove() {
x.rm = true
}
// SetSubnet sets identifier of the subnet in a binary NeoFS API protocol format.
func (x *ManageNodesPrm) SetSubnet(id []byte) {
x.args[0] = id
}
// SetNode sets node's public key in a binary format.
func (x *ManageNodesPrm) SetNode(id []byte) {
x.args[1] = id
}
// ManageNodesRes groups the resulting values of node management methods of Subnet contract.
type ManageNodesRes struct{}
// ManageNodes manages node list of the NeoFS subnet through Subnet contract calls.
func (x Client) ManageNodes(prm ManageNodesPrm) (*ManageNodesRes, error) {
var method string
if prm.rm {
method = removeNodeMethod
} else {
method = addNodeMethod
}
var prmInvoke client.InvokePrm
prmInvoke.SetMethod(method)
prmInvoke.SetArgs(prm.args[:]...)
err := x.client.Invoke(prmInvoke)
if err != nil {
return nil, err
}
return new(ManageNodesRes), nil
}

View file

@ -1,50 +0,0 @@
package morphsubnet
import (
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// PutPrm groups parameters of Put method of Subnet contract.
type PutPrm struct {
cliPrm client.InvokePrm
args [3]any
}
// SetTxHash sets hash of the transaction which spawned the notification.
// Ignore this parameter for new requests.
func (x *PutPrm) SetTxHash(hash util.Uint256) {
x.cliPrm.SetHash(hash)
}
// SetID sets identifier of the created subnet in a binary FrostFS API protocol format.
func (x *PutPrm) SetID(id []byte) {
x.args[0] = id
}
// SetOwner sets identifier of the subnet owner in a binary FrostFS API protocol format.
func (x *PutPrm) SetOwner(id []byte) {
x.args[1] = id
}
// SetInfo sets information about the created subnet in a binary FrostFS API protocol format.
func (x *PutPrm) SetInfo(id []byte) {
x.args[2] = id
}
// PutRes groups the resulting values of Put method of Subnet contract.
type PutRes struct{}
// Put creates subnet though the call of the corresponding method of the Subnet contract.
func (x Client) Put(prm PutPrm) (*PutRes, error) {
prm.cliPrm.SetMethod(putMethod)
prm.cliPrm.SetArgs(prm.args[:]...)
err := x.client.Invoke(prm.cliPrm)
if err != nil {
return nil, err
}
return new(PutRes), nil
}

View file

@ -1,63 +0,0 @@
package subnetevents
import (
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// Delete structures information about the notification generated by Delete method of Subnet contract.
type Delete struct {
txHash util.Uint256
id []byte
}
// MorphEvent implements Neo:Morph Event interface.
func (Delete) MorphEvent() {}
// ID returns identifier of the removed subnet in a binary format of NeoFS API protocol.
func (x Delete) ID() []byte {
return x.id
}
// TxHash returns hash of the transaction which thrown the notification event.
// Makes sense only in notary environments.
func (x Delete) TxHash() util.Uint256 {
return x.txHash
}
// ParseDelete parses the notification about the removal of a subnet which has been thrown
// by the appropriate method of the Subnet contract.
//
// Resulting event is of Delete type.
func ParseDelete(e *state.ContainedNotificationEvent) (event.Event, error) {
var (
ev Delete
err error
)
items, err := event.ParseStackArray(e)
if err != nil {
return nil, fmt.Errorf("parse stack array: %w", err)
}
const itemNumDelete = 1
if ln := len(items); ln != itemNumDelete {
return nil, event.WrongNumberOfParameters(itemNumDelete, ln)
}
// parse ID
ev.id, err = client.BytesFromStackItem(items[0])
if err != nil {
return nil, fmt.Errorf("id item: %w", err)
}
ev.txHash = e.Container
return ev, nil
}

View file

@ -1,42 +0,0 @@
package subnetevents_test
import (
"testing"
subnetevents "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/subnet"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)
func TestParseDelete(t *testing.T) {
id := []byte("id")
t.Run("wrong number of items", func(t *testing.T) {
prms := []stackitem.Item{
stackitem.NewByteArray(nil),
stackitem.NewByteArray(nil),
}
_, err := subnetevents.ParseDelete(createNotifyEventFromItems(prms))
require.Error(t, err)
})
t.Run("wrong id item", func(t *testing.T) {
_, err := subnetevents.ParseDelete(createNotifyEventFromItems([]stackitem.Item{
stackitem.NewMap(),
}))
require.Error(t, err)
})
t.Run("correct behavior", func(t *testing.T) {
ev, err := subnetevents.ParseDelete(createNotifyEventFromItems([]stackitem.Item{
stackitem.NewByteArray(id),
}))
require.NoError(t, err)
v := ev.(subnetevents.Delete)
require.Equal(t, id, v.ID())
})
}

View file

@ -1,147 +0,0 @@
package subnetevents
import (
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/network/payload"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// Put structures information about the notification generated by Put method of Subnet contract.
type Put struct {
notaryRequest *payload.P2PNotaryRequest
txHash util.Uint256
id []byte
owner []byte
info []byte
}
// MorphEvent implements Neo:Morph Event interface.
func (Put) MorphEvent() {}
// ID returns identifier of the creating subnet in a binary format of FrostFS API protocol.
func (x Put) ID() []byte {
return x.id
}
// Owner returns subnet owner's public key in a binary format.
func (x Put) Owner() []byte {
return x.owner
}
// Info returns information about the subnet in a binary format of FrostFS API protocol.
func (x Put) Info() []byte {
return x.info
}
// TxHash returns hash of the transaction which thrown the notification event.
// Makes sense only in notary environments.
func (x Put) TxHash() util.Uint256 {
return x.txHash
}
// NotaryMainTx returns main transaction of the request in the Notary service.
// Returns nil in non-notary environments.
func (x Put) NotaryMainTx() *transaction.Transaction {
if x.notaryRequest != nil {
return x.notaryRequest.MainTransaction
}
return nil
}
// number of items in notification about subnet creation.
const itemNumPut = 3
// ParsePut parses the notification about the creation of a subnet which has been thrown
// by the appropriate method of the subnet contract.
//
// Resulting event is of Put type.
func ParsePut(e *state.ContainedNotificationEvent) (event.Event, error) {
var (
put Put
err error
)
items, err := event.ParseStackArray(e)
if err != nil {
return nil, fmt.Errorf("parse stack array: %w", err)
}
if ln := len(items); ln != itemNumPut {
return nil, event.WrongNumberOfParameters(itemNumPut, ln)
}
// parse ID
put.id, err = client.BytesFromStackItem(items[0])
if err != nil {
return nil, fmt.Errorf("id item: %w", err)
}
// parse owner
put.owner, err = client.BytesFromStackItem(items[1])
if err != nil {
return nil, fmt.Errorf("owner item: %w", err)
}
// parse info about subnet
put.info, err = client.BytesFromStackItem(items[2])
if err != nil {
return nil, fmt.Errorf("info item: %w", err)
}
put.txHash = e.Container
return put, nil
}
// ParseNotaryPut parses the notary notification about the creation of a subnet which has been
// thrown by the appropriate method of the subnet contract.
//
// Resulting event is of Put type.
func ParseNotaryPut(e event.NotaryEvent) (event.Event, error) {
var put Put
put.notaryRequest = e.Raw()
if put.notaryRequest == nil {
panic(fmt.Sprintf("nil %T in notary environment", put.notaryRequest))
}
var (
err error
prms = e.Params()
)
if ln := len(prms); ln != itemNumPut {
return nil, event.WrongNumberOfParameters(itemNumPut, ln)
}
// parse info about subnet
put.info, err = event.BytesFromOpcode(prms[0])
if err != nil {
return nil, fmt.Errorf("info param: %w", err)
}
// parse owner
put.owner, err = event.BytesFromOpcode(prms[1])
if err != nil {
return nil, fmt.Errorf("creator param: %w", err)
}
// parse ID
put.id, err = event.BytesFromOpcode(prms[2])
if err != nil {
return nil, fmt.Errorf("id param: %w", err)
}
return put, nil
}

View file

@ -1,69 +0,0 @@
package subnetevents_test
import (
"testing"
subnetevents "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/subnet"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)
func TestParsePut(t *testing.T) {
var (
id = []byte("id")
owner = []byte("owner")
info = []byte("info")
)
t.Run("wrong number of items", func(t *testing.T) {
prms := []stackitem.Item{
stackitem.NewByteArray(nil),
stackitem.NewByteArray(nil),
}
_, err := subnetevents.ParsePut(createNotifyEventFromItems(prms))
require.Error(t, err)
})
t.Run("wrong id item", func(t *testing.T) {
_, err := subnetevents.ParsePut(createNotifyEventFromItems([]stackitem.Item{
stackitem.NewMap(),
}))
require.Error(t, err)
})
t.Run("wrong owner item", func(t *testing.T) {
_, err := subnetevents.ParsePut(createNotifyEventFromItems([]stackitem.Item{
stackitem.NewByteArray(id),
stackitem.NewMap(),
}))
require.Error(t, err)
})
t.Run("wrong info item", func(t *testing.T) {
_, err := subnetevents.ParsePut(createNotifyEventFromItems([]stackitem.Item{
stackitem.NewByteArray(id),
stackitem.NewByteArray(owner),
stackitem.NewMap(),
}))
require.Error(t, err)
})
t.Run("correct behavior", func(t *testing.T) {
ev, err := subnetevents.ParsePut(createNotifyEventFromItems([]stackitem.Item{
stackitem.NewByteArray(id),
stackitem.NewByteArray(owner),
stackitem.NewByteArray(info),
}))
require.NoError(t, err)
v := ev.(subnetevents.Put)
require.Equal(t, id, v.ID())
require.Equal(t, owner, v.Owner())
require.Equal(t, info, v.Info())
})
}

View file

@ -1,69 +0,0 @@
package subnetevents
import (
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// RemoveNode structure of subnet.RemoveNode notification from morph chain.
type RemoveNode struct {
subnetID []byte
nodeKey []byte
// txHash is used in notary environmental
// for calculating unique but same for
// all notification receivers values.
txHash util.Uint256
}
// MorphEvent implements Neo:Morph Event interface.
func (RemoveNode) MorphEvent() {}
// SubnetworkID returns a marshalled subnetID structure, defined in API.
func (rn RemoveNode) SubnetworkID() []byte { return rn.subnetID }
// Node is public key of the nodeKey that is being deleted.
func (rn RemoveNode) Node() []byte { return rn.nodeKey }
// TxHash returns hash of the TX with RemoveNode
// notification.
func (rn RemoveNode) TxHash() util.Uint256 { return rn.txHash }
const expectedItemNumRemoveNode = 2
// ParseRemoveNode parses notification into subnet event structure.
//
// Expects 2 stack items.
func ParseRemoveNode(e *state.ContainedNotificationEvent) (event.Event, error) {
var (
ev RemoveNode
err error
)
params, err := event.ParseStackArray(e)
if err != nil {
return nil, fmt.Errorf("could not parse stack items from notify event: %w", err)
}
if ln := len(params); ln != expectedItemNumRemoveNode {
return nil, event.WrongNumberOfParameters(expectedItemNumRemoveNode, ln)
}
ev.subnetID, err = client.BytesFromStackItem(params[0])
if err != nil {
return nil, fmt.Errorf("could not get raw subnetID: %w", err)
}
ev.nodeKey, err = client.BytesFromStackItem(params[1])
if err != nil {
return nil, fmt.Errorf("could not get raw public key of the node: %w", err)
}
ev.txHash = e.Container
return ev, nil
}

View file

@ -1,56 +0,0 @@
package subnetevents_test
import (
"testing"
. "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/subnet"
subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)
func TestParseRemoveNode(t *testing.T) {
t.Run("wrong number of arguments", func(t *testing.T) {
_, err := ParseRemoveNode(createNotifyEventFromItems([]stackitem.Item{}))
require.Error(t, err)
})
t.Run("invalid item type", func(t *testing.T) {
args := []stackitem.Item{stackitem.NewMap(), stackitem.Make(123)}
_, err := ParseRemoveNode(createNotifyEventFromItems(args))
require.Error(t, err)
})
subnetID := subnetid.ID{}
subnetID.SetNumeric(123)
rawSubnetID := subnetID.Marshal()
priv, err := keys.NewPrivateKey()
require.NoError(t, err)
pub := priv.PublicKey()
t.Run("good", func(t *testing.T) {
args := []stackitem.Item{stackitem.NewByteArray(rawSubnetID), stackitem.Make(pub.Bytes())}
e, err := ParseRemoveNode(createNotifyEventFromItems(args))
require.NoError(t, err)
gotRaw := e.(RemoveNode).SubnetworkID()
require.NoError(t, err)
require.Equal(t, rawSubnetID, gotRaw)
require.Equal(t, pub.Bytes(), e.(RemoveNode).Node())
})
}
func createNotifyEventFromItems(items []stackitem.Item) *state.ContainedNotificationEvent {
return &state.ContainedNotificationEvent{
NotificationEvent: state.NotificationEvent{
Item: stackitem.NewArray(items),
},
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -54,9 +54,6 @@ message NodeInfo {
// attributes it's a string presenting floating point number with comma or
// point delimiter for decimal part. In the Network Map it will be saved as
// 64-bit unsigned integer representing number of minimal token fractions.
// * Subnet \
// String ID of Node's storage subnet. There can be only one subnet served
// by the Storage Node.
// * Locode \
// Node's geographic location in
// [UN/LOCODE](https://www.unece.org/cefact/codesfortrade/codes_index.html)

Binary file not shown.

Binary file not shown.

Binary file not shown.