diff --git a/pkg/morph/client/netmap/client.go b/pkg/morph/client/netmap/client.go index f0b0015160..805e5b1f45 100644 --- a/pkg/morph/client/netmap/client.go +++ b/pkg/morph/client/netmap/client.go @@ -34,6 +34,7 @@ type cfg struct { addPeerMethod, // add peer method name for invocation newEpochMethod, // new epoch method name for invocation netMapMethod, // get network map method name + netMapCandidatesMethod, // get network candidates method name snapshotMethod, // get network map snapshot method name epochSnapshotMethod, // get network map snapshot by epoch method name updateStateMethod, // update state method name for invocation @@ -44,30 +45,32 @@ type cfg struct { } const ( - defaultAddPeerMethod = "addPeer" // default add peer method name - defaultNewEpochMethod = "newEpoch" // default new epoch method name - defaultNetMapMethod = "netmap" // default get network map method name - defaultSnapshotMethod = "snapshot" // default get network map snapshot method name - defaultUpdateStateMethod = "updateState" // default update state method name - defaultEpochMethod = "epoch" // default get epoch number method name - defaultSetInnerRingMethod = "updateInnerRing" // default set innerring method name - defaultSetConfigMethod = "setConfig" // default get config value method name - defaultConfigMethod = "config" // default get config value method name + defaultAddPeerMethod = "addPeer" // default add peer method name + defaultConfigMethod = "config" // default get config value method name + defaultEpochMethod = "epoch" // default get epoch number method name + defaultNetMapCandidateMethod = "netmapCandidates" // default get network candidates method name + defaultNetMapMethod = "netmap" // default get network map method name + defaultNewEpochMethod = "newEpoch" // default new epoch method name + defaultSetConfigMethod = "setConfig" // default get config value method name + defaultSetInnerRingMethod = "updateInnerRing" // default set innerring method name + defaultSnapshotMethod = "snapshot" // default get network map snapshot method name + defaultUpdateStateMethod = "updateState" // default update state method name defaultEpochSnapshotMethod = "snapshotByEpoch" // default get network map snapshot by epoch method name ) func defaultConfig() *cfg { return &cfg{ - addPeerMethod: defaultAddPeerMethod, - newEpochMethod: defaultNewEpochMethod, - netMapMethod: defaultNetMapMethod, - snapshotMethod: defaultSnapshotMethod, - epochSnapshotMethod: defaultEpochSnapshotMethod, - updateStateMethod: defaultUpdateStateMethod, - epochMethod: defaultEpochMethod, - setConfigMethod: defaultSetConfigMethod, - configMethod: defaultConfigMethod, + addPeerMethod: defaultAddPeerMethod, + configMethod: defaultConfigMethod, + epochMethod: defaultEpochMethod, + netMapCandidatesMethod: defaultNetMapCandidateMethod, + netMapMethod: defaultNetMapMethod, + newEpochMethod: defaultNewEpochMethod, + setConfigMethod: defaultSetConfigMethod, + snapshotMethod: defaultSnapshotMethod, + updateStateMethod: defaultUpdateStateMethod, + epochSnapshotMethod: defaultEpochSnapshotMethod, } } diff --git a/pkg/morph/client/netmap/netmap.go b/pkg/morph/client/netmap/netmap.go index 1a0dd83920..4e7662da9f 100644 --- a/pkg/morph/client/netmap/netmap.go +++ b/pkg/morph/client/netmap/netmap.go @@ -36,7 +36,55 @@ type EpochSnapshotValues struct { *GetNetMapValues } -const nodeInfoFixedPrmNumber = 1 +// GetNetMapCandidatesArgs groups the arguments +// of get network map candidates test invoke call. +type GetNetMapCandidatesArgs struct { +} + +// GetNetMapCandidatesValues groups the stack parameters +// returned by get network map candidates test invoke. +type GetNetMapCandidatesValues struct { + netmapNodes []*PeerWithState +} + +func (g GetNetMapCandidatesValues) NetmapNodes() []*PeerWithState { + return g.netmapNodes +} + +// State is an enumeration of various states of the NeoFS node. +type State int64 + +const ( + // Undefined is unknown state. + Undefined State = iota + + // Online is network unavailable state. + Online + + // Offline is an active state in the network. + Offline +) + +// PeerWithState groups information about peer +// and its state in network map. +type PeerWithState struct { + peer []byte + state State +} + +func (ps PeerWithState) State() State { + return ps.state +} + +func (ps PeerWithState) Peer() []byte { + return ps.peer +} + +const ( + nodeInfoFixedPrmNumber = 1 + + peerWithStateFixedPrmNumber = 2 +) // SetDiff sets argument for snapshot method of // netmap contract. @@ -107,6 +155,85 @@ func (c *Client) EpochSnapshot(args EpochSnapshotArgs) (*EpochSnapshotValues, er }, nil } +func (c *Client) Candidates(_ GetNetMapCandidatesArgs) (*GetNetMapCandidatesValues, error) { + prms, err := c.client.TestInvoke( + c.netMapCandidatesMethod, + ) + if err != nil { + return nil, fmt.Errorf("could not perform test invocation (%s): %w", c.netMapCandidatesMethod, err) + } + + candVals, err := peersWithStateFromStackItems(prms, c.netMapCandidatesMethod) + if err != nil { + return nil, fmt.Errorf("could not parse contract response: %w", err) + } + + return candVals, nil +} + +func peersWithStateFromStackItems(stack []stackitem.Item, method string) (*GetNetMapCandidatesValues, error) { + if ln := len(stack); ln != 1 { + return nil, fmt.Errorf("unexpected stack item count (%s): %d", method, ln) + } + + netmapNodes, err := client.ArrayFromStackItem(stack[0]) + if err != nil { + return nil, fmt.Errorf("could not get stack item array from stack item (%s): %w", method, err) + } + + res := &GetNetMapCandidatesValues{ + netmapNodes: make([]*PeerWithState, 0, len(netmapNodes)), + } + + for i := range netmapNodes { + node, err := peerWithStateFromStackItem(netmapNodes[i]) + if err != nil { + return nil, fmt.Errorf("could not parse stack item (Peer #%d): %w", i, err) + } + + res.netmapNodes = append(res.netmapNodes, node) + } + + return res, nil +} + +func peerWithStateFromStackItem(prm stackitem.Item) (*PeerWithState, error) { + prms, err := client.ArrayFromStackItem(prm) + if err != nil { + return nil, fmt.Errorf("could not get stack item array (PeerWithState): %w", err) + } else if ln := len(prms); ln != peerWithStateFixedPrmNumber { + return nil, fmt.Errorf( + "unexpected stack item count (PeerWithState): expected %d, has %d", + peerWithStateFixedPrmNumber, + ln, + ) + } + + var res PeerWithState + + // peer + if res.peer, err = peerInfoFromStackItem(prms[0]); err != nil { + return nil, fmt.Errorf("could not get bytes from 'node' field of PeerWithState: %w", err) + } + + // state + state, err := client.IntFromStackItem(prms[1]) + if err != nil { + return nil, fmt.Errorf("could not get int from 'state' field of PeerWithState: %w", err) + } + + switch state { + case 1: + res.state = Online + case 2: + res.state = Offline + default: + res.state = Undefined + } + + return &res, nil +} + func peersFromStackItems(stack []stackitem.Item, method string) (*GetNetMapValues, error) { if ln := len(stack); ln != 1 { return nil, fmt.Errorf("unexpected stack item count (%s): %d",