forked from TrueCloudLab/frostfs-node
[#973] ir: Listen and process Put/Delete events of Subnet contract
Define notification events, implement parsers. Add morph client of Subnet contract. Listen, verify and approve events in Inner Ring app. Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
parent
214c2bd0cb
commit
41eaa1e246
22 changed files with 1274 additions and 6 deletions
|
@ -80,6 +80,7 @@ func defaultConfiguration(cfg *viper.Viper) {
|
|||
cfg.SetDefault("workers.container", "10")
|
||||
cfg.SetDefault("workers.alphabet", "10")
|
||||
cfg.SetDefault("workers.reputation", "10")
|
||||
cfg.SetDefault("workers.subnet", "10")
|
||||
|
||||
cfg.SetDefault("netmap_cleaner.enabled", true)
|
||||
cfg.SetDefault("netmap_cleaner.threshold", 3)
|
||||
|
|
4
go.mod
4
go.mod
|
@ -11,9 +11,9 @@ require (
|
|||
github.com/mr-tron/base58 v1.2.0
|
||||
github.com/multiformats/go-multiaddr v0.4.0
|
||||
github.com/nspcc-dev/hrw v1.0.9
|
||||
github.com/nspcc-dev/neo-go v0.97.4-pre.0.20211123163659-b25c3775e847
|
||||
github.com/nspcc-dev/neo-go v0.97.4-pre.0.20211126130906-87f5113c031b
|
||||
github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211124141318-d93828f46514
|
||||
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20211126123811-1dde267424aa
|
||||
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20211126125208-279a5a1e0bfe
|
||||
github.com/nspcc-dev/tzhash v1.4.0
|
||||
github.com/panjf2000/ants/v2 v2.4.0
|
||||
github.com/paulmach/orb v0.2.2
|
||||
|
|
BIN
go.sum
BIN
go.sum
Binary file not shown.
|
@ -119,6 +119,8 @@ type (
|
|||
//
|
||||
// TODO: unify with workers.
|
||||
runners []func(chan<- error)
|
||||
|
||||
subnetHandler
|
||||
}
|
||||
|
||||
chainParams struct {
|
||||
|
@ -857,6 +859,10 @@ func New(ctx context.Context, log *zap.Logger, cfg *viper.Viper) (*Server, error
|
|||
log.Info("no Control server endpoint specified, service is disabled")
|
||||
}
|
||||
|
||||
server.initSubnet(subnetConfig{
|
||||
queueSize: cfg.GetUint32("workers.subnet"),
|
||||
})
|
||||
|
||||
return server, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
timerEvent "github.com/nspcc-dev/neofs-node/pkg/innerring/timers"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/morph/event"
|
||||
netmapEvent "github.com/nspcc-dev/neofs-node/pkg/morph/event/netmap"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/morph/event/subnet"
|
||||
subnetevents "github.com/nspcc-dev/neofs-node/pkg/morph/event/subnet"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -102,7 +102,7 @@ func (np *Processor) handleCleanupTick(ev event.Event) {
|
|||
}
|
||||
|
||||
func (np *Processor) handleRemoveNode(ev event.Event) {
|
||||
removeNode := ev.(subnet.RemoveNode)
|
||||
removeNode := ev.(subnetevents.RemoveNode)
|
||||
|
||||
np.log.Info("notification",
|
||||
zap.String("type", "remove node from subnet"),
|
||||
|
|
22
pkg/innerring/processors/subnet/common.go
Normal file
22
pkg/innerring/processors/subnet/common.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package subnetevents
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
subnetid "github.com/nspcc-dev/neofs-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)
|
||||
}
|
19
pkg/innerring/processors/subnet/common_test.go
Normal file
19
pkg/innerring/processors/subnet/common_test.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package subnetevents
|
||||
|
||||
import subnetid "github.com/nspcc-dev/neofs-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
|
||||
}
|
44
pkg/innerring/processors/subnet/delete.go
Normal file
44
pkg/innerring/processors/subnet/delete.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
package subnetevents
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||
)
|
||||
|
||||
// Delete represents notification about NeoFS subnet removal.
|
||||
// Generated by a contract when intending to delete a subnet.
|
||||
type Delete interface {
|
||||
// Contains ID of the subnet to be removed.
|
||||
eventWithID
|
||||
}
|
||||
|
||||
// DeleteValidator asserts intent to remove a subnet.
|
||||
type DeleteValidator struct{}
|
||||
|
||||
// Assert processes the attempt to remove a subnet. Approves the removal through nil return.
|
||||
//
|
||||
// All read errors of Delete are forwarded.
|
||||
//
|
||||
// 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 DeleteValidator) Assert(event Delete) 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 removal
|
||||
if subnetid.IsZero(id) {
|
||||
return zeroSubnetOp{
|
||||
op: "removal",
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
44
pkg/innerring/processors/subnet/delete_test.go
Normal file
44
pkg/innerring/processors/subnet/delete_test.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
package subnetevents
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||
)
|
||||
|
||||
type delete struct {
|
||||
idEvent
|
||||
}
|
||||
|
||||
func TestDeleteValidator_Assert(t *testing.T) {
|
||||
var (
|
||||
v DeleteValidator
|
||||
|
||||
e delete
|
||||
|
||||
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.SetNumber(idNum)
|
||||
|
||||
err = v.Assert(e)
|
||||
require.NoError(t, err)
|
||||
}
|
82
pkg/innerring/processors/subnet/put.go
Normal file
82
pkg/innerring/processors/subnet/put.go
Normal file
|
@ -0,0 +1,82 @@
|
|||
package subnetevents
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/subnet"
|
||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||
)
|
||||
|
||||
// Put represents notification about NeoFS subnet creation.
|
||||
// Generated by a contract when intending to create a subnet.
|
||||
type Put interface {
|
||||
// Contains ID of the subnet to be created.
|
||||
eventWithID
|
||||
|
||||
// ReadCreator reads user ID of the subnet creator.
|
||||
// Returns an error if ID is missing.
|
||||
ReadCreator(id *owner.ID) error
|
||||
|
||||
// ReadInfo reads information about subnet to be created.
|
||||
ReadInfo(info *subnet.Info) error
|
||||
}
|
||||
|
||||
// PutValidator asserts intent to create a subnet.
|
||||
type PutValidator struct{}
|
||||
|
||||
// errDiffOwner is returned when subnet owners differ.
|
||||
var errDiffOwner = errors.New("diff subnet owners")
|
||||
|
||||
// errDiffID is returned when subnet IDs differ.
|
||||
var errDiffID = errors.New("diff subnet IDs")
|
||||
|
||||
// Assert processes the attempt to create a subnet. Approves the creation through nil return.
|
||||
//
|
||||
// All read errors of Put are forwarded.
|
||||
//
|
||||
// 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 NeoFS system
|
||||
var creator owner.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 explicit ID equals to the one from info
|
||||
if !subnet.IDEquals(info, id) {
|
||||
return errDiffID
|
||||
}
|
||||
|
||||
// check if explicit creator equals to the one from info
|
||||
if !subnet.IsOwner(info, creator) {
|
||||
return errDiffOwner
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
115
pkg/innerring/processors/subnet/put_test.go
Normal file
115
pkg/innerring/processors/subnet/put_test.go
Normal file
|
@ -0,0 +1,115 @@
|
|||
package subnetevents
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
ownertest "github.com/nspcc-dev/neofs-sdk-go/owner/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/subnet"
|
||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||
)
|
||||
|
||||
type put struct {
|
||||
idEvent
|
||||
|
||||
creator owner.ID
|
||||
|
||||
creatorErr error
|
||||
|
||||
info subnet.Info
|
||||
|
||||
infoErr error
|
||||
}
|
||||
|
||||
func (x put) ReadCreator(id *owner.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.SetNumber(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.SetNumber(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
|
||||
var creator2 owner.ID
|
||||
|
||||
creator2 = *ownertest.GenerateID()
|
||||
|
||||
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)
|
||||
}
|
332
pkg/innerring/subnet.go
Normal file
332
pkg/innerring/subnet.go
Normal file
|
@ -0,0 +1,332 @@
|
|||
package innerring
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/mempoolevent"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
irsubnet "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/subnet"
|
||||
morphsubnet "github.com/nspcc-dev/neofs-node/pkg/morph/client/subnet"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/morph/event"
|
||||
subnetevents "github.com/nspcc-dev/neofs-node/pkg/morph/event/subnet"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/util"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/subnet"
|
||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||
"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
|
||||
|
||||
delValidator irsubnet.DeleteValidator
|
||||
}
|
||||
|
||||
// configuration of subnet component.
|
||||
type subnetConfig struct {
|
||||
queueSize uint32
|
||||
}
|
||||
|
||||
// makes IR server to catch Subnet notifications from sidechain listener,
|
||||
// and to release 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 (
|
||||
// subnet creation
|
||||
subnetCreateEvName = "put"
|
||||
// subnet removal
|
||||
subnetRemoveEvName = "delete"
|
||||
)
|
||||
|
||||
// makes IR server to listen notifications of Subnet contract.
|
||||
// All required resources must be initialized before (initSubnet).
|
||||
// Works in one of two modes (configured): notary and non-notary.
|
||||
//
|
||||
// All handlers are executed only if local node is an alphabet one.
|
||||
//
|
||||
// Events (notary):
|
||||
// * put (parser: subnetevents.ParseNotaryPut, handler: catchSubnetCreation);
|
||||
// * delete (parser: subnetevents.ParseNotaryDelete, handler: catchSubnetRemoval).
|
||||
//
|
||||
// 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)
|
||||
|
||||
listenEvent := 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
|
||||
listenEvent(subnetCreateEvName, subnetevents.ParseNotaryPut, s.onlyAlphabetEventHandler(s.catchSubnetCreation))
|
||||
// subnet removal
|
||||
listenEvent(subnetRemoveEvName, subnetevents.ParseNotaryDelete, s.onlyAlphabetEventHandler(s.catchSubnetRemoval))
|
||||
}
|
||||
|
||||
func (s *Server) listenSubnetWithoutNotary() {
|
||||
var (
|
||||
parserInfo event.NotificationParserInfo
|
||||
handlerInfo event.NotificationHandlerInfo
|
||||
)
|
||||
|
||||
parserInfo.SetScriptHash(s.contracts.subnet)
|
||||
handlerInfo.SetScriptHash(s.contracts.subnet)
|
||||
|
||||
listenEvent := func(notifyName string, parser event.NotificationParser, handler event.Handler) {
|
||||
notifyTyp := event.TypeFromString(notifyName)
|
||||
|
||||
parserInfo.SetType(notifyTyp)
|
||||
handlerInfo.SetType(notifyTyp)
|
||||
|
||||
parserInfo.SetParser(parser)
|
||||
handlerInfo.SetHandler(handler)
|
||||
|
||||
s.morphListener.SetNotificationParser(parserInfo)
|
||||
s.morphListener.RegisterNotificationHandler(handlerInfo)
|
||||
}
|
||||
|
||||
// subnet creation
|
||||
listenEvent(subnetCreateEvName, subnetevents.ParsePut, s.onlyAlphabetEventHandler(s.catchSubnetCreation))
|
||||
// subnet removal
|
||||
listenEvent(subnetRemoveEvName, subnetevents.ParseDelete, s.onlyAlphabetEventHandler(s.catchSubnetRemoval))
|
||||
}
|
||||
|
||||
// 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("subnet creation queue failure",
|
||||
zap.String("error", err.Error()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// implements irsubnet.Put event interface required by irsubnet.PutValidator.
|
||||
type putSubnetEvent struct {
|
||||
ev subnetevents.Put
|
||||
}
|
||||
|
||||
// ReadID unmarshals subnet ID from a binary NeoFS 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 subnet creator from a binary NeoFS API protocol's format.
|
||||
// Returns an error if byte array is empty.
|
||||
func (x putSubnetEvent) ReadCreator(id *owner.ID) error {
|
||||
data := x.ev.Owner()
|
||||
|
||||
if len(data) == 0 {
|
||||
return errMissingSubnetOwner
|
||||
}
|
||||
|
||||
key, err := keys.NewPublicKeyFromBytes(data, elliptic.P256())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wal, err := owner.NEO3WalletFromPublicKey((*ecdsa.PublicKey)(key))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// it would be better if we could do it not like this
|
||||
*id = *owner.NewIDFromNeo3Wallet(wal)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadInfo unmarshal subnet info from a binary NeoFS API protocol's format.
|
||||
func (x putSubnetEvent) ReadInfo(info *subnet.Info) error {
|
||||
return info.Unmarshal(x.ev.Info())
|
||||
}
|
||||
|
||||
// handleSubnetCreation handles 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("discard subnet creation",
|
||||
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("approve subnet creation",
|
||||
zap.Bool("notary", isNotary),
|
||||
zap.String("error", err.Error()),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// catchSubnetRemoval catches event of subnet removal from listener and queues the processing.
|
||||
func (s *Server) catchSubnetRemoval(e event.Event) {
|
||||
err := s.subnetHandler.workerPool.Submit(func() {
|
||||
s.handleSubnetCreation(e)
|
||||
})
|
||||
if err != nil {
|
||||
s.log.Error("subnet removal queue failure",
|
||||
zap.String("error", err.Error()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// implements irsubnet.Delete event interface required by irsubnet.DeleteValidator.
|
||||
type deleteSubnetEvent struct {
|
||||
ev subnetevents.Delete
|
||||
}
|
||||
|
||||
// ReadID unmarshals subnet ID from a binary NeoFS API protocol's format.
|
||||
func (x deleteSubnetEvent) ReadID(id *subnetid.ID) error {
|
||||
return id.Unmarshal(x.ev.ID())
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
err := s.subnetHandler.delValidator.Assert(deleteSubnetEvent{
|
||||
ev: delEv,
|
||||
})
|
||||
if err != nil {
|
||||
s.log.Info("discard subnet removal",
|
||||
zap.String("reason", err.Error()),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
notaryMainTx := delEv.NotaryMainTx()
|
||||
|
||||
isNotary := notaryMainTx != nil
|
||||
if isNotary {
|
||||
// re-sign notary request
|
||||
err = s.morphClient.NotarySignAndInvokeTX(notaryMainTx)
|
||||
} else {
|
||||
// send new transaction
|
||||
var prm morphsubnet.DeletePrm
|
||||
|
||||
prm.SetID(delEv.ID())
|
||||
prm.SetTxHash(delEv.TxHash())
|
||||
|
||||
_, err = s.subnetHandler.morphClient.Delete(prm)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.log.Error("approve subnet removal",
|
||||
zap.Bool("notary", isNotary),
|
||||
zap.String("error", err.Error()),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: handle removal of the subnet in netmap candidates
|
||||
}
|
91
pkg/morph/client/subnet/client.go
Normal file
91
pkg/morph/client/subnet/client.go
Normal file
|
@ -0,0 +1,91 @@
|
|||
package morphsubnet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/morph/client"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// SetBaseClient sets basic morph client.
|
||||
func (x *InitPrm) SetBaseClient(base *client.Client) {
|
||||
x.base = base
|
||||
}
|
||||
|
||||
// SetContractAddress sets address of Subnet contract in NeoFS 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
|
||||
|
||||
lastMode
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
40
pkg/morph/client/subnet/delete.go
Normal file
40
pkg/morph/client/subnet/delete.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package morphsubnet
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/morph/client"
|
||||
)
|
||||
|
||||
// DeletePrm groups parameters of Delete method of Subnet contract.
|
||||
type DeletePrm struct {
|
||||
cliPrm client.InvokePrm
|
||||
|
||||
args [1]interface{}
|
||||
}
|
||||
|
||||
// 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 NeoFS API protocol format.
|
||||
func (x *DeletePrm) SetID(id []byte) {
|
||||
x.args[0] = id
|
||||
}
|
||||
|
||||
// DeleteRes groups 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("delete")
|
||||
prm.cliPrm.SetArgs(prm.args[:]...)
|
||||
|
||||
err := x.client.Invoke(prm.cliPrm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return new(DeleteRes), nil
|
||||
}
|
57
pkg/morph/client/subnet/get.go
Normal file
57
pkg/morph/client/subnet/get.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package morphsubnet
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/nspcc-dev/neofs-node/pkg/morph/client"
|
||||
)
|
||||
|
||||
// GetPrm groups parameters of Get method of Subnet contract.
|
||||
type GetPrm struct {
|
||||
cliPrm client.TestInvokePrm
|
||||
|
||||
args [1]interface{}
|
||||
}
|
||||
|
||||
// SetID sets identifier of the subnet to be read in a binary NeoFS API protocol format.
|
||||
func (x *GetPrm) SetID(id []byte) {
|
||||
x.args[0] = id
|
||||
}
|
||||
|
||||
// GetRes groups resulting values of Get method of Subnet contract.
|
||||
type GetRes struct {
|
||||
info []byte
|
||||
}
|
||||
|
||||
// Info returns information about the subnet in a binary format of NeoFS 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) {
|
||||
prm.cliPrm.SetMethod("get")
|
||||
prm.cliPrm.SetArgs(prm.args[:]...)
|
||||
|
||||
res, err := x.client.TestInvoke(prm.cliPrm)
|
||||
if err != nil {
|
||||
fmt.Println()
|
||||
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
|
||||
}
|
50
pkg/morph/client/subnet/put.go
Normal file
50
pkg/morph/client/subnet/put.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package morphsubnet
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/morph/client"
|
||||
)
|
||||
|
||||
// PutPrm groups parameters of Put method of Subnet contract.
|
||||
type PutPrm struct {
|
||||
cliPrm client.InvokePrm
|
||||
|
||||
args [3]interface{}
|
||||
}
|
||||
|
||||
// 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 NeoFS API protocol format.
|
||||
func (x *PutPrm) SetID(id []byte) {
|
||||
x.args[0] = id
|
||||
}
|
||||
|
||||
// SetOwner sets identifier of the subnet owner in a binary NeoFS API protocol format.
|
||||
func (x *PutPrm) SetOwner(id []byte) {
|
||||
x.args[1] = id
|
||||
}
|
||||
|
||||
// SetInfo sets information about the created subnet in a binary NeoFS API protocol format.
|
||||
func (x *PutPrm) SetInfo(id []byte) {
|
||||
x.args[2] = id
|
||||
}
|
||||
|
||||
// PutRes groups 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("put")
|
||||
prm.cliPrm.SetArgs(prm.args[:]...)
|
||||
|
||||
err := x.client.Invoke(prm.cliPrm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return new(PutRes), nil
|
||||
}
|
109
pkg/morph/event/subnet/delete.go
Normal file
109
pkg/morph/event/subnet/delete.go
Normal file
|
@ -0,0 +1,109 @@
|
|||
package subnetevents
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"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/rpc/response/result/subscriptions"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/morph/client"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/morph/event"
|
||||
)
|
||||
|
||||
// Delete structures information about the notification generated by Delete method of Subnet contract.
|
||||
type Delete struct {
|
||||
notaryRequest *payload.P2PNotaryRequest
|
||||
|
||||
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 non-notary environments (see NotaryMainTx).
|
||||
func (x Delete) 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 Delete) NotaryMainTx() *transaction.Transaction {
|
||||
if x.notaryRequest != nil {
|
||||
return x.notaryRequest.MainTransaction
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const itemNumDelete = 1
|
||||
|
||||
// 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 *subscriptions.NotificationEvent) (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)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// ParseNotaryDelete parses the notary 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 ParseNotaryDelete(e event.NotaryEvent) (event.Event, error) {
|
||||
var ev Delete
|
||||
|
||||
ev.notaryRequest = e.Raw()
|
||||
if ev.notaryRequest == nil {
|
||||
panic(fmt.Sprintf("nil %T in notary environment", ev.notaryRequest))
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
|
||||
prms = e.Params()
|
||||
)
|
||||
|
||||
if ln := len(prms); ln != itemNumDelete {
|
||||
return nil, event.WrongNumberOfParameters(itemNumDelete, ln)
|
||||
}
|
||||
|
||||
ev.id, err = event.BytesFromOpcode(prms[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("id param: %w", err)
|
||||
}
|
||||
|
||||
ev.notaryRequest = e.Raw()
|
||||
|
||||
return ev, nil
|
||||
}
|
42
pkg/morph/event/subnet/delete_test.go
Normal file
42
pkg/morph/event/subnet/delete_test.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package subnetevents_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
subnetevents "github.com/nspcc-dev/neofs-node/pkg/morph/event/subnet"
|
||||
"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())
|
||||
})
|
||||
}
|
144
pkg/morph/event/subnet/put.go
Normal file
144
pkg/morph/event/subnet/put.go
Normal file
|
@ -0,0 +1,144 @@
|
|||
package subnetevents
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"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/rpc/response/result/subscriptions"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/morph/client"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/morph/event"
|
||||
)
|
||||
|
||||
// Put structures information about the notification generated by Put method of Subnet contract.
|
||||
type Put struct {
|
||||
notaryRequest *payload.P2PNotaryRequest
|
||||
|
||||
txHash util.Uint256
|
||||
|
||||
id []byte
|
||||
|
||||
ownerID []byte
|
||||
|
||||
info []byte
|
||||
}
|
||||
|
||||
// MorphEvent implements Neo:Morph Event interface.
|
||||
func (Put) MorphEvent() {}
|
||||
|
||||
// ID returns identifier of the creating subnet in a binary format of NeoFS API protocol.
|
||||
func (x Put) ID() []byte {
|
||||
return x.id
|
||||
}
|
||||
|
||||
// Owner returns subnet owner's ID in a binary format of NeoFS API protocol.
|
||||
func (x Put) Owner() []byte {
|
||||
return x.ownerID
|
||||
}
|
||||
|
||||
// Info returns information about the subnet in a binary format of NeoFS 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 non-notary environments (see NotaryMainTx).
|
||||
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 *subscriptions.NotificationEvent) (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.ownerID, err = client.BytesFromStackItem(items[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("owner item: %w", err)
|
||||
}
|
||||
|
||||
// parse public key
|
||||
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)
|
||||
}
|
||||
|
||||
put.info, err = event.BytesFromOpcode(prms[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("info param: %w", err)
|
||||
}
|
||||
|
||||
put.ownerID, err = event.BytesFromOpcode(prms[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creator param: %w", err)
|
||||
}
|
||||
|
||||
put.id, err = event.BytesFromOpcode(prms[2])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("id param: %w", err)
|
||||
}
|
||||
|
||||
return put, nil
|
||||
}
|
69
pkg/morph/event/subnet/put_test.go
Normal file
69
pkg/morph/event/subnet/put_test.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
package subnetevents_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
subnetevents "github.com/nspcc-dev/neofs-node/pkg/morph/event/subnet"
|
||||
"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())
|
||||
})
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package subnet
|
||||
package subnetevents
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package subnet
|
||||
package subnetevents_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result/subscriptions"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
. "github.com/nspcc-dev/neofs-node/pkg/morph/event/subnet"
|
||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue