Skip to content
This repository has been archived by the owner on Aug 2, 2021. It is now read-only.

p2p/simulation: move connection methods from swarm/network/simulation #1057

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 60 additions & 34 deletions swarm/network/simulation/connect.go → p2p/simulations/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,38 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package simulation
package simulations

import (
"errors"
"strings"

"github.com/ethereum/go-ethereum/p2p/enode"
)

var (
ErrNodeNotFound = errors.New("node not found")
ErrNoPivotNode = errors.New("no pivot node set")
)

// ConnectToPivotNode connects the node with provided NodeID
// to the pivot node, already set by Simulation.SetPivotNode method.
// to the pivot node, already set by Network.SetPivotNode method.
// It is useful when constructing a star network topology
// when simulation adds and removes nodes dynamically.
func (s *Simulation) ConnectToPivotNode(id enode.ID) (err error) {
pid := s.PivotNodeID()
if pid == nil {
// when Network adds and removes nodes dynamically.
func (net *Network) ConnectToPivotNode(id enode.ID) (err error) {
pivot := net.PivotNodeID()
if pivot == nil {
return ErrNoPivotNode
}
return s.connect(*pid, id)
return net.connect(*pivot, id)
}

// ConnectToLastNode connects the node with provided NodeID
// to the last node that is up, and avoiding connection to self.
// It is useful when constructing a chain network topology
// when simulation adds and removes nodes dynamically.
func (s *Simulation) ConnectToLastNode(id enode.ID) (err error) {
ids := s.UpNodeIDs()
// when Network adds and removes nodes dynamically.
func (net *Network) ConnectToLastNode(id enode.ID) (err error) {
ids := net.GetUpNodeIDs()
frncmx marked this conversation as resolved.
Show resolved Hide resolved
l := len(ids)
if l < 2 {
return nil
Expand All @@ -48,30 +54,31 @@ func (s *Simulation) ConnectToLastNode(id enode.ID) (err error) {
if lid == id {
lid = ids[l-2]
}
return s.connect(lid, id)
return net.connect(lid, id)
}

// ConnectToRandomNode connects the node with provieded NodeID
// ConnectToRandomNode connects the node with provided NodeID
// to a random node that is up.
func (s *Simulation) ConnectToRandomNode(id enode.ID) (err error) {
n := s.RandomUpNode(id)
func (net *Network) ConnectToRandomNode(id enode.ID) (err error) {
nolash marked this conversation as resolved.
Show resolved Hide resolved
n := net.GetRandomUpNode(id)

if n == nil {
return ErrNodeNotFound
}
return s.connect(n.ID, id)
return net.connect(n.ID(), id)
}

// ConnectNodesFull connects all nodes one to another.
// It provides a complete connectivity in the network
// which should be rarely needed.
func (s *Simulation) ConnectNodesFull(ids []enode.ID) (err error) {
func (net *Network) ConnectNodesFull(ids []enode.ID) (err error) {
if ids == nil {
ids = s.UpNodeIDs()
ids = net.GetUpNodeIDs()
}
l := len(ids)
for i := 0; i < l; i++ {
for j := i + 1; j < l; j++ {
err = s.connect(ids[i], ids[j])
err = net.connect(ids[i], ids[j])
if err != nil {
return err
}
Expand All @@ -82,13 +89,13 @@ func (s *Simulation) ConnectNodesFull(ids []enode.ID) (err error) {

// ConnectNodesChain connects all nodes in a chain topology.
// If ids argument is nil, all nodes that are up will be connected.
func (s *Simulation) ConnectNodesChain(ids []enode.ID) (err error) {
func (net *Network) ConnectNodesChain(ids []enode.ID) (err error) {
if ids == nil {
ids = s.UpNodeIDs()
ids = net.GetUpNodeIDs()
}
l := len(ids)
for i := 0; i < l-1; i++ {
err = s.connect(ids[i], ids[i+1])
err = net.connect(ids[i], ids[i+1])
if err != nil {
return err
}
Expand All @@ -98,36 +105,36 @@ func (s *Simulation) ConnectNodesChain(ids []enode.ID) (err error) {

// ConnectNodesRing connects all nodes in a ring topology.
// If ids argument is nil, all nodes that are up will be connected.
func (s *Simulation) ConnectNodesRing(ids []enode.ID) (err error) {
func (net *Network) ConnectNodesRing(ids []enode.ID) (err error) {
if ids == nil {
ids = s.UpNodeIDs()
ids = net.GetUpNodeIDs()
}
l := len(ids)
if l < 2 {
return nil
}
for i := 0; i < l-1; i++ {
err = s.connect(ids[i], ids[i+1])
err = net.connect(ids[i], ids[i+1])
if err != nil {
return err
}
}
return s.connect(ids[l-1], ids[0])
return net.connect(ids[l-1], ids[0])
}

// ConnectNodesStar connects all nodes in a star topology
// with the center at provided NodeID.
// If ids argument is nil, all nodes that are up will be connected.
func (s *Simulation) ConnectNodesStar(id enode.ID, ids []enode.ID) (err error) {
func (net *Network) ConnectNodesStar(id enode.ID, ids []enode.ID) (err error) {
if ids == nil {
ids = s.UpNodeIDs()
ids = net.GetUpNodeIDs()
}
l := len(ids)
for i := 0; i < l; i++ {
if id == ids[i] {
continue
}
err = s.connect(id, ids[i])
err = net.connect(id, ids[i])
if err != nil {
return err
}
Expand All @@ -138,17 +145,17 @@ func (s *Simulation) ConnectNodesStar(id enode.ID, ids []enode.ID) (err error) {
// ConnectNodesStarPivot connects all nodes in a star topology
// with the center at already set pivot node.
// If ids argument is nil, all nodes that are up will be connected.
func (s *Simulation) ConnectNodesStarPivot(ids []enode.ID) (err error) {
id := s.PivotNodeID()
if id == nil {
func (net *Network) ConnectNodesStarPivot(ids []enode.ID) (err error) {
pivot := net.PivotNodeID()
if pivot == nil {
return ErrNoPivotNode
}
return s.ConnectNodesStar(*id, ids)
return net.ConnectNodesStar(*pivot, ids)
}

// connect connects two nodes but ignores already connected error.
func (s *Simulation) connect(oneID, otherID enode.ID) error {
return ignoreAlreadyConnectedErr(s.Net.Connect(oneID, otherID))
func (net *Network) connect(oneID, otherID enode.ID) error {
return ignoreAlreadyConnectedErr(net.Connect(oneID, otherID))
}

func ignoreAlreadyConnectedErr(err error) error {
Expand All @@ -157,3 +164,22 @@ func ignoreAlreadyConnectedErr(err error) error {
}
return err
}

// SetPivotNode sets the NodeID of the network's pivot node.
// Pivot node is just a specific node that should be treated
// differently then other nodes in test. SetPivotNode and
// PivotNodeID are just a convenient functions to set and
// retrieve it.
func (net *Network) SetPivotNode(id enode.ID) {
net.lock.Lock()
defer net.lock.Unlock()
net.pivotNodeID = &id
}

// PivotNodeID returns NodeID of the pivot node set by
// Network.SetPivotNode method.
func (net *Network) PivotNodeID() (id *enode.ID) {
frncmx marked this conversation as resolved.
Show resolved Hide resolved
net.lock.RLock()
defer net.lock.RUnlock()
return net.pivotNodeID
}
190 changes: 190 additions & 0 deletions p2p/simulations/connect_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package simulations

import (
"testing"

"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
)

func newTestNetwork(t *testing.T, nodeCount int) (*Network, []enode.ID) {
adapter := adapters.NewSimAdapter(adapters.Services{
"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
return NewNoopService(nil), nil
},
})

// create network
network := NewNetwork(adapter, &NetworkConfig{
DefaultService: "noopwoop",
})

// create and start nodes
ids := make([]enode.ID, nodeCount)
for i := range ids {
conf := adapters.RandomNodeConfig()
node, err := network.NewNodeWithConfig(conf)
if err != nil {
t.Fatalf("error creating node: %s", err)
}
if err := network.Start(node.ID()); err != nil {
t.Fatalf("error starting node: %s", err)
}
ids[i] = node.ID()
}

if len(network.Conns) > 0 {
frncmx marked this conversation as resolved.
Show resolved Hide resolved
t.Fatal("no connections should exist after just adding nodes")
}

return network, ids
}

func TestConnectToPivotNode(t *testing.T) {
net, ids := newTestNetwork(t, 2)
defer net.Shutdown()

pivot := ids[0]
net.SetPivotNode(pivot)

other := ids[1]
err := net.ConnectToPivotNode(other)
if err != nil {
t.Fatal(err)
}

if net.GetConn(pivot, other) == nil {
frncmx marked this conversation as resolved.
Show resolved Hide resolved
t.Error("pivot and the other node are not connected")
}
}

func TestConnectToLastNode(t *testing.T) {
net, ids := newTestNetwork(t, 10)
defer net.Shutdown()

first := ids[0]
if err := net.ConnectToLastNode(first); err != nil {
t.Fatal(err)
}

last := ids[len(ids)-1]
for i, id := range ids {
if id == first || id == last {
continue
}

if net.GetConn(first, id) != nil {
t.Errorf("connection must not exits with node(ind: %v, id: %v)", i, id)
frncmx marked this conversation as resolved.
Show resolved Hide resolved
}
}

if net.GetConn(first, last) == nil {
t.Error("first and last node must be connected")
}
}

func TestConnectToRandomNode(t *testing.T) {
net, ids := newTestNetwork(t, 10)
defer net.Shutdown()

err := net.ConnectToRandomNode(ids[0])
if err != nil {
t.Fatal(err)
}

var cc int
for i, a := range ids {
for _, b := range ids[i:] {
if net.GetConn(a, b) != nil {
cc++
}
}
}

if cc != 1 {
t.Errorf("expected one connection, got %v", cc)
}
}

func TestConnectNodesFull(t *testing.T) {
net, ids := newTestNetwork(t, 12)
defer net.Shutdown()

err := net.ConnectNodesFull(ids)
if err != nil {
t.Fatal(err)
}

VerifyFull(t, net, ids)
frncmx marked this conversation as resolved.
Show resolved Hide resolved
}

func TestConnectNodesChain(t *testing.T) {
net, ids := newTestNetwork(t, 10)
defer net.Shutdown()

err := net.ConnectNodesChain(ids)
if err != nil {
t.Fatal(err)
}

VerifyChain(t, net, ids)
}

func TestConnectNodesRing(t *testing.T) {
net, ids := newTestNetwork(t, 10)
defer net.Shutdown()

err := net.ConnectNodesRing(ids)
if err != nil {
t.Fatal(err)
}

VerifyRing(t, net, ids)
}

func TestConnectNodesStar(t *testing.T) {
net, ids := newTestNetwork(t, 10)
defer net.Shutdown()

pivotIndex := 2

err := net.ConnectNodesStar(ids[pivotIndex], ids)
frncmx marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
t.Fatal(err)
}

VerifyStar(t, net, ids, pivotIndex)
}

func TestConnectNodesStarPivot(t *testing.T) {
net, ids := newTestNetwork(t, 10)
defer net.Shutdown()

pivotIndex := 4

net.SetPivotNode(ids[pivotIndex])

err := net.ConnectNodesStarPivot(ids)
if err != nil {
t.Fatal(err)
}

VerifyStar(t, net, ids, pivotIndex)
}
Loading