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.container", "10")
|
||||||
cfg.SetDefault("workers.alphabet", "10")
|
cfg.SetDefault("workers.alphabet", "10")
|
||||||
cfg.SetDefault("workers.reputation", "10")
|
cfg.SetDefault("workers.reputation", "10")
|
||||||
|
cfg.SetDefault("workers.subnet", "10")
|
||||||
|
|
||||||
cfg.SetDefault("netmap_cleaner.enabled", true)
|
cfg.SetDefault("netmap_cleaner.enabled", true)
|
||||||
cfg.SetDefault("netmap_cleaner.threshold", 3)
|
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/mr-tron/base58 v1.2.0
|
||||||
github.com/multiformats/go-multiaddr v0.4.0
|
github.com/multiformats/go-multiaddr v0.4.0
|
||||||
github.com/nspcc-dev/hrw v1.0.9
|
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-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/nspcc-dev/tzhash v1.4.0
|
||||||
github.com/panjf2000/ants/v2 v2.4.0
|
github.com/panjf2000/ants/v2 v2.4.0
|
||||||
github.com/paulmach/orb v0.2.2
|
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.
|
// TODO: unify with workers.
|
||||||
runners []func(chan<- error)
|
runners []func(chan<- error)
|
||||||
|
|
||||||
|
subnetHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
chainParams struct {
|
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")
|
log.Info("no Control server endpoint specified, service is disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
server.initSubnet(subnetConfig{
|
||||||
|
queueSize: cfg.GetUint32("workers.subnet"),
|
||||||
|
})
|
||||||
|
|
||||||
return server, nil
|
return server, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
timerEvent "github.com/nspcc-dev/neofs-node/pkg/innerring/timers"
|
timerEvent "github.com/nspcc-dev/neofs-node/pkg/innerring/timers"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/morph/event"
|
"github.com/nspcc-dev/neofs-node/pkg/morph/event"
|
||||||
netmapEvent "github.com/nspcc-dev/neofs-node/pkg/morph/event/netmap"
|
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"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ func (np *Processor) handleCleanupTick(ev event.Event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (np *Processor) handleRemoveNode(ev event.Event) {
|
func (np *Processor) handleRemoveNode(ev event.Event) {
|
||||||
removeNode := ev.(subnet.RemoveNode)
|
removeNode := ev.(subnetevents.RemoveNode)
|
||||||
|
|
||||||
np.log.Info("notification",
|
np.log.Info("notification",
|
||||||
zap.String("type", "remove node from subnet"),
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package subnet
|
package subnetevents_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"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/rpc/response/result/subscriptions"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"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"
|
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue