github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/p2p/test_util.go (about)

     1  package p2p
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"time"
     7  
     8  	"github.com/gnolang/gno/tm2/pkg/crypto"
     9  	"github.com/gnolang/gno/tm2/pkg/crypto/ed25519"
    10  	"github.com/gnolang/gno/tm2/pkg/errors"
    11  	"github.com/gnolang/gno/tm2/pkg/log"
    12  	"github.com/gnolang/gno/tm2/pkg/p2p/config"
    13  	"github.com/gnolang/gno/tm2/pkg/p2p/conn"
    14  	"github.com/gnolang/gno/tm2/pkg/random"
    15  	"github.com/gnolang/gno/tm2/pkg/versionset"
    16  )
    17  
    18  const testCh = 0x01
    19  
    20  // ------------------------------------------------
    21  
    22  func AddPeerToSwitchPeerSet(sw *Switch, peer Peer) {
    23  	sw.peers.Add(peer)
    24  }
    25  
    26  func CreateRandomPeer(outbound bool) *peer {
    27  	addr, netAddr := CreateRoutableAddr()
    28  	p := &peer{
    29  		peerConn: peerConn{
    30  			outbound:   outbound,
    31  			socketAddr: netAddr,
    32  		},
    33  		nodeInfo: NodeInfo{NetAddress: netAddr},
    34  		mconn:    &conn.MConnection{},
    35  	}
    36  	p.SetLogger(log.NewNoopLogger().With("peer", addr))
    37  	return p
    38  }
    39  
    40  func CreateRoutableAddr() (addr string, netAddr *NetAddress) {
    41  	for {
    42  		id := ed25519.GenPrivKey().PubKey().Address().ID()
    43  		var err error
    44  		addr = fmt.Sprintf("%s@%v.%v.%v.%v:26656", id, random.RandInt()%256, random.RandInt()%256, random.RandInt()%256, random.RandInt()%256)
    45  		netAddr, err = NewNetAddressFromString(addr)
    46  		if err != nil {
    47  			panic(err)
    48  		}
    49  		if netAddr.Routable() {
    50  			break
    51  		}
    52  	}
    53  	return
    54  }
    55  
    56  // ------------------------------------------------------------------
    57  // Connects switches via arbitrary net.Conn. Used for testing.
    58  
    59  const TEST_HOST = "localhost"
    60  
    61  // MakeConnectedSwitches returns n switches, connected according to the connect func.
    62  // If connect==Connect2Switches, the switches will be fully connected.
    63  // initSwitch defines how the i'th switch should be initialized (ie. with what reactors).
    64  // NOTE: panics if any switch fails to start.
    65  func MakeConnectedSwitches(cfg *config.P2PConfig, n int, initSwitch func(int, *Switch) *Switch, connect func([]*Switch, int, int)) []*Switch {
    66  	switches := make([]*Switch, n)
    67  	for i := 0; i < n; i++ {
    68  		switches[i] = MakeSwitch(cfg, i, TEST_HOST, "123.123.123", initSwitch)
    69  	}
    70  
    71  	if err := StartSwitches(switches); err != nil {
    72  		panic(err)
    73  	}
    74  
    75  	for i := 0; i < n; i++ {
    76  		for j := i + 1; j < n; j++ {
    77  			connect(switches, i, j)
    78  		}
    79  	}
    80  
    81  	return switches
    82  }
    83  
    84  // Connect2Switches will connect switches i and j via net.Pipe().
    85  // Blocks until a connection is established.
    86  // NOTE: caller ensures i and j are within bounds.
    87  func Connect2Switches(switches []*Switch, i, j int) {
    88  	switchI := switches[i]
    89  	switchJ := switches[j]
    90  
    91  	c1, c2 := conn.NetPipe()
    92  
    93  	doneCh := make(chan struct{})
    94  	go func() {
    95  		err := switchI.addPeerWithConnection(c1)
    96  		if err != nil {
    97  			panic(err)
    98  		}
    99  		doneCh <- struct{}{}
   100  	}()
   101  	go func() {
   102  		err := switchJ.addPeerWithConnection(c2)
   103  		if err != nil {
   104  			panic(err)
   105  		}
   106  		doneCh <- struct{}{}
   107  	}()
   108  	<-doneCh
   109  	<-doneCh
   110  }
   111  
   112  func (sw *Switch) addPeerWithConnection(conn net.Conn) error {
   113  	pc, err := testInboundPeerConn(conn, sw.config, sw.nodeKey.PrivKey)
   114  	if err != nil {
   115  		if err := conn.Close(); err != nil {
   116  			sw.Logger.Error("Error closing connection", "err", err)
   117  		}
   118  		return err
   119  	}
   120  
   121  	ni, err := handshake(conn, time.Second, sw.nodeInfo)
   122  	if err != nil {
   123  		if err := conn.Close(); err != nil {
   124  			sw.Logger.Error("Error closing connection", "err", err)
   125  		}
   126  		return err
   127  	}
   128  
   129  	p := newPeer(
   130  		pc,
   131  		MConnConfig(sw.config),
   132  		ni,
   133  		sw.reactorsByCh,
   134  		sw.chDescs,
   135  		sw.StopPeerForError,
   136  	)
   137  
   138  	if err = sw.addPeer(p); err != nil {
   139  		pc.CloseConn()
   140  		return err
   141  	}
   142  
   143  	return nil
   144  }
   145  
   146  // StartSwitches calls sw.Start() for each given switch.
   147  // It returns the first encountered error.
   148  func StartSwitches(switches []*Switch) error {
   149  	for _, s := range switches {
   150  		err := s.Start() // start switch and reactors
   151  		if err != nil {
   152  			return err
   153  		}
   154  	}
   155  	return nil
   156  }
   157  
   158  func MakeSwitch(
   159  	cfg *config.P2PConfig,
   160  	i int,
   161  	network, version string,
   162  	initSwitch func(int, *Switch) *Switch,
   163  	opts ...SwitchOption,
   164  ) *Switch {
   165  	nodeKey := NodeKey{
   166  		PrivKey: ed25519.GenPrivKey(),
   167  	}
   168  	nodeInfo := testNodeInfo(nodeKey.ID(), fmt.Sprintf("node%d", i))
   169  
   170  	t := NewMultiplexTransport(nodeInfo, nodeKey, MConnConfig(cfg))
   171  
   172  	if err := t.Listen(*nodeInfo.NetAddress); err != nil {
   173  		panic(err)
   174  	}
   175  
   176  	// TODO: let the config be passed in?
   177  	sw := initSwitch(i, NewSwitch(cfg, t, opts...))
   178  	sw.SetLogger(log.NewNoopLogger().With("switch", i))
   179  	sw.SetNodeKey(&nodeKey)
   180  
   181  	for ch := range sw.reactorsByCh {
   182  		nodeInfo.Channels = append(nodeInfo.Channels, ch)
   183  	}
   184  
   185  	// TODO: We need to setup reactors ahead of time so the NodeInfo is properly
   186  	// populated and we don't have to do those awkward overrides and setters.
   187  	t.nodeInfo = nodeInfo
   188  	sw.SetNodeInfo(nodeInfo)
   189  
   190  	return sw
   191  }
   192  
   193  func testInboundPeerConn(
   194  	conn net.Conn,
   195  	config *config.P2PConfig,
   196  	ourNodePrivKey crypto.PrivKey,
   197  ) (peerConn, error) {
   198  	return testPeerConn(conn, config, false, false, ourNodePrivKey, nil)
   199  }
   200  
   201  func testPeerConn(
   202  	rawConn net.Conn,
   203  	cfg *config.P2PConfig,
   204  	outbound, persistent bool,
   205  	ourNodePrivKey crypto.PrivKey,
   206  	socketAddr *NetAddress,
   207  ) (pc peerConn, err error) {
   208  	conn := rawConn
   209  
   210  	// Fuzz connection
   211  	if cfg.TestFuzz {
   212  		// so we have time to do peer handshakes and get set up
   213  		conn = FuzzConnAfterFromConfig(conn, 10*time.Second, cfg.TestFuzzConfig)
   214  	}
   215  
   216  	// Encrypt connection
   217  	conn, err = upgradeSecretConn(conn, cfg.HandshakeTimeout, ourNodePrivKey)
   218  	if err != nil {
   219  		return pc, errors.Wrap(err, "Error creating peer")
   220  	}
   221  
   222  	// Only the information we already have
   223  	return newPeerConn(outbound, persistent, conn, socketAddr), nil
   224  }
   225  
   226  // ----------------------------------------------------------------
   227  // rand node info
   228  
   229  func testNodeInfo(id ID, name string) NodeInfo {
   230  	return testNodeInfoWithNetwork(id, name, "testing")
   231  }
   232  
   233  func testVersionSet() versionset.VersionSet {
   234  	return versionset.VersionSet{
   235  		versionset.VersionInfo{
   236  			Name:    "p2p",
   237  			Version: "v0.0.0", // dontcare
   238  		},
   239  	}
   240  }
   241  
   242  func testNodeInfoWithNetwork(id ID, name, network string) NodeInfo {
   243  	return NodeInfo{
   244  		VersionSet: testVersionSet(),
   245  		NetAddress: NewNetAddressFromIPPort(id, net.ParseIP("127.0.0.1"), 0),
   246  		Network:    network,
   247  		Software:   "p2ptest",
   248  		Version:    "v1.2.3-rc.0-deadbeef",
   249  		Channels:   []byte{testCh},
   250  		Moniker:    name,
   251  		Other: NodeInfoOther{
   252  			TxIndex:    "on",
   253  			RPCAddress: fmt.Sprintf("127.0.0.1:%d", 0),
   254  		},
   255  	}
   256  }