forked from TrueCloudLab/frostfs-node
ad7ad12a0c
There is a need to work with a set of Neo RPC nodes in order not to depend on the failure of some nodes while others are active. Support "multi-client" mode of morph `Client` entity. If instance is not "multi-client", it works as before. Constructor `New` creates multi-client, and each method performs iterating over the fixed set of endpoints until success. Opened client connections are cached (without eviction for now). Storage (as earlier) and IR (from now) nodes can be configured with multiple Neo endpoints. As above, `New` creates multi-client instance, so we don't need initialization changes on app-side. `Wait` and `GetDesignateHash` methods of `Client` return an error from now to detect connection errors. `NotaryEnabled` method is removed as unused. Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
152 lines
4.4 KiB
Go
152 lines
4.4 KiB
Go
package governance
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
"github.com/nspcc-dev/neofs-node/pkg/morph/client"
|
|
neofscontract "github.com/nspcc-dev/neofs-node/pkg/morph/client/neofs/wrapper"
|
|
nmWrapper "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap/wrapper"
|
|
"github.com/nspcc-dev/neofs-node/pkg/morph/event"
|
|
"github.com/nspcc-dev/neofs-node/pkg/morph/event/rolemanagement"
|
|
"github.com/panjf2000/ants/v2"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// ProcessorPoolSize limits pool size for governance Processor. Processor manages
|
|
// governance sync tasks. This process must not be interrupted by other sync
|
|
// operation, so we limit pool size for processor to one.
|
|
const ProcessorPoolSize = 1
|
|
|
|
type (
|
|
// AlphabetState is a callback interface for innerring global state.
|
|
AlphabetState interface {
|
|
IsAlphabet() bool
|
|
}
|
|
|
|
// Voter is a callback interface for alphabet contract voting.
|
|
Voter interface {
|
|
VoteForSidechainValidator(keys keys.PublicKeys) error
|
|
}
|
|
|
|
// EpochState is a callback interface for innerring global state.
|
|
EpochState interface {
|
|
EpochCounter() uint64
|
|
}
|
|
|
|
// IRFetcher is a callback interface for innerring keys.
|
|
// Implementation must take into account availability of
|
|
// the notary contract.
|
|
IRFetcher interface {
|
|
InnerRingKeys() (keys.PublicKeys, error)
|
|
}
|
|
|
|
// Processor of events related to governance in the network.
|
|
Processor struct {
|
|
log *zap.Logger
|
|
pool *ants.Pool
|
|
neofsClient *neofscontract.ClientWrapper
|
|
netmapClient *nmWrapper.Wrapper
|
|
|
|
alphabetState AlphabetState
|
|
epochState EpochState
|
|
voter Voter
|
|
irFetcher IRFetcher
|
|
|
|
mainnetClient *client.Client
|
|
morphClient *client.Client
|
|
|
|
notaryDisabled bool
|
|
|
|
designate util.Uint160
|
|
}
|
|
|
|
// Params of the processor constructor.
|
|
Params struct {
|
|
Log *zap.Logger
|
|
|
|
AlphabetState AlphabetState
|
|
EpochState EpochState
|
|
Voter Voter
|
|
IRFetcher IRFetcher
|
|
|
|
MorphClient *client.Client
|
|
MainnetClient *client.Client
|
|
NeoFSClient *neofscontract.ClientWrapper
|
|
NetmapClient *nmWrapper.Wrapper
|
|
|
|
NotaryDisabled bool
|
|
}
|
|
)
|
|
|
|
// New creates balance contract processor instance.
|
|
func New(p *Params) (*Processor, error) {
|
|
switch {
|
|
case p.Log == nil:
|
|
return nil, errors.New("ir/governance: logger is not set")
|
|
case p.MainnetClient == nil:
|
|
return nil, errors.New("ir/governance: neo:mainnet client is not set")
|
|
case p.MorphClient == nil:
|
|
return nil, errors.New("ir/governance: neo:sidechain client is not set")
|
|
case p.AlphabetState == nil:
|
|
return nil, errors.New("ir/governance: global state is not set")
|
|
case p.EpochState == nil:
|
|
return nil, errors.New("ir/governance: global state is not set")
|
|
case p.Voter == nil:
|
|
return nil, errors.New("ir/governance: global state is not set")
|
|
case p.IRFetcher == nil:
|
|
return nil, errors.New("ir/governance: innerring keys fetcher is not set")
|
|
}
|
|
|
|
pool, err := ants.NewPool(ProcessorPoolSize, ants.WithNonblocking(true))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ir/governance: can't create worker pool: %w", err)
|
|
}
|
|
|
|
// result is cached by neo-go, so we can pre-calc it
|
|
designate, err := p.MainnetClient.GetDesignateHash()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not get designate hash: %w", err)
|
|
}
|
|
|
|
return &Processor{
|
|
log: p.Log,
|
|
pool: pool,
|
|
neofsClient: p.NeoFSClient,
|
|
netmapClient: p.NetmapClient,
|
|
alphabetState: p.AlphabetState,
|
|
epochState: p.EpochState,
|
|
voter: p.Voter,
|
|
irFetcher: p.IRFetcher,
|
|
mainnetClient: p.MainnetClient,
|
|
morphClient: p.MorphClient,
|
|
notaryDisabled: p.NotaryDisabled,
|
|
designate: designate,
|
|
}, nil
|
|
}
|
|
|
|
// ListenerParsers for the 'event.Listener' event producer.
|
|
func (gp *Processor) ListenerParsers() []event.ParserInfo {
|
|
var pi event.ParserInfo
|
|
pi.SetScriptHash(gp.designate)
|
|
pi.SetType(event.TypeFromString(native.DesignationEventName))
|
|
pi.SetParser(rolemanagement.ParseDesignate)
|
|
return []event.ParserInfo{pi}
|
|
}
|
|
|
|
// ListenerHandlers for the 'event.Listener' event producer.
|
|
func (gp *Processor) ListenerHandlers() []event.HandlerInfo {
|
|
var hi event.HandlerInfo
|
|
hi.SetScriptHash(gp.designate)
|
|
hi.SetType(event.TypeFromString(native.DesignationEventName))
|
|
hi.SetHandler(gp.HandleAlphabetSync)
|
|
return []event.HandlerInfo{hi}
|
|
}
|
|
|
|
// TimersHandlers for the 'Timers' event producer.
|
|
func (gp *Processor) TimersHandlers() []event.HandlerInfo {
|
|
return nil
|
|
}
|