From 30fba8170916d773f2281b85174d1f9d9a1efc45 Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Fri, 1 Sep 2023 19:40:55 +0300 Subject: [PATCH] ir: Check that just bootstrapped SN is available Closes #2475. Signed-off-by: Pavel Karpy --- CHANGELOG.md | 1 + pkg/core/client/client.go | 1 + pkg/innerring/innerring.go | 1 + .../processors/netmap/process_peers.go | 95 +++++++++++++++++++ pkg/innerring/processors/netmap/processor.go | 14 +++ pkg/network/cache/multi.go | 9 ++ 6 files changed, 121 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1d5c0ad90f..b41f0bf5947 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ minor release, the component will be purged, so be prepared (see `Updating` sect - `neofs-cli object nodes` command to get SNs for an object (#2512) - Fetching container estimations via iterators to prevent NeoVM stack overflow (#2173) - `neofs-adm morph netmap-candidates` CLI command (#1889) +- SN network validation (is available by its announced addresses) on bootstrap by the IR (#2475) ### Fixed - `neo-go` RPC connection loss handling (#1337) diff --git a/pkg/core/client/client.go b/pkg/core/client/client.go index 13901fd3385..651b172a2e4 100644 --- a/pkg/core/client/client.go +++ b/pkg/core/client/client.go @@ -29,6 +29,7 @@ type Client interface { AnnounceLocalTrust(ctx context.Context, epoch uint64, trusts []reputationSDK.Trust, prm client.PrmAnnounceLocalTrust) error AnnounceIntermediateTrust(ctx context.Context, epoch uint64, trust reputationSDK.PeerToPeerTrust, prm client.PrmAnnounceIntermediateTrust) error ExecRaw(f func(client *rawclient.Client) error) error + EndpointInfo(ctx context.Context, prm client.PrmEndpointInfo) (*client.ResEndpointInfo, error) Close() error } diff --git a/pkg/innerring/innerring.go b/pkg/innerring/innerring.go index 6195cfd3ce7..6170c996ff6 100644 --- a/pkg/innerring/innerring.go +++ b/pkg/innerring/innerring.go @@ -756,6 +756,7 @@ func New(ctx context.Context, log *zap.Logger, cfg *viper.Viper, errChan chan<- locodeValidator, ), NodeStateSettings: netSettings, + ClientSource: clientCache, }) if err != nil { return nil, err diff --git a/pkg/innerring/processors/netmap/process_peers.go b/pkg/innerring/processors/netmap/process_peers.go index f8013058f2e..371c0b650de 100644 --- a/pkg/innerring/processors/netmap/process_peers.go +++ b/pkg/innerring/processors/netmap/process_peers.go @@ -1,10 +1,17 @@ package netmap import ( + "bytes" + "context" "encoding/hex" + "errors" + "fmt" + clientcore "github.com/nspcc-dev/neofs-node/pkg/core/client" + netmapCore "github.com/nspcc-dev/neofs-node/pkg/core/netmap" netmapclient "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap" netmapEvent "github.com/nspcc-dev/neofs-node/pkg/morph/event/netmap" + "github.com/nspcc-dev/neofs-sdk-go/client" "github.com/nspcc-dev/neofs-sdk-go/netmap" "go.uber.org/zap" ) @@ -37,6 +44,37 @@ func (np *Processor) processAddPeer(ev netmapEvent.AddPeer) { return } + var info clientcore.NodeInfo + err = clientcore.NodeInfoFromRawNetmapElement(&info, netmapCore.Node(nodeInfo)) + if err != nil { + np.log.Warn("could not convert node info: %w", zap.Error(err)) + return + } + + c, err := np.clientSource.Get(info) + if err != nil { + np.log.Warn("could not create client to the node: %w", + zap.Error(err), + zap.String("node", hex.EncodeToString(info.PublicKey()))) + return + } + + res, err := c.EndpointInfo(context.Background(), client.PrmEndpointInfo{}) + if err != nil { + np.log.Warn("could not ping node with `EndpointInfo`", + zap.Error(err), + zap.String("node", hex.EncodeToString(info.PublicKey()))) + return + } + + err = compareNodeInfos(nodeInfo, res.NodeInfo()) + if err != nil && !errors.Is(err, errUnknownDifference) { + np.log.Warn("correct node info, but `EndpointInfo` result differs", + zap.Error(err), + zap.String("node", hex.EncodeToString(info.PublicKey()))) + return + } + // validate and update node info err = np.nodeValidator.VerifyAndUpdate(&nodeInfo) if err != nil { @@ -116,3 +154,60 @@ func (np *Processor) processUpdatePeer(ev netmapEvent.UpdatePeer) { np.log.Error("can't invoke netmap.UpdatePeer", zap.Error(err)) } } + +var errUnknownDifference = errors.New("announced and received information differ but not critical") + +func compareNodeInfos(niExp, niGot netmap.NodeInfo) error { + // if a node bootstraps the first time it is OK + // to be OFFLINE but send ONLINE bootstrap requests + niGot.SetOnline() + if exp, got := niExp.Marshal(), niGot.Marshal(); bytes.Equal(exp, got) { + return nil + } + + var err error + + if exp, got := niExp.Hash(), niGot.Hash(); exp != got { + return fmt.Errorf("hash: got %d, expect %d", got, exp) + } + + niExp.SortAttributes() + niGot.SortAttributes() + if exp, got := niExp.NumberOfAttributes(), niGot.NumberOfAttributes(); exp != got { + return fmt.Errorf("attr number: got %d, expect %d", got, exp) + } + + niExp.IterateAttributes(func(key, value string) { + vGot := niGot.Attribute(key) + if vGot != value { + err = fmt.Errorf("non-equal %s attribute: got %s, expect %s", key, vGot, value) + } + }) + if err != nil { + return err + } + + if exp, got := niExp.NumberOfNetworkEndpoints(), niGot.NumberOfNetworkEndpoints(); exp != got { + return fmt.Errorf("address number: got %d, expect %d", got, exp) + } + + expAddrM := make(map[string]struct{}, niExp.NumberOfAttributes()) + niExp.IterateNetworkEndpoints(func(s string) bool { + expAddrM[s] = struct{}{} + return false + }) + + niGot.IterateNetworkEndpoints(func(s string) bool { + if _, ok := expAddrM[s]; !ok { + err = fmt.Errorf("got unexpected address: %s", s) + return true + } + + return false + }) + if err != nil { + return err + } + + return errUnknownDifference +} diff --git a/pkg/innerring/processors/netmap/processor.go b/pkg/innerring/processors/netmap/processor.go index af5e149d9cb..cdbd98e1937 100644 --- a/pkg/innerring/processors/netmap/processor.go +++ b/pkg/innerring/processors/netmap/processor.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/nspcc-dev/neo-go/pkg/core/mempoolevent" + clientcore "github.com/nspcc-dev/neofs-node/pkg/core/client" "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/netmap/nodevalidation/state" "github.com/nspcc-dev/neofs-node/pkg/morph/client/container" nmClient "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap" @@ -49,6 +50,11 @@ type ( VerifyAndUpdate(*netmap.NodeInfo) error } + // ClientSource must provide working [clientcore.Client]. + ClientSource interface { + Get(clientcore.NodeInfo) (clientcore.Client, error) + } + // Processor of events produced by network map contract // and new epoch ticker, because it is related to contract. Processor struct { @@ -71,6 +77,8 @@ type ( nodeValidator NodeValidator nodeStateSettings state.NetworkSettings + + clientSource ClientSource } // Params of the processor constructor. @@ -93,6 +101,8 @@ type ( NodeValidator NodeValidator NodeStateSettings state.NetworkSettings + + ClientSource ClientSource } ) @@ -126,6 +136,8 @@ func New(p *Params) (*Processor, error) { return nil, errors.New("ir/netmap: node validator is not set") case p.NodeStateSettings == nil: return nil, errors.New("ir/netmap: node state settings is not set") + case p.ClientSource == nil: + return nil, errors.New("ir/netmap: client source is not set") } p.Log.Debug("netmap worker pool", zap.Int("size", p.PoolSize)) @@ -155,6 +167,8 @@ func New(p *Params) (*Processor, error) { nodeValidator: p.NodeValidator, nodeStateSettings: p.NodeStateSettings, + + clientSource: p.ClientSource, }, nil } diff --git a/pkg/network/cache/multi.go b/pkg/network/cache/multi.go index 08ed4c3f4c0..92e67070240 100644 --- a/pkg/network/cache/multi.go +++ b/pkg/network/cache/multi.go @@ -279,6 +279,15 @@ func (x *multiClient) ObjectSearchInit(ctx context.Context, containerID cid.ID, return } +func (x *multiClient) EndpointInfo(ctx context.Context, prm client.PrmEndpointInfo) (res *client.ResEndpointInfo, err error) { + err = x.iterateClients(ctx, func(c clientcore.Client) error { + res, err = c.EndpointInfo(ctx, prm) + return err + }) + + return +} + func (x *multiClient) AnnounceLocalTrust(ctx context.Context, epoch uint64, trusts []reputationSDK.Trust, prm client.PrmAnnounceLocalTrust) error { return x.iterateClients(ctx, func(c clientcore.Client) error { return c.AnnounceLocalTrust(ctx, epoch, trusts, prm)