From 3a0e76bcc983b13923a8d34225a966e72ef4bbee Mon Sep 17 00:00:00 2001 From: Kirill Pisarev Date: Thu, 20 Feb 2025 21:20:16 +0100 Subject: [PATCH 1/9] feat: add SessionGridTopology and dependencies to calculate neighbors --- dot/parachain/grid/grid_topology.go | 274 +++++++++++++++++++++++ dot/parachain/grid/grid_topology_test.go | 124 ++++++++++ 2 files changed, 398 insertions(+) create mode 100644 dot/parachain/grid/grid_topology.go create mode 100644 dot/parachain/grid/grid_topology_test.go diff --git a/dot/parachain/grid/grid_topology.go b/dot/parachain/grid/grid_topology.go new file mode 100644 index 0000000000..30050ee0a8 --- /dev/null +++ b/dot/parachain/grid/grid_topology.go @@ -0,0 +1,274 @@ +package grid + +import ( + "fmt" + parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" + "github.com/ChainSafe/gossamer/dot/types" + "github.com/libp2p/go-libp2p/core/peer" + "math" +) + +// TopologyPeerInfo is an information about the peer in the gossip topology +type TopologyPeerInfo struct { + // Peers is a list pf peers this peer knows about + Peers peer.IDSlice + /// ValidatorIndex is the index of the validator in the discovery keys of the corresponding + /// `SessionInfo`. This can extend _beyond_ the set of active parachain validators. + ValidatorIndex parachaintypes.ValidatorIndex + /// DiscoveryID is the authority discovery public key of the validator in the corresponding + /// `SessionInfo`. + DiscoveryID types.AuthorityID +} + +// SessionGridTopology is topology representation for session +type SessionGridTopology struct { + // Peers List of all peers ids in session. Represented as "hashset" for fast lookup. + Peers map[peer.ID]struct{} + /// canonicalShuffling is the canonical shuffling of validators for the session. + CanonicalShuffling []TopologyPeerInfo + /// shuffledIndices is an array mapping validator indices to their indices in the + /// shuffling itself. This has the same size as the number of validators + /// in the session. + ShuffledIndices []uint +} + +// NewSessionGridTopology creates a new SessionGridTopology +// Peers populated with peer ids from canonicalShuffling argument. +// canonicalShuffling set of authorities for this session. within one session it can have only one key. +func NewSessionGridTopology(shuffledIndices []uint, canonicalShuffling []TopologyPeerInfo) *SessionGridTopology { + peers := make(map[peer.ID]struct{}) + for _, peerInfo := range canonicalShuffling { + for _, p := range peerInfo.Peers { + peers[p] = struct{}{} + } + } + return &SessionGridTopology{ + Peers: peers, + CanonicalShuffling: canonicalShuffling, + ShuffledIndices: shuffledIndices, + } +} + +// UpdateAuthoritiesIDs Updates the known peer ids in SessionGridTopology for the passed authorities ids. +// Between sessions validators can update their authorityID because of key rotation, peerID might be changed as well. +// hence there could be multiple AuthorityID associated with a peerID. +// Updates Peers hashset with new peer id if the peer is in the grid topology. +// Returns true if the peer is in the grid topology. +func (gt *SessionGridTopology) UpdateAuthoritiesIDs(peer peer.ID, discoveryIDs map[types.AuthorityID]struct{}) bool { + updated := false + if _, ok := gt.Peers[peer]; !ok { + for i := range gt.CanonicalShuffling { + p := >.CanonicalShuffling[i] + if _, ok := discoveryIDs[p.DiscoveryID]; ok { + gt.Peers[peer] = struct{}{} + p.Peers = append(p.Peers, peer) + updated = true + } + } + } + return updated +} + +// IsValidator returns true is given peerID is in the session +func (gt *SessionGridTopology) IsValidator(peer peer.ID) bool { + _, ok := gt.Peers[peer] + return ok +} + +func (gt *SessionGridTopology) ComputeGridNeighborsFor(vi parachaintypes.ValidatorIndex) (*GridNeighbors, error) { + if len(gt.ShuffledIndices) != len(gt.CanonicalShuffling) { + return nil, fmt.Errorf("grid topology malformed: "+ + "shuffledIndices length %d is not equal to canonicalShuffling length %d", + len(gt.ShuffledIndices), + len(gt.CanonicalShuffling), + ) + } + + shuffledIndex := gt.ShuffledIndices[vi] + + neighbors, err := CalculateMatrixNeighbors(shuffledIndex, uint(len(gt.ShuffledIndices))) + if err != nil { + return nil, err + } + + gridSubset := NewEmptyGridNeighbors() + + for _, rN := range neighbors.RowNeighbors { + n := >.CanonicalShuffling[rN] + gridSubset.ValidatorIndicesRow[n.ValidatorIndex] = struct{}{} + for _, p := range n.Peers { + gridSubset.PeersRow[p] = struct{}{} + } + } + + for _, cN := range neighbors.ColumnNeighbors { + n := >.CanonicalShuffling[cN] + gridSubset.ValidatorIndicesCol[n.ValidatorIndex] = struct{}{} + for _, p := range n.Peers { + gridSubset.PeersCol[p] = struct{}{} + } + } + + return gridSubset, nil +} + +type GridNeighbors struct { + PeersRow map[peer.ID]struct{} + PeersCol map[peer.ID]struct{} + + ValidatorIndicesRow map[parachaintypes.ValidatorIndex]struct{} + ValidatorIndicesCol map[parachaintypes.ValidatorIndex]struct{} +} + +func NewEmptyGridNeighbors() *GridNeighbors { + return &GridNeighbors{ + PeersRow: make(map[peer.ID]struct{}), + PeersCol: make(map[peer.ID]struct{}), + ValidatorIndicesRow: make(map[parachaintypes.ValidatorIndex]struct{}), + ValidatorIndicesCol: make(map[parachaintypes.ValidatorIndex]struct{}), + } +} + +func (gn *GridNeighbors) RequiredRoutingByIndex(origin parachaintypes.ValidatorIndex, local bool) RequiredRouting { + if local { + return RequiredRoutingGridXY + } + _, x := gn.ValidatorIndicesRow[origin] + _, y := gn.ValidatorIndicesCol[origin] + + if x && y { + // If all works correctly origin peer can't be in both rows and columns. But we leave it anyways + return RequiredRoutingGridXY + } + if !(x || y) { + return RequiredRoutingNone + } + if x && !y { + return RequiredRoutingGridY + } + return RequiredRoutingGridX +} + +// RequiredRoutingByPeer Given the originator of a message as a peer index, indicates the part of the topology +// we're meant to send the message to. +// TODO: This method is actually not used in the codebase. +func (gn *GridNeighbors) RequiredRoutingByPeer(origin peer.ID, local bool) RequiredRouting { + if local { + return RequiredRoutingGridXY + } + _, x := gn.PeersRow[origin] + _, y := gn.PeersCol[origin] + + if x && y { + // If all works correctly origin peer can't be in both rows and columns. But we leave it anyways. + return RequiredRoutingGridXY + } + if !(x || y) { + return RequiredRoutingNone + } + if x && !y { + return RequiredRoutingGridY + } + return RequiredRoutingGridX +} + +// ShouldRouteToPeer indicates does peer should receive a message based on GridTopology and Routing strategy +func (gn *GridNeighbors) ShouldRouteToPeer(routing RequiredRouting, peer peer.ID) bool { + switch routing { + case RequiredRoutingAll: + return true + case RequiredRoutingNone, PendingTopology: + return false + case RequiredRoutingGridXY: + _, x := gn.PeersRow[peer] + _, y := gn.PeersCol[peer] + return x || y + case RequiredRoutingGridX: + _, x := gn.PeersRow[peer] + return x + case RequiredRoutingGridY: + _, y := gn.PeersCol[peer] + return y + default: + // No way we get here + return false + } +} + +// PeersDiff returns a differents between two GridNeighbors +func (gn *GridNeighbors) PeersDiff(other *GridNeighbors) []peer.ID { + diff := make([]peer.ID, 0) + for p := range gn.PeersRow { + _, inRows := other.PeersRow[p] + _, inCols := other.PeersCol[p] + if !inRows && !inCols { + diff = append(diff, p) + } + } + return diff +} + +func (gn *GridNeighbors) Len() int { + return len(gn.PeersRow) + len(gn.PeersCol) +} + +type RequiredRouting uint + +const ( + PendingTopology RequiredRouting = iota + RequiredRoutingAll // All peers in the grid + RequiredRoutingGridXY + RequiredRoutingGridX + RequiredRoutingGridY + RequiredRoutingNone +) + +// MatrixNeighbors holds the row and column neighbors of a given index in a matrix. +type MatrixNeighbors struct { + RowNeighbors []uint + ColumnNeighbors []uint +} + +// CalculateMatrixNeighbors computes the row and column neighbors of valIndex in a matrix of given length. +// e.g. for size 11 the matrix would be +// +// 0 1 2 +// 3 4 5 +// 6 7 8 +// 9 10 +// +// and for index 10, the neighbors would be 1, 4, 7, 9 +func CalculateMatrixNeighbors(valIndex, length uint) (*MatrixNeighbors, error) { + if valIndex >= length { + return nil, fmt.Errorf("grid topology malformed: valIndex %d is greater than length %d", + valIndex, + length, + ) + } + + sqrt := uint(math.Sqrt(float64(length))) + ourRow := valIndex / sqrt + ourColumn := valIndex % sqrt + + rowStart := ourRow * sqrt + rowEnd := uint(math.Min(float64(rowStart+sqrt), float64(length))) + + rowNeighbors := make([]uint, 0) + for i := rowStart; i < rowEnd; i++ { + if i != valIndex { + rowNeighbors = append(rowNeighbors, i) + } + } + + columnNeighbors := make([]uint, 0) + for i := ourColumn; i < length; i += sqrt { + if i != valIndex { + columnNeighbors = append(columnNeighbors, i) + } + } + + return &MatrixNeighbors{ + RowNeighbors: rowNeighbors, + ColumnNeighbors: columnNeighbors, + }, nil +} diff --git a/dot/parachain/grid/grid_topology_test.go b/dot/parachain/grid/grid_topology_test.go new file mode 100644 index 0000000000..dcad129c7e --- /dev/null +++ b/dot/parachain/grid/grid_topology_test.go @@ -0,0 +1,124 @@ +package grid_test + +import ( + "github.com/ChainSafe/gossamer/dot/parachain/grid" + parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" + "github.com/ChainSafe/gossamer/dot/types" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/stretchr/testify/assert" + "testing" +) + +// FixtureTopologyPeerInfo returns a slice of 11 TopologyPeerInfo for testing purposes. +func FixtureTopologyPeerInfo() []grid.TopologyPeerInfo { + return []grid.TopologyPeerInfo{ + { + Peers: []peer.ID{"peer1", "peer2"}, + ValidatorIndex: parachaintypes.ValidatorIndex(0), + DiscoveryID: types.AuthorityID{1}, + }, + { + Peers: []peer.ID{"peer3", "peer4"}, + ValidatorIndex: parachaintypes.ValidatorIndex(0), + DiscoveryID: types.AuthorityID{2}, + }, + { + Peers: []peer.ID{"peer5", "peer6"}, + ValidatorIndex: parachaintypes.ValidatorIndex(2), + DiscoveryID: types.AuthorityID{3}, + }, + { + Peers: []peer.ID{"peer7", "peer8"}, + ValidatorIndex: parachaintypes.ValidatorIndex(3), + DiscoveryID: types.AuthorityID{4}, + }, + { + Peers: []peer.ID{"peer9", "peer10"}, + ValidatorIndex: parachaintypes.ValidatorIndex(4), + DiscoveryID: types.AuthorityID{5}, + }, + { + Peers: []peer.ID{"peer11", "peer12"}, + ValidatorIndex: parachaintypes.ValidatorIndex(5), + DiscoveryID: types.AuthorityID{6}, + }, + { + Peers: []peer.ID{"peer13", "peer14"}, + ValidatorIndex: parachaintypes.ValidatorIndex(6), + DiscoveryID: types.AuthorityID{7}, + }, + { + Peers: []peer.ID{"peer15", "peer16"}, + ValidatorIndex: parachaintypes.ValidatorIndex(7), + DiscoveryID: types.AuthorityID{8}, + }, + { + Peers: []peer.ID{"peer17", "peer18"}, + ValidatorIndex: parachaintypes.ValidatorIndex(8), + DiscoveryID: types.AuthorityID{9}, + }, + { + Peers: []peer.ID{"peer19", "peer20"}, + ValidatorIndex: parachaintypes.ValidatorIndex(9), + DiscoveryID: types.AuthorityID{10}, + }, + { + Peers: []peer.ID{"peer21", "peer22"}, + ValidatorIndex: parachaintypes.ValidatorIndex(10), + DiscoveryID: types.AuthorityID{11}, + }, + } +} + +func Test_SessionGridTopology(t *testing.T) { + gt := grid.NewSessionGridTopology([]uint{1, 2, 3}, []grid.TopologyPeerInfo{grid.TopologyPeerInfo{ + Peers: []peer.ID{"peer1", "peer2"}, + ValidatorIndex: parachaintypes.ValidatorIndex(1), + DiscoveryID: types.AuthorityID{1}, + }, + }) + assert.Equal(t, 2, len(gt.Peers)) + + updated := gt.UpdateAuthoritiesIDs(peer.ID("peer2"), map[types.AuthorityID]struct{}{types.AuthorityID{1}: {}}) + assert.False(t, updated) + + updated = gt.UpdateAuthoritiesIDs(peer.ID("peer3"), map[types.AuthorityID]struct{}{types.AuthorityID{1}: {}}) + assert.True(t, updated) + assert.Equal(t, 3, len(gt.Peers)) + assert.Equal(t, + peer.IDSlice(peer.IDSlice{"peer1", "peer2", "peer3"}), + gt.CanonicalShuffling[0].Peers, + ) +} + +func Test_SessionGridTopologyNeighbors(t *testing.T) { + gt := grid.NewSessionGridTopology([]uint{1, 2, 3}, []grid.TopologyPeerInfo{grid.TopologyPeerInfo{ + Peers: []peer.ID{"peer1", "peer2"}, + ValidatorIndex: parachaintypes.ValidatorIndex(1), + DiscoveryID: types.AuthorityID{1}, + }, + }) + _, err := gt.ComputeGridNeighborsFor(1) + assert.NotNil(t, err) + + gt = grid.NewSessionGridTopology([]uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, FixtureTopologyPeerInfo()) + gn, err := gt.ComputeGridNeighborsFor(10) + assert.Nil(t, err) + assert.Equal(t, len(gn.ValidatorIndicesRow), 1) + assert.Equal(t, len(gn.ValidatorIndicesCol), 3) + assert.Equal(t, len(gn.PeersRow), 2) + assert.Equal(t, len(gn.PeersCol), 6) + +} + +func Test_MatrixNeighbors(t *testing.T) { + m, err := grid.CalculateMatrixNeighbors(10, 11) + assert.Nil(t, err) + assert.Equal(t, len(m.ColumnNeighbors), 3) + assert.Equal(t, len(m.RowNeighbors), 1) + + m, err = grid.CalculateMatrixNeighbors(4, 12) + assert.Nil(t, err) + assert.Equal(t, len(m.ColumnNeighbors), 3) + assert.Equal(t, len(m.RowNeighbors), 2) +} From dd7910cc7616a4161ce76c46e842b342d88c514e Mon Sep 17 00:00:00 2001 From: Kirill Pisarev Date: Fri, 21 Feb 2025 13:58:21 +0100 Subject: [PATCH 2/9] feat: add more unit tests and remove unnecessary function --- dot/parachain/grid/grid_topology.go | 24 +-------------------- dot/parachain/grid/grid_topology_test.go | 27 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/dot/parachain/grid/grid_topology.go b/dot/parachain/grid/grid_topology.go index 30050ee0a8..5f07fb47ec 100644 --- a/dot/parachain/grid/grid_topology.go +++ b/dot/parachain/grid/grid_topology.go @@ -129,6 +129,7 @@ func NewEmptyGridNeighbors() *GridNeighbors { } } +// RequiredRoutingByIndex Given the originator of a message as a validator index, indicates the part of the topology func (gn *GridNeighbors) RequiredRoutingByIndex(origin parachaintypes.ValidatorIndex, local bool) RequiredRouting { if local { return RequiredRoutingGridXY @@ -149,29 +150,6 @@ func (gn *GridNeighbors) RequiredRoutingByIndex(origin parachaintypes.ValidatorI return RequiredRoutingGridX } -// RequiredRoutingByPeer Given the originator of a message as a peer index, indicates the part of the topology -// we're meant to send the message to. -// TODO: This method is actually not used in the codebase. -func (gn *GridNeighbors) RequiredRoutingByPeer(origin peer.ID, local bool) RequiredRouting { - if local { - return RequiredRoutingGridXY - } - _, x := gn.PeersRow[origin] - _, y := gn.PeersCol[origin] - - if x && y { - // If all works correctly origin peer can't be in both rows and columns. But we leave it anyways. - return RequiredRoutingGridXY - } - if !(x || y) { - return RequiredRoutingNone - } - if x && !y { - return RequiredRoutingGridY - } - return RequiredRoutingGridX -} - // ShouldRouteToPeer indicates does peer should receive a message based on GridTopology and Routing strategy func (gn *GridNeighbors) ShouldRouteToPeer(routing RequiredRouting, peer peer.ID) bool { switch routing { diff --git a/dot/parachain/grid/grid_topology_test.go b/dot/parachain/grid/grid_topology_test.go index dcad129c7e..10006c6257 100644 --- a/dot/parachain/grid/grid_topology_test.go +++ b/dot/parachain/grid/grid_topology_test.go @@ -122,3 +122,30 @@ func Test_MatrixNeighbors(t *testing.T) { assert.Equal(t, len(m.ColumnNeighbors), 3) assert.Equal(t, len(m.RowNeighbors), 2) } + +func Test_GridNeighbors(t *testing.T) { + // e.g. for size 11 the matrix would be + // + // 0 1 2 + // 3 4 5 + // 6 7 8 + // 9 10 + // + // and for index 10, the neighbors would be 1, 4, 7, 9 + gt := grid.NewSessionGridTopology([]uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, FixtureTopologyPeerInfo()) + gn, err := gt.ComputeGridNeighborsFor(10) + assert.Nil(t, err) + assert.Equal(t, len(gn.ValidatorIndicesRow), 1) + assert.Equal(t, len(gn.ValidatorIndicesCol), 3) + assert.Equal(t, len(gn.PeersRow), 2) + assert.Equal(t, len(gn.PeersCol), 6) + assert.Nil(t, err) + routing := gn.RequiredRoutingByIndex(4, false) + // The origin of the message was from column so we should rout in a row + assert.Equal(t, grid.RequiredRoutingGridX, routing) + + // Since routing is for rows we have only validator with index 9 there and tis peers are peer19 and peer20 + assert.False(t, gn.ShouldRouteToPeer(grid.RequiredRoutingGridX, peer.ID("peer1"))) + assert.True(t, gn.ShouldRouteToPeer(grid.RequiredRoutingGridX, peer.ID("peer19"))) + +} From 9d1ba30978b418b3b5ba0acab7a34232f6e08bdc Mon Sep 17 00:00:00 2001 From: Kirill Pisarev Date: Fri, 21 Feb 2025 18:35:59 +0100 Subject: [PATCH 3/9] feat: implement sessiongridtopologystorage --- dot/parachain/grid/grid_topology.go | 94 ++++++++++++++++++++++++ dot/parachain/grid/grid_topology_test.go | 56 +++++++++++++- 2 files changed, 148 insertions(+), 2 deletions(-) diff --git a/dot/parachain/grid/grid_topology.go b/dot/parachain/grid/grid_topology.go index 5f07fb47ec..bc4e8a2a93 100644 --- a/dot/parachain/grid/grid_topology.go +++ b/dot/parachain/grid/grid_topology.go @@ -250,3 +250,97 @@ func CalculateMatrixNeighbors(valIndex, length uint) (*MatrixNeighbors, error) { ColumnNeighbors: columnNeighbors, }, nil } + +type SessionGridTopologyEntry struct { + Topology *SessionGridTopology + LocalNeighbors *GridNeighbors + LocalIndex parachaintypes.ValidatorIndex + SessionIndex parachaintypes.SessionIndex +} + +func (s *SessionGridTopologyEntry) PeersToRoute(routing RequiredRouting) []peer.ID { + switch routing { + case RequiredRoutingAll: + peers := make([]peer.ID, 0) + for p, _ := range s.Topology.Peers { + peers = append(peers, p) + } + return peers + case RequiredRoutingGridY: + peers := make([]peer.ID, 0) + for p, _ := range s.LocalNeighbors.PeersCol { + peers = append(peers, p) + } + return peers + case RequiredRoutingGridX: + peers := make([]peer.ID, 0) + for p, _ := range s.LocalNeighbors.PeersRow { + peers = append(peers, p) + } + return peers + case RequiredRoutingGridXY: + peers := make([]peer.ID, 0) + for p, _ := range s.LocalNeighbors.PeersCol { + peers = append(peers, p) + } + for p, _ := range s.LocalNeighbors.PeersRow { + peers = append(peers, p) + } + return peers + } + return make([]peer.ID, 0) +} + +func (s *SessionGridTopologyEntry) UpdateAuthoritiesIDs(peer peer.ID, discoveryIDs map[types.AuthorityID]struct{}) (bool, error) { + if s.Topology.UpdateAuthoritiesIDs(peer, discoveryIDs) { + // If authorities update, recompile neighbors + new_neighbors, err := s.Topology.ComputeGridNeighborsFor(s.LocalIndex) + if err != nil { + return false, err + } + s.LocalNeighbors = new_neighbors + return true, nil + } + return false, nil +} + +type SessionGridTopologyStorage struct { + CurrentTopology *SessionGridTopologyEntry + PrevTopology *SessionGridTopologyEntry +} + +func (s *SessionGridTopologyStorage) GetTopologyBySessionIndex(idx parachaintypes.SessionIndex) *SessionGridTopologyEntry { + if s.CurrentTopology.SessionIndex == idx { + return s.CurrentTopology + } + if s.PrevTopology.SessionIndex == idx { + return s.PrevTopology + } + return nil +} + +func (s *SessionGridTopologyStorage) GetTopologyOrFallback(idx parachaintypes.SessionIndex) *SessionGridTopologyEntry { + toplogy := s.GetTopologyBySessionIndex(idx) + if toplogy != nil { + return toplogy + } else { + return s.CurrentTopology + } +} + +func (s *SessionGridTopologyStorage) UpdateCurrentTopology(idx parachaintypes.SessionIndex, + topology *SessionGridTopology, + localIndex parachaintypes.ValidatorIndex) error { + localNeighbors, err := topology.ComputeGridNeighborsFor(localIndex) + if err != nil { + return err + } + s.PrevTopology = s.CurrentTopology + s.CurrentTopology = &SessionGridTopologyEntry{ + Topology: topology, + LocalNeighbors: localNeighbors, + LocalIndex: localIndex, + SessionIndex: idx, + } + return nil +} diff --git a/dot/parachain/grid/grid_topology_test.go b/dot/parachain/grid/grid_topology_test.go index 10006c6257..97491b727b 100644 --- a/dot/parachain/grid/grid_topology_test.go +++ b/dot/parachain/grid/grid_topology_test.go @@ -19,7 +19,7 @@ func FixtureTopologyPeerInfo() []grid.TopologyPeerInfo { }, { Peers: []peer.ID{"peer3", "peer4"}, - ValidatorIndex: parachaintypes.ValidatorIndex(0), + ValidatorIndex: parachaintypes.ValidatorIndex(1), DiscoveryID: types.AuthorityID{2}, }, { @@ -144,8 +144,60 @@ func Test_GridNeighbors(t *testing.T) { // The origin of the message was from column so we should rout in a row assert.Equal(t, grid.RequiredRoutingGridX, routing) - // Since routing is for rows we have only validator with index 9 there and tis peers are peer19 and peer20 + // Since routing is for rows we have only validator with index 9 there and its peers are peer19 and peer20 assert.False(t, gn.ShouldRouteToPeer(grid.RequiredRoutingGridX, peer.ID("peer1"))) assert.True(t, gn.ShouldRouteToPeer(grid.RequiredRoutingGridX, peer.ID("peer19"))) + assert.True(t, gn.ShouldRouteToPeer(grid.RequiredRoutingGridX, peer.ID("peer20"))) +} + +func Test_SessionGridTopologyEntry(t *testing.T) { + gt := grid.NewSessionGridTopology([]uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, FixtureTopologyPeerInfo()) + gn, err := gt.ComputeGridNeighborsFor(10) + assert.Nil(t, err) + sgte := &grid.SessionGridTopologyEntry{ + Topology: gt, + LocalNeighbors: gn, + LocalIndex: 10, + SessionIndex: 99, + } + // Since routing is for rows we have only validator with index 9 there and its peers are peer19 and peer20 + assert.Equal(t, len(sgte.PeersToRoute(grid.RequiredRoutingGridX)), 2) + // Since routing is for rows we have only validator with index 1,4,7 there and its peers are peer19 and peer20 + //[]peer.ID{"peer3", "peer4", "peer9", "peer10", "peer15", "peer16"}, + assert.Equal(t, len(sgte.PeersToRoute(grid.RequiredRoutingGridY)), 6) + assert.Equal(t, len(sgte.PeersToRoute(grid.RequiredRoutingAll)), 22) + updated, err := sgte.UpdateAuthoritiesIDs(peer.ID("peer99"), map[types.AuthorityID]struct{}{types.AuthorityID{10}: {}}) + assert.True(t, updated) + assert.Nil(t, err) + // Now we added one more peer to validator with AuthorityID 10, index 9. Sine we are actins as validator with index 10 + // we should route to this peer if strategy RequiredRoutingGridX + assert.Equal(t, len(sgte.PeersToRoute(grid.RequiredRoutingGridX)), 3) +} + +func Test_SessionGridTopologyStorage(t *testing.T) { + gt := grid.NewSessionGridTopology([]uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, FixtureTopologyPeerInfo()) + gn, err := gt.ComputeGridNeighborsFor(10) + assert.Nil(t, err) + sgteCurrent := &grid.SessionGridTopologyEntry{ + Topology: gt, + LocalNeighbors: gn, + LocalIndex: 10, + SessionIndex: 99, + } + sgtePrev := &grid.SessionGridTopologyEntry{ + Topology: gt, + LocalNeighbors: gn, + LocalIndex: 10, + SessionIndex: 98, + } + storage := grid.SessionGridTopologyStorage{ + CurrentTopology: sgteCurrent, + PrevTopology: sgtePrev, + } + + err = storage.UpdateCurrentTopology(100, gt, 10) + assert.Nil(t, err) + assert.Equal(t, storage.PrevTopology.SessionIndex, parachaintypes.SessionIndex(99)) + assert.Equal(t, storage.CurrentTopology.SessionIndex, parachaintypes.SessionIndex(100)) } From 1dacec34d963db69250189f02b8089b200a45c46 Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 25 Feb 2025 15:39:30 +0100 Subject: [PATCH 4/9] Update dot/parachain/grid/grid_topology.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eclésio Junior --- dot/parachain/grid/grid_topology.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dot/parachain/grid/grid_topology.go b/dot/parachain/grid/grid_topology.go index bc4e8a2a93..1e411c3d75 100644 --- a/dot/parachain/grid/grid_topology.go +++ b/dot/parachain/grid/grid_topology.go @@ -216,7 +216,7 @@ type MatrixNeighbors struct { // 9 10 // // and for index 10, the neighbors would be 1, 4, 7, 9 -func CalculateMatrixNeighbors(valIndex, length uint) (*MatrixNeighbors, error) { +func calculateMatrixNeighbors(valIndex, length uint) (*MatrixNeighbors, error) { if valIndex >= length { return nil, fmt.Errorf("grid topology malformed: valIndex %d is greater than length %d", valIndex, From 7172780d13390e83d8c7fdf758c227c69fe9f864 Mon Sep 17 00:00:00 2001 From: Kirill Pisarev Date: Tue, 25 Feb 2025 15:52:31 +0100 Subject: [PATCH 5/9] chore: fix lint --- dot/parachain/grid/grid_topology.go | 102 +++++++++++---------- dot/parachain/grid/grid_topology_test.go | 109 ++++++++++++----------- 2 files changed, 109 insertions(+), 102 deletions(-) diff --git a/dot/parachain/grid/grid_topology.go b/dot/parachain/grid/grid_topology.go index 1e411c3d75..0020c10836 100644 --- a/dot/parachain/grid/grid_topology.go +++ b/dot/parachain/grid/grid_topology.go @@ -2,10 +2,12 @@ package grid import ( "fmt" + "math" + parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" "github.com/ChainSafe/gossamer/dot/types" + "github.com/libp2p/go-libp2p/core/peer" - "math" ) // TopologyPeerInfo is an information about the peer in the gossip topology @@ -75,7 +77,7 @@ func (gt *SessionGridTopology) IsValidator(peer peer.ID) bool { return ok } -func (gt *SessionGridTopology) ComputeGridNeighborsFor(vi parachaintypes.ValidatorIndex) (*GridNeighbors, error) { +func (gt *SessionGridTopology) ComputeGridNeighboursFor(vi parachaintypes.ValidatorIndex) (*GridNeighbours, error) { if len(gt.ShuffledIndices) != len(gt.CanonicalShuffling) { return nil, fmt.Errorf("grid topology malformed: "+ "shuffledIndices length %d is not equal to canonicalShuffling length %d", @@ -86,14 +88,14 @@ func (gt *SessionGridTopology) ComputeGridNeighborsFor(vi parachaintypes.Validat shuffledIndex := gt.ShuffledIndices[vi] - neighbors, err := CalculateMatrixNeighbors(shuffledIndex, uint(len(gt.ShuffledIndices))) + neighbours, err := calculateMatrixNeighbours(shuffledIndex, uint(len(gt.ShuffledIndices))) if err != nil { return nil, err } - gridSubset := NewEmptyGridNeighbors() + gridSubset := NewEmptyGridNeighbours() - for _, rN := range neighbors.RowNeighbors { + for _, rN := range neighbours.RowNeighbours { n := >.CanonicalShuffling[rN] gridSubset.ValidatorIndicesRow[n.ValidatorIndex] = struct{}{} for _, p := range n.Peers { @@ -101,7 +103,7 @@ func (gt *SessionGridTopology) ComputeGridNeighborsFor(vi parachaintypes.Validat } } - for _, cN := range neighbors.ColumnNeighbors { + for _, cN := range neighbours.ColumnNeighbours { n := >.CanonicalShuffling[cN] gridSubset.ValidatorIndicesCol[n.ValidatorIndex] = struct{}{} for _, p := range n.Peers { @@ -112,7 +114,7 @@ func (gt *SessionGridTopology) ComputeGridNeighborsFor(vi parachaintypes.Validat return gridSubset, nil } -type GridNeighbors struct { +type GridNeighbours struct { PeersRow map[peer.ID]struct{} PeersCol map[peer.ID]struct{} @@ -120,8 +122,8 @@ type GridNeighbors struct { ValidatorIndicesCol map[parachaintypes.ValidatorIndex]struct{} } -func NewEmptyGridNeighbors() *GridNeighbors { - return &GridNeighbors{ +func NewEmptyGridNeighbours() *GridNeighbours { + return &GridNeighbours{ PeersRow: make(map[peer.ID]struct{}), PeersCol: make(map[peer.ID]struct{}), ValidatorIndicesRow: make(map[parachaintypes.ValidatorIndex]struct{}), @@ -130,7 +132,7 @@ func NewEmptyGridNeighbors() *GridNeighbors { } // RequiredRoutingByIndex Given the originator of a message as a validator index, indicates the part of the topology -func (gn *GridNeighbors) RequiredRoutingByIndex(origin parachaintypes.ValidatorIndex, local bool) RequiredRouting { +func (gn *GridNeighbours) RequiredRoutingByIndex(origin parachaintypes.ValidatorIndex, local bool) RequiredRouting { if local { return RequiredRoutingGridXY } @@ -151,7 +153,7 @@ func (gn *GridNeighbors) RequiredRoutingByIndex(origin parachaintypes.ValidatorI } // ShouldRouteToPeer indicates does peer should receive a message based on GridTopology and Routing strategy -func (gn *GridNeighbors) ShouldRouteToPeer(routing RequiredRouting, peer peer.ID) bool { +func (gn *GridNeighbours) ShouldRouteToPeer(routing RequiredRouting, peer peer.ID) bool { switch routing { case RequiredRoutingAll: return true @@ -173,8 +175,8 @@ func (gn *GridNeighbors) ShouldRouteToPeer(routing RequiredRouting, peer peer.ID } } -// PeersDiff returns a differents between two GridNeighbors -func (gn *GridNeighbors) PeersDiff(other *GridNeighbors) []peer.ID { +// PeersDiff returns a differents between two GridNeighbours +func (gn *GridNeighbours) PeersDiff(other *GridNeighbours) []peer.ID { diff := make([]peer.ID, 0) for p := range gn.PeersRow { _, inRows := other.PeersRow[p] @@ -186,7 +188,7 @@ func (gn *GridNeighbors) PeersDiff(other *GridNeighbors) []peer.ID { return diff } -func (gn *GridNeighbors) Len() int { +func (gn *GridNeighbours) Len() int { return len(gn.PeersRow) + len(gn.PeersCol) } @@ -201,13 +203,13 @@ const ( RequiredRoutingNone ) -// MatrixNeighbors holds the row and column neighbors of a given index in a matrix. -type MatrixNeighbors struct { - RowNeighbors []uint - ColumnNeighbors []uint +// MatrixNeighbours holds the row and column neighbours of a given index in a matrix. +type MatrixNeighbours struct { + RowNeighbours []uint + ColumnNeighbours []uint } -// CalculateMatrixNeighbors computes the row and column neighbors of valIndex in a matrix of given length. +// calculateMatrixNeighbours computes the row and column neighbours of valIndex in a matrix of given length. // e.g. for size 11 the matrix would be // // 0 1 2 @@ -215,8 +217,8 @@ type MatrixNeighbors struct { // 6 7 8 // 9 10 // -// and for index 10, the neighbors would be 1, 4, 7, 9 -func calculateMatrixNeighbors(valIndex, length uint) (*MatrixNeighbors, error) { +// and for index 10, the neighbours would be 1, 4, 7, 9 +func calculateMatrixNeighbours(valIndex, length uint) (*MatrixNeighbours, error) { if valIndex >= length { return nil, fmt.Errorf("grid topology malformed: valIndex %d is greater than length %d", valIndex, @@ -231,59 +233,59 @@ func calculateMatrixNeighbors(valIndex, length uint) (*MatrixNeighbors, error) { rowStart := ourRow * sqrt rowEnd := uint(math.Min(float64(rowStart+sqrt), float64(length))) - rowNeighbors := make([]uint, 0) + rowNeighbours := make([]uint, 0) for i := rowStart; i < rowEnd; i++ { if i != valIndex { - rowNeighbors = append(rowNeighbors, i) + rowNeighbours = append(rowNeighbours, i) } } - columnNeighbors := make([]uint, 0) + columnNeighbours := make([]uint, 0) for i := ourColumn; i < length; i += sqrt { if i != valIndex { - columnNeighbors = append(columnNeighbors, i) + columnNeighbours = append(columnNeighbours, i) } } - return &MatrixNeighbors{ - RowNeighbors: rowNeighbors, - ColumnNeighbors: columnNeighbors, + return &MatrixNeighbours{ + RowNeighbours: rowNeighbours, + ColumnNeighbours: columnNeighbours, }, nil } type SessionGridTopologyEntry struct { - Topology *SessionGridTopology - LocalNeighbors *GridNeighbors - LocalIndex parachaintypes.ValidatorIndex - SessionIndex parachaintypes.SessionIndex + Topology *SessionGridTopology + LocalNeighbours *GridNeighbours + LocalIndex parachaintypes.ValidatorIndex + SessionIndex parachaintypes.SessionIndex } func (s *SessionGridTopologyEntry) PeersToRoute(routing RequiredRouting) []peer.ID { switch routing { case RequiredRoutingAll: peers := make([]peer.ID, 0) - for p, _ := range s.Topology.Peers { + for p := range s.Topology.Peers { peers = append(peers, p) } return peers case RequiredRoutingGridY: peers := make([]peer.ID, 0) - for p, _ := range s.LocalNeighbors.PeersCol { + for p := range s.LocalNeighbours.PeersCol { peers = append(peers, p) } return peers case RequiredRoutingGridX: peers := make([]peer.ID, 0) - for p, _ := range s.LocalNeighbors.PeersRow { + for p := range s.LocalNeighbours.PeersRow { peers = append(peers, p) } return peers case RequiredRoutingGridXY: peers := make([]peer.ID, 0) - for p, _ := range s.LocalNeighbors.PeersCol { + for p := range s.LocalNeighbours.PeersCol { peers = append(peers, p) } - for p, _ := range s.LocalNeighbors.PeersRow { + for p := range s.LocalNeighbours.PeersRow { peers = append(peers, p) } return peers @@ -291,14 +293,16 @@ func (s *SessionGridTopologyEntry) PeersToRoute(routing RequiredRouting) []peer. return make([]peer.ID, 0) } -func (s *SessionGridTopologyEntry) UpdateAuthoritiesIDs(peer peer.ID, discoveryIDs map[types.AuthorityID]struct{}) (bool, error) { +func (s *SessionGridTopologyEntry) UpdateAuthoritiesIDs( + peer peer.ID, + discoveryIDs map[types.AuthorityID]struct{}) (bool, error) { if s.Topology.UpdateAuthoritiesIDs(peer, discoveryIDs) { - // If authorities update, recompile neighbors - new_neighbors, err := s.Topology.ComputeGridNeighborsFor(s.LocalIndex) + // If authorities update, recompile neighbours + new_neighbours, err := s.Topology.ComputeGridNeighboursFor(s.LocalIndex) if err != nil { return false, err } - s.LocalNeighbors = new_neighbors + s.LocalNeighbours = new_neighbours return true, nil } return false, nil @@ -309,7 +313,8 @@ type SessionGridTopologyStorage struct { PrevTopology *SessionGridTopologyEntry } -func (s *SessionGridTopologyStorage) GetTopologyBySessionIndex(idx parachaintypes.SessionIndex) *SessionGridTopologyEntry { +func (s *SessionGridTopologyStorage) GetTopologyBySessionIndex( + idx parachaintypes.SessionIndex) *SessionGridTopologyEntry { if s.CurrentTopology.SessionIndex == idx { return s.CurrentTopology } @@ -319,7 +324,8 @@ func (s *SessionGridTopologyStorage) GetTopologyBySessionIndex(idx parachaintype return nil } -func (s *SessionGridTopologyStorage) GetTopologyOrFallback(idx parachaintypes.SessionIndex) *SessionGridTopologyEntry { +func (s *SessionGridTopologyStorage) GetTopologyOrFallback( + idx parachaintypes.SessionIndex) *SessionGridTopologyEntry { toplogy := s.GetTopologyBySessionIndex(idx) if toplogy != nil { return toplogy @@ -331,16 +337,16 @@ func (s *SessionGridTopologyStorage) GetTopologyOrFallback(idx parachaintypes.Se func (s *SessionGridTopologyStorage) UpdateCurrentTopology(idx parachaintypes.SessionIndex, topology *SessionGridTopology, localIndex parachaintypes.ValidatorIndex) error { - localNeighbors, err := topology.ComputeGridNeighborsFor(localIndex) + localNeighbours, err := topology.ComputeGridNeighboursFor(localIndex) if err != nil { return err } s.PrevTopology = s.CurrentTopology s.CurrentTopology = &SessionGridTopologyEntry{ - Topology: topology, - LocalNeighbors: localNeighbors, - LocalIndex: localIndex, - SessionIndex: idx, + Topology: topology, + LocalNeighbours: localNeighbours, + LocalIndex: localIndex, + SessionIndex: idx, } return nil } diff --git a/dot/parachain/grid/grid_topology_test.go b/dot/parachain/grid/grid_topology_test.go index 97491b727b..78faea0caf 100644 --- a/dot/parachain/grid/grid_topology_test.go +++ b/dot/parachain/grid/grid_topology_test.go @@ -1,17 +1,18 @@ -package grid_test +package grid import ( - "github.com/ChainSafe/gossamer/dot/parachain/grid" + "testing" + parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" "github.com/ChainSafe/gossamer/dot/types" + "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/assert" - "testing" ) // FixtureTopologyPeerInfo returns a slice of 11 TopologyPeerInfo for testing purposes. -func FixtureTopologyPeerInfo() []grid.TopologyPeerInfo { - return []grid.TopologyPeerInfo{ +func FixtureTopologyPeerInfo() []TopologyPeerInfo { + return []TopologyPeerInfo{ { Peers: []peer.ID{"peer1", "peer2"}, ValidatorIndex: parachaintypes.ValidatorIndex(0), @@ -71,7 +72,7 @@ func FixtureTopologyPeerInfo() []grid.TopologyPeerInfo { } func Test_SessionGridTopology(t *testing.T) { - gt := grid.NewSessionGridTopology([]uint{1, 2, 3}, []grid.TopologyPeerInfo{grid.TopologyPeerInfo{ + gt := NewSessionGridTopology([]uint{1, 2, 3}, []TopologyPeerInfo{{ Peers: []peer.ID{"peer1", "peer2"}, ValidatorIndex: parachaintypes.ValidatorIndex(1), DiscoveryID: types.AuthorityID{1}, @@ -79,30 +80,30 @@ func Test_SessionGridTopology(t *testing.T) { }) assert.Equal(t, 2, len(gt.Peers)) - updated := gt.UpdateAuthoritiesIDs(peer.ID("peer2"), map[types.AuthorityID]struct{}{types.AuthorityID{1}: {}}) + updated := gt.UpdateAuthoritiesIDs(peer.ID("peer2"), map[types.AuthorityID]struct{}{{1}: {}}) assert.False(t, updated) - updated = gt.UpdateAuthoritiesIDs(peer.ID("peer3"), map[types.AuthorityID]struct{}{types.AuthorityID{1}: {}}) + updated = gt.UpdateAuthoritiesIDs(peer.ID("peer3"), map[types.AuthorityID]struct{}{{1}: {}}) assert.True(t, updated) assert.Equal(t, 3, len(gt.Peers)) assert.Equal(t, - peer.IDSlice(peer.IDSlice{"peer1", "peer2", "peer3"}), + peer.IDSlice{"peer1", "peer2", "peer3"}, gt.CanonicalShuffling[0].Peers, ) } -func Test_SessionGridTopologyNeighbors(t *testing.T) { - gt := grid.NewSessionGridTopology([]uint{1, 2, 3}, []grid.TopologyPeerInfo{grid.TopologyPeerInfo{ +func Test_SessionGridTopologyNeighbours(t *testing.T) { + gt := NewSessionGridTopology([]uint{1, 2, 3}, []TopologyPeerInfo{{ Peers: []peer.ID{"peer1", "peer2"}, ValidatorIndex: parachaintypes.ValidatorIndex(1), DiscoveryID: types.AuthorityID{1}, }, }) - _, err := gt.ComputeGridNeighborsFor(1) + _, err := gt.ComputeGridNeighboursFor(1) assert.NotNil(t, err) - gt = grid.NewSessionGridTopology([]uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, FixtureTopologyPeerInfo()) - gn, err := gt.ComputeGridNeighborsFor(10) + gt = NewSessionGridTopology([]uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, FixtureTopologyPeerInfo()) + gn, err := gt.ComputeGridNeighboursFor(10) assert.Nil(t, err) assert.Equal(t, len(gn.ValidatorIndicesRow), 1) assert.Equal(t, len(gn.ValidatorIndicesCol), 3) @@ -111,19 +112,19 @@ func Test_SessionGridTopologyNeighbors(t *testing.T) { } -func Test_MatrixNeighbors(t *testing.T) { - m, err := grid.CalculateMatrixNeighbors(10, 11) +func Test_MatrixNeighbours(t *testing.T) { + m, err := calculateMatrixNeighbours(10, 11) assert.Nil(t, err) - assert.Equal(t, len(m.ColumnNeighbors), 3) - assert.Equal(t, len(m.RowNeighbors), 1) + assert.Equal(t, len(m.ColumnNeighbours), 3) + assert.Equal(t, len(m.RowNeighbours), 1) - m, err = grid.CalculateMatrixNeighbors(4, 12) + m, err = calculateMatrixNeighbours(4, 12) assert.Nil(t, err) - assert.Equal(t, len(m.ColumnNeighbors), 3) - assert.Equal(t, len(m.RowNeighbors), 2) + assert.Equal(t, len(m.ColumnNeighbours), 3) + assert.Equal(t, len(m.RowNeighbours), 2) } -func Test_GridNeighbors(t *testing.T) { +func Test_GridNeighbours(t *testing.T) { // e.g. for size 11 the matrix would be // // 0 1 2 @@ -131,9 +132,9 @@ func Test_GridNeighbors(t *testing.T) { // 6 7 8 // 9 10 // - // and for index 10, the neighbors would be 1, 4, 7, 9 - gt := grid.NewSessionGridTopology([]uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, FixtureTopologyPeerInfo()) - gn, err := gt.ComputeGridNeighborsFor(10) + // and for index 10, the neighbours would be 1, 4, 7, 9 + gt := NewSessionGridTopology([]uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, FixtureTopologyPeerInfo()) + gn, err := gt.ComputeGridNeighboursFor(10) assert.Nil(t, err) assert.Equal(t, len(gn.ValidatorIndicesRow), 1) assert.Equal(t, len(gn.ValidatorIndicesCol), 3) @@ -142,56 +143,56 @@ func Test_GridNeighbors(t *testing.T) { assert.Nil(t, err) routing := gn.RequiredRoutingByIndex(4, false) // The origin of the message was from column so we should rout in a row - assert.Equal(t, grid.RequiredRoutingGridX, routing) + assert.Equal(t, RequiredRoutingGridX, routing) // Since routing is for rows we have only validator with index 9 there and its peers are peer19 and peer20 - assert.False(t, gn.ShouldRouteToPeer(grid.RequiredRoutingGridX, peer.ID("peer1"))) - assert.True(t, gn.ShouldRouteToPeer(grid.RequiredRoutingGridX, peer.ID("peer19"))) - assert.True(t, gn.ShouldRouteToPeer(grid.RequiredRoutingGridX, peer.ID("peer20"))) + assert.False(t, gn.ShouldRouteToPeer(RequiredRoutingGridX, peer.ID("peer1"))) + assert.True(t, gn.ShouldRouteToPeer(RequiredRoutingGridX, peer.ID("peer19"))) + assert.True(t, gn.ShouldRouteToPeer(RequiredRoutingGridX, peer.ID("peer20"))) } func Test_SessionGridTopologyEntry(t *testing.T) { - gt := grid.NewSessionGridTopology([]uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, FixtureTopologyPeerInfo()) - gn, err := gt.ComputeGridNeighborsFor(10) + gt := NewSessionGridTopology([]uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, FixtureTopologyPeerInfo()) + gn, err := gt.ComputeGridNeighboursFor(10) assert.Nil(t, err) - sgte := &grid.SessionGridTopologyEntry{ - Topology: gt, - LocalNeighbors: gn, - LocalIndex: 10, - SessionIndex: 99, + sgte := &SessionGridTopologyEntry{ + Topology: gt, + LocalNeighbours: gn, + LocalIndex: 10, + SessionIndex: 99, } // Since routing is for rows we have only validator with index 9 there and its peers are peer19 and peer20 - assert.Equal(t, len(sgte.PeersToRoute(grid.RequiredRoutingGridX)), 2) + assert.Equal(t, len(sgte.PeersToRoute(RequiredRoutingGridX)), 2) // Since routing is for rows we have only validator with index 1,4,7 there and its peers are peer19 and peer20 //[]peer.ID{"peer3", "peer4", "peer9", "peer10", "peer15", "peer16"}, - assert.Equal(t, len(sgte.PeersToRoute(grid.RequiredRoutingGridY)), 6) - assert.Equal(t, len(sgte.PeersToRoute(grid.RequiredRoutingAll)), 22) + assert.Equal(t, len(sgte.PeersToRoute(RequiredRoutingGridY)), 6) + assert.Equal(t, len(sgte.PeersToRoute(RequiredRoutingAll)), 22) - updated, err := sgte.UpdateAuthoritiesIDs(peer.ID("peer99"), map[types.AuthorityID]struct{}{types.AuthorityID{10}: {}}) + updated, err := sgte.UpdateAuthoritiesIDs(peer.ID("peer99"), map[types.AuthorityID]struct{}{{10}: {}}) assert.True(t, updated) assert.Nil(t, err) // Now we added one more peer to validator with AuthorityID 10, index 9. Sine we are actins as validator with index 10 // we should route to this peer if strategy RequiredRoutingGridX - assert.Equal(t, len(sgte.PeersToRoute(grid.RequiredRoutingGridX)), 3) + assert.Equal(t, len(sgte.PeersToRoute(RequiredRoutingGridX)), 3) } func Test_SessionGridTopologyStorage(t *testing.T) { - gt := grid.NewSessionGridTopology([]uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, FixtureTopologyPeerInfo()) - gn, err := gt.ComputeGridNeighborsFor(10) + gt := NewSessionGridTopology([]uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, FixtureTopologyPeerInfo()) + gn, err := gt.ComputeGridNeighboursFor(10) assert.Nil(t, err) - sgteCurrent := &grid.SessionGridTopologyEntry{ - Topology: gt, - LocalNeighbors: gn, - LocalIndex: 10, - SessionIndex: 99, + sgteCurrent := &SessionGridTopologyEntry{ + Topology: gt, + LocalNeighbours: gn, + LocalIndex: 10, + SessionIndex: 99, } - sgtePrev := &grid.SessionGridTopologyEntry{ - Topology: gt, - LocalNeighbors: gn, - LocalIndex: 10, - SessionIndex: 98, + sgtePrev := &SessionGridTopologyEntry{ + Topology: gt, + LocalNeighbours: gn, + LocalIndex: 10, + SessionIndex: 98, } - storage := grid.SessionGridTopologyStorage{ + storage := SessionGridTopologyStorage{ CurrentTopology: sgteCurrent, PrevTopology: sgtePrev, } From 594370942f8b1f746f434b854fa27c0d2f57ab7b Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 3 Mar 2025 11:35:58 +0100 Subject: [PATCH 6/9] Update dot/parachain/grid/grid_topology.go Co-authored-by: Axay Sagathiya --- dot/parachain/grid/grid_topology.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dot/parachain/grid/grid_topology.go b/dot/parachain/grid/grid_topology.go index 0020c10836..22aa8f3c2d 100644 --- a/dot/parachain/grid/grid_topology.go +++ b/dot/parachain/grid/grid_topology.go @@ -26,7 +26,7 @@ type TopologyPeerInfo struct { type SessionGridTopology struct { // Peers List of all peers ids in session. Represented as "hashset" for fast lookup. Peers map[peer.ID]struct{} - /// canonicalShuffling is the canonical shuffling of validators for the session. + // canonicalShuffling is the canonical shuffling of validators for the session. CanonicalShuffling []TopologyPeerInfo /// shuffledIndices is an array mapping validator indices to their indices in the /// shuffling itself. This has the same size as the number of validators From 59c52e1082fb42e2ca1811accc9e4de9af22ce9b Mon Sep 17 00:00:00 2001 From: Kirill Pisarev Date: Mon, 3 Mar 2025 11:47:17 +0100 Subject: [PATCH 7/9] chore: small fixes according to review --- dot/parachain/grid/grid_topology.go | 16 +++++----- dot/parachain/grid/grid_topology_test.go | 37 ++++++++++++------------ 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/dot/parachain/grid/grid_topology.go b/dot/parachain/grid/grid_topology.go index 22aa8f3c2d..c0cf61d605 100644 --- a/dot/parachain/grid/grid_topology.go +++ b/dot/parachain/grid/grid_topology.go @@ -12,13 +12,12 @@ import ( // TopologyPeerInfo is an information about the peer in the gossip topology type TopologyPeerInfo struct { - // Peers is a list pf peers this peer knows about + // Peers is a list of peers this peer knows about Peers peer.IDSlice - /// ValidatorIndex is the index of the validator in the discovery keys of the corresponding - /// `SessionInfo`. This can extend _beyond_ the set of active parachain validators. + // ValidatorIndex is the index of the validator in the discovery keys of the corresponding `SessionInfo`. + // This can extend _beyond_ the set of active parachain validators. ValidatorIndex parachaintypes.ValidatorIndex - /// DiscoveryID is the authority discovery public key of the validator in the corresponding - /// `SessionInfo`. + // DiscoveryID is the authority discovery public key of the validator in the corresponding `SessionInfo`. DiscoveryID types.AuthorityID } @@ -28,9 +27,8 @@ type SessionGridTopology struct { Peers map[peer.ID]struct{} // canonicalShuffling is the canonical shuffling of validators for the session. CanonicalShuffling []TopologyPeerInfo - /// shuffledIndices is an array mapping validator indices to their indices in the - /// shuffling itself. This has the same size as the number of validators - /// in the session. + // shuffledIndices is an array mapping validator indices to their indices in the shuffling itself. + // This has the same size as the number of validators in the session. ShuffledIndices []uint } @@ -253,6 +251,8 @@ func calculateMatrixNeighbours(valIndex, length uint) (*MatrixNeighbours, error) }, nil } +// SessionGridTopologyEntry is a single entry in the SessionGridTopologyStorage. Contain SessionTopology itself, +// calculated neighbours for this topology according to the validator index. type SessionGridTopologyEntry struct { Topology *SessionGridTopology LocalNeighbours *GridNeighbours diff --git a/dot/parachain/grid/grid_topology_test.go b/dot/parachain/grid/grid_topology_test.go index 78faea0caf..e9ac958b7c 100644 --- a/dot/parachain/grid/grid_topology_test.go +++ b/dot/parachain/grid/grid_topology_test.go @@ -78,14 +78,14 @@ func Test_SessionGridTopology(t *testing.T) { DiscoveryID: types.AuthorityID{1}, }, }) - assert.Equal(t, 2, len(gt.Peers)) + assert.Len(t, gt.Peers, 2) updated := gt.UpdateAuthoritiesIDs(peer.ID("peer2"), map[types.AuthorityID]struct{}{{1}: {}}) assert.False(t, updated) updated = gt.UpdateAuthoritiesIDs(peer.ID("peer3"), map[types.AuthorityID]struct{}{{1}: {}}) assert.True(t, updated) - assert.Equal(t, 3, len(gt.Peers)) + assert.Len(t, gt.Peers, 3) assert.Equal(t, peer.IDSlice{"peer1", "peer2", "peer3"}, gt.CanonicalShuffling[0].Peers, @@ -105,23 +105,23 @@ func Test_SessionGridTopologyNeighbours(t *testing.T) { gt = NewSessionGridTopology([]uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, FixtureTopologyPeerInfo()) gn, err := gt.ComputeGridNeighboursFor(10) assert.Nil(t, err) - assert.Equal(t, len(gn.ValidatorIndicesRow), 1) - assert.Equal(t, len(gn.ValidatorIndicesCol), 3) - assert.Equal(t, len(gn.PeersRow), 2) - assert.Equal(t, len(gn.PeersCol), 6) + assert.Len(t, gn.ValidatorIndicesRow, 1) + assert.Len(t, gn.ValidatorIndicesCol, 3) + assert.Len(t, gn.PeersRow, 2) + assert.Len(t, gn.PeersCol, 6) } func Test_MatrixNeighbours(t *testing.T) { m, err := calculateMatrixNeighbours(10, 11) assert.Nil(t, err) - assert.Equal(t, len(m.ColumnNeighbours), 3) - assert.Equal(t, len(m.RowNeighbours), 1) + assert.Len(t, m.ColumnNeighbours, 3) + assert.Len(t, m.RowNeighbours, 1) m, err = calculateMatrixNeighbours(4, 12) assert.Nil(t, err) - assert.Equal(t, len(m.ColumnNeighbours), 3) - assert.Equal(t, len(m.RowNeighbours), 2) + assert.Len(t, m.ColumnNeighbours, 3) + assert.Len(t, m.RowNeighbours, 2) } func Test_GridNeighbours(t *testing.T) { @@ -136,11 +136,10 @@ func Test_GridNeighbours(t *testing.T) { gt := NewSessionGridTopology([]uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, FixtureTopologyPeerInfo()) gn, err := gt.ComputeGridNeighboursFor(10) assert.Nil(t, err) - assert.Equal(t, len(gn.ValidatorIndicesRow), 1) - assert.Equal(t, len(gn.ValidatorIndicesCol), 3) - assert.Equal(t, len(gn.PeersRow), 2) - assert.Equal(t, len(gn.PeersCol), 6) - assert.Nil(t, err) + assert.Len(t, gn.ValidatorIndicesRow, 1) + assert.Len(t, gn.ValidatorIndicesCol, 3) + assert.Len(t, gn.PeersRow, 2) + assert.Len(t, gn.PeersCol, 6) routing := gn.RequiredRoutingByIndex(4, false) // The origin of the message was from column so we should rout in a row assert.Equal(t, RequiredRoutingGridX, routing) @@ -162,18 +161,18 @@ func Test_SessionGridTopologyEntry(t *testing.T) { SessionIndex: 99, } // Since routing is for rows we have only validator with index 9 there and its peers are peer19 and peer20 - assert.Equal(t, len(sgte.PeersToRoute(RequiredRoutingGridX)), 2) + assert.Len(t, sgte.PeersToRoute(RequiredRoutingGridX), 2) // Since routing is for rows we have only validator with index 1,4,7 there and its peers are peer19 and peer20 //[]peer.ID{"peer3", "peer4", "peer9", "peer10", "peer15", "peer16"}, - assert.Equal(t, len(sgte.PeersToRoute(RequiredRoutingGridY)), 6) - assert.Equal(t, len(sgte.PeersToRoute(RequiredRoutingAll)), 22) + assert.Len(t, sgte.PeersToRoute(RequiredRoutingGridY), 6) + assert.Len(t, sgte.PeersToRoute(RequiredRoutingAll), 22) updated, err := sgte.UpdateAuthoritiesIDs(peer.ID("peer99"), map[types.AuthorityID]struct{}{{10}: {}}) assert.True(t, updated) assert.Nil(t, err) // Now we added one more peer to validator with AuthorityID 10, index 9. Sine we are actins as validator with index 10 // we should route to this peer if strategy RequiredRoutingGridX - assert.Equal(t, len(sgte.PeersToRoute(RequiredRoutingGridX)), 3) + assert.Len(t, sgte.PeersToRoute(RequiredRoutingGridX), 3) } func Test_SessionGridTopologyStorage(t *testing.T) { From 1678bb7c7ec768a737ac148114b3dace23fef713 Mon Sep 17 00:00:00 2001 From: Kirill Pisarev Date: Mon, 3 Mar 2025 13:56:42 +0100 Subject: [PATCH 8/9] feat: fix routing strategy to panic in case of uknown strategy provided --- dot/parachain/grid/grid_topology.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dot/parachain/grid/grid_topology.go b/dot/parachain/grid/grid_topology.go index c0cf61d605..de619b9016 100644 --- a/dot/parachain/grid/grid_topology.go +++ b/dot/parachain/grid/grid_topology.go @@ -169,7 +169,7 @@ func (gn *GridNeighbours) ShouldRouteToPeer(routing RequiredRouting, peer peer.I return y default: // No way we get here - return false + panic(fmt.Errorf("unknown routing strategy for grid topology: %d", routing)) } } From 12c3109f4966b67a1bf45bf2a9ac841016625b3f Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 3 Mar 2025 15:27:12 +0100 Subject: [PATCH 9/9] Update dot/parachain/grid/grid_topology.go Co-authored-by: Axay Sagathiya --- dot/parachain/grid/grid_topology.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dot/parachain/grid/grid_topology.go b/dot/parachain/grid/grid_topology.go index de619b9016..735817e9b8 100644 --- a/dot/parachain/grid/grid_topology.go +++ b/dot/parachain/grid/grid_topology.go @@ -169,7 +169,7 @@ func (gn *GridNeighbours) ShouldRouteToPeer(routing RequiredRouting, peer peer.I return y default: // No way we get here - panic(fmt.Errorf("unknown routing strategy for grid topology: %d", routing)) + panic(fmt.Sprintf("unknown routing strategy for grid topology: %d", routing)) } }