From ed80f704d0cecf8773ee730647ac3136f04f7117 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 31 May 2021 21:14:30 +0300 Subject: [PATCH] [#556] innerring/neofs: Process Bind/Unbind events Make IR processor of NeoFS contract to handle `Bind`/`Unbind` notification events. The processor verifies the format of wallet script hash and public keys, and call NeoFS ID client wrapper in order to approve adding/removing keys from NeoFS account. Signed-off-by: Leonard Lyubich --- pkg/innerring/innerring.go | 1 + pkg/innerring/processors/neofs/handlers.go | 32 +++++ .../processors/neofs/process_bind.go | 110 +++++++++++++++++ pkg/innerring/processors/neofs/processor.go | 114 +++++++++++------- 4 files changed, 215 insertions(+), 42 deletions(-) create mode 100644 pkg/innerring/processors/neofs/process_bind.go diff --git a/pkg/innerring/innerring.go b/pkg/innerring/innerring.go index 1a829023..ef3f6381 100644 --- a/pkg/innerring/innerring.go +++ b/pkg/innerring/innerring.go @@ -602,6 +602,7 @@ func New(ctx context.Context, log *zap.Logger, cfg *viper.Viper) (*Server, error NeoFSContract: server.contracts.neofs, BalanceContract: server.contracts.balance, NetmapContract: server.contracts.netmap, + NeoFSIDContract: server.contracts.neofsID, MorphClient: server.morphClient, EpochState: server, AlphabetState: server, diff --git a/pkg/innerring/processors/neofs/handlers.go b/pkg/innerring/processors/neofs/handlers.go index 486a5425..ef9ccdce 100644 --- a/pkg/innerring/processors/neofs/handlers.go +++ b/pkg/innerring/processors/neofs/handlers.go @@ -72,3 +72,35 @@ func (np *Processor) handleConfig(ev event.Event) { zap.Int("capacity", np.pool.Cap())) } } + +func (np *Processor) handleBind(ev event.Event) { + e := ev.(neofsEvent.Bind) + np.log.Info("notification", + zap.String("type", "bind"), + ) + + // send event to the worker pool + + err := np.pool.Submit(func() { np.processBind(e) }) + if err != nil { + // there system can be moved into controlled degradation stage + np.log.Warn("neofs processor worker pool drained", + zap.Int("capacity", np.pool.Cap())) + } +} + +func (np *Processor) handleUnbind(ev event.Event) { + e := ev.(neofsEvent.Unbind) + np.log.Info("notification", + zap.String("type", "unbind"), + ) + + // send event to the worker pool + + err := np.pool.Submit(func() { np.processBind(e) }) + if err != nil { + // there system can be moved into controlled degradation stage + np.log.Warn("neofs processor worker pool drained", + zap.Int("capacity", np.pool.Cap())) + } +} diff --git a/pkg/innerring/processors/neofs/process_bind.go b/pkg/innerring/processors/neofs/process_bind.go new file mode 100644 index 00000000..a05d6436 --- /dev/null +++ b/pkg/innerring/processors/neofs/process_bind.go @@ -0,0 +1,110 @@ +package neofs + +import ( + "crypto/elliptic" + "fmt" + + "github.com/mr-tron/base58" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neofs-node/pkg/morph/event/neofs" + "go.uber.org/zap" +) + +type bindCommon interface { + User() []byte + Keys() [][]byte +} + +func (np *Processor) processBind(e bindCommon) { + if !np.alphabetState.IsAlphabet() { + np.log.Info("non alphabet mode, ignore bind") + return + } + + c := &bindCommonContext{ + bindCommon: e, + } + + _, c.bind = e.(neofs.Bind) + + err := np.checkBindCommon(c) + if err != nil { + np.log.Error("invalid manage key event", + zap.Bool("bind", c.bind), + zap.String("error", err.Error()), + ) + + return + } + + np.approveBindCommon(c) +} + +type bindCommonContext struct { + bindCommon + + bind bool + + scriptHash util.Uint160 +} + +func (np *Processor) checkBindCommon(e *bindCommonContext) error { + var err error + + e.scriptHash, err = util.Uint160DecodeBytesBE(e.User()) + if err != nil { + return err + } + + curve := elliptic.P256() + + for _, key := range e.Keys() { + _, err = keys.NewPublicKeyFromBytes(key, curve) + if err != nil { + return err + } + } + + return nil +} + +func (np *Processor) approveBindCommon(e *bindCommonContext) { + // calculate wallet address + // TODO: implement some utilities in API Go lib to do it + scriptHash := e.User() + + u160, err := util.Uint160DecodeBytesBE(scriptHash) + if err != nil { + np.log.Error("could not decode script hash from bytes", + zap.String("error", err.Error()), + ) + + return + } + + wallet, err := base58.Decode(address.Uint160ToString(u160)) + if err != nil { + np.log.Error("could not decode wallet address", + zap.String("error", err.Error()), + ) + + return + } + + err = np.neofsIDClient.ManageKeys(wallet, e.Keys(), e.bind) + if err != nil { + var typ string + + if e.bind { + typ = "bind" + } else { + typ = "unbind" + } + + np.log.Error(fmt.Sprintf("could not approve %s", typ), + zap.String("error", err.Error()), + ) + } +} diff --git a/pkg/innerring/processors/neofs/processor.go b/pkg/innerring/processors/neofs/processor.go index 58047912..814cc19a 100644 --- a/pkg/innerring/processors/neofs/processor.go +++ b/pkg/innerring/processors/neofs/processor.go @@ -10,6 +10,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neofs-node/pkg/innerring/config" "github.com/nspcc-dev/neofs-node/pkg/morph/client" + neofsid "github.com/nspcc-dev/neofs-node/pkg/morph/client/neofsid/wrapper" "github.com/nspcc-dev/neofs-node/pkg/morph/event" neofsEvent "github.com/nspcc-dev/neofs-node/pkg/morph/event/neofs" "github.com/panjf2000/ants/v2" @@ -39,6 +40,7 @@ type ( neofsContract util.Uint160 balanceContract util.Uint160 netmapContract util.Uint160 + neofsIDContract util.Uint160 morphClient *client.Client epochState EpochState alphabetState AlphabetState @@ -49,6 +51,8 @@ type ( mintEmitThreshold uint64 mintEmitValue fixedn.Fixed8 gasBalanceThreshold int64 + + neofsIDClient *neofsid.ClientWrapper } // Params of the processor constructor. @@ -58,6 +62,7 @@ type ( NeoFSContract util.Uint160 BalanceContract util.Uint160 NetmapContract util.Uint160 + NeoFSIDContract util.Uint160 MorphClient *client.Client EpochState EpochState AlphabetState AlphabetState @@ -75,6 +80,8 @@ const ( withdrawNotification = "Withdraw" chequeNotification = "Cheque" configNotification = "SetConfig" + bindNotification = "Bind" + unbindNotification = "Unbind" ) // New creates neofs mainnet contract processor instance. @@ -106,6 +113,11 @@ func New(p *Params) (*Processor, error) { return nil, fmt.Errorf("ir/neofs: can't create LRU cache for gas emission: %w", err) } + clientWrapper, err := neofsid.NewFromMorph(p.MorphClient, p.NeoFSIDContract, p.FeeProvider.SideChainFee()) + if err != nil { + return nil, fmt.Errorf("could not create NeoFS ID client wrapper: %w", err) + } + return &Processor{ log: p.Log, pool: pool, @@ -122,75 +134,93 @@ func New(p *Params) (*Processor, error) { mintEmitThreshold: p.MintEmitThreshold, mintEmitValue: p.MintEmitValue, gasBalanceThreshold: p.GasBalanceThreshold, + + neofsIDClient: clientWrapper, }, nil } // ListenerParsers for the 'event.Listener' event producer. func (np *Processor) ListenerParsers() []event.ParserInfo { - var parsers []event.ParserInfo + var ( + parsers = make([]event.ParserInfo, 0, 6) + + p event.ParserInfo + ) + + p.SetScriptHash(np.neofsContract) // deposit event - deposit := event.ParserInfo{} - deposit.SetType(depositNotification) - deposit.SetScriptHash(np.neofsContract) - deposit.SetParser(neofsEvent.ParseDeposit) - parsers = append(parsers, deposit) + p.SetType(event.TypeFromString(depositNotification)) + p.SetParser(neofsEvent.ParseDeposit) + parsers = append(parsers, p) // withdraw event - withdraw := event.ParserInfo{} - withdraw.SetType(withdrawNotification) - withdraw.SetScriptHash(np.neofsContract) - withdraw.SetParser(neofsEvent.ParseWithdraw) - parsers = append(parsers, withdraw) + p.SetType(event.TypeFromString(withdrawNotification)) + p.SetParser(neofsEvent.ParseWithdraw) + parsers = append(parsers, p) // cheque event - cheque := event.ParserInfo{} - cheque.SetType(chequeNotification) - cheque.SetScriptHash(np.neofsContract) - cheque.SetParser(neofsEvent.ParseCheque) - parsers = append(parsers, cheque) + p.SetType(event.TypeFromString(chequeNotification)) + p.SetParser(neofsEvent.ParseCheque) + parsers = append(parsers, p) // config event - config := event.ParserInfo{} - config.SetType(configNotification) - config.SetScriptHash(np.neofsContract) - config.SetParser(neofsEvent.ParseConfig) - parsers = append(parsers, config) + p.SetType(event.TypeFromString(configNotification)) + p.SetParser(neofsEvent.ParseConfig) + parsers = append(parsers, p) + + // bind event + p.SetType(event.TypeFromString(bindNotification)) + p.SetParser(neofsEvent.ParseBind) + parsers = append(parsers, p) + + // unbind event + p.SetType(event.TypeFromString(unbindNotification)) + p.SetParser(neofsEvent.ParseUnbind) + parsers = append(parsers, p) return parsers } // ListenerHandlers for the 'event.Listener' event producer. func (np *Processor) ListenerHandlers() []event.HandlerInfo { - var handlers []event.HandlerInfo + var ( + handlers = make([]event.HandlerInfo, 0, 6) + + h event.HandlerInfo + ) + + h.SetScriptHash(np.neofsContract) // deposit handler - deposit := event.HandlerInfo{} - deposit.SetType(depositNotification) - deposit.SetScriptHash(np.neofsContract) - deposit.SetHandler(np.handleDeposit) - handlers = append(handlers, deposit) + h.SetType(event.TypeFromString(depositNotification)) + h.SetHandler(np.handleDeposit) + handlers = append(handlers, h) // withdraw handler - withdraw := event.HandlerInfo{} - withdraw.SetType(withdrawNotification) - withdraw.SetScriptHash(np.neofsContract) - withdraw.SetHandler(np.handleWithdraw) - handlers = append(handlers, withdraw) + h.SetType(event.TypeFromString(withdrawNotification)) + h.SetHandler(np.handleWithdraw) + handlers = append(handlers, h) // cheque handler - cheque := event.HandlerInfo{} - cheque.SetType(chequeNotification) - cheque.SetScriptHash(np.neofsContract) - cheque.SetHandler(np.handleCheque) - handlers = append(handlers, cheque) + h.SetType(event.TypeFromString(chequeNotification)) + h.SetHandler(np.handleCheque) + handlers = append(handlers, h) // config handler - config := event.HandlerInfo{} - config.SetType(configNotification) - config.SetScriptHash(np.neofsContract) - config.SetHandler(np.handleConfig) - handlers = append(handlers, config) + h.SetType(event.TypeFromString(configNotification)) + h.SetHandler(np.handleConfig) + handlers = append(handlers, h) + + // bind handler + h.SetType(event.TypeFromString(bindNotification)) + h.SetHandler(np.handleBind) + handlers = append(handlers, h) + + // unbind handler + h.SetType(event.TypeFromString(unbindNotification)) + h.SetHandler(np.handleUnbind) + handlers = append(handlers, h) return handlers }