[#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 <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2021-05-31 21:14:30 +03:00 committed by Alex Vanin
parent 48d934ecf8
commit ed80f704d0
4 changed files with 215 additions and 42 deletions

View file

@ -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,

View file

@ -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()))
}
}

View file

@ -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()),
)
}
}

View file

@ -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
}