You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

287 lines
6.8 KiB

package p2p
import (
"context"
"fmt"
mrand "math/rand"
"net"
"github.com/tendermint/tendermint/libs/log"
tmnet "github.com/tendermint/tendermint/libs/net"
tmrand "github.com/tendermint/tendermint/libs/rand"
"github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/internal/p2p/conn"
)
const testCh = 0x01
//------------------------------------------------
func AddPeerToSwitchPeerSet(sw *Switch, peer Peer) {
sw.peers.Add(peer) //nolint:errcheck // ignore error
}
func CreateRandomPeer(outbound bool) Peer {
addr, netAddr := CreateRoutableAddr()
p := &peer{
peerConn: peerConn{outbound: outbound},
nodeInfo: NodeInfo{
NodeID: netAddr.ID,
ListenAddr: netAddr.DialString(),
},
metrics: NopMetrics(),
}
p.SetLogger(log.TestingLogger().With("peer", addr))
return p
}
// nolint:gosec // G404: Use of weak random number generator
func CreateRoutableAddr() (addr string, netAddr *NetAddress) {
for {
var err error
addr = fmt.Sprintf("%X@%v.%v.%v.%v:26656",
tmrand.Bytes(20),
mrand.Int()%256,
mrand.Int()%256,
mrand.Int()%256,
mrand.Int()%256)
netAddr, err = NewNetAddressString(addr)
if err != nil {
panic(err)
}
if netAddr.Routable() {
break
}
}
return
}
//------------------------------------------------------------------
// Connects switches via arbitrary net.Conn. Used for testing.
const TestHost = "localhost"
// MakeConnectedSwitches returns n switches, connected according to the connect func.
// If connect==Connect2Switches, the switches will be fully connected.
// initSwitch defines how the i'th switch should be initialized (ie. with what reactors).
// NOTE: panics if any switch fails to start.
func MakeConnectedSwitches(cfg *config.P2PConfig,
n int,
initSwitch func(int, *Switch) *Switch,
connect func([]*Switch, int, int),
) []*Switch {
switches := make([]*Switch, n)
for i := 0; i < n; i++ {
switches[i] = MakeSwitch(cfg, i, TestHost, "123.123.123", initSwitch, log.TestingLogger())
}
if err := StartSwitches(switches); err != nil {
panic(err)
}
for i := 0; i < n; i++ {
for j := i + 1; j < n; j++ {
connect(switches, i, j)
}
}
return switches
}
// Connect2Switches will connect switches i and j via net.Pipe().
// Blocks until a connection is established.
// NOTE: caller ensures i and j are within bounds.
func Connect2Switches(switches []*Switch, i, j int) {
switchI := switches[i]
switchJ := switches[j]
c1, c2 := conn.NetPipe()
doneCh := make(chan struct{})
go func() {
err := switchI.addPeerWithConnection(c1)
if err != nil {
panic(err)
}
doneCh <- struct{}{}
}()
go func() {
err := switchJ.addPeerWithConnection(c2)
if err != nil {
panic(err)
}
doneCh <- struct{}{}
}()
<-doneCh
<-doneCh
}
func (sw *Switch) addPeerWithConnection(conn net.Conn) error {
pc, err := testInboundPeerConn(sw.transport.(*MConnTransport), conn)
if err != nil {
if err := conn.Close(); err != nil {
sw.Logger.Error("Error closing connection", "err", err)
}
return err
}
peerNodeInfo, _, err := pc.conn.Handshake(context.Background(), sw.nodeInfo, sw.nodeKey.PrivKey)
if err != nil {
if err := conn.Close(); err != nil {
sw.Logger.Error("Error closing connection", "err", err)
}
return err
}
p := newPeer(
peerNodeInfo,
pc,
sw.reactorsByCh,
sw.StopPeerForError,
)
if err = sw.addPeer(p); err != nil {
pc.CloseConn()
return err
}
return nil
}
// StartSwitches calls sw.Start() for each given switch.
// It returns the first encountered error.
func StartSwitches(switches []*Switch) error {
for _, s := range switches {
err := s.Start() // start switch and reactors
if err != nil {
return err
}
}
return nil
}
func MakeSwitch(
cfg *config.P2PConfig,
i int,
network, version string,
initSwitch func(int, *Switch) *Switch,
logger log.Logger,
opts ...SwitchOption,
) *Switch {
nodeKey := GenNodeKey()
nodeInfo := testNodeInfo(nodeKey.ID, fmt.Sprintf("node%d", i))
addr, err := NewNetAddressString(
IDAddressString(nodeKey.ID, nodeInfo.ListenAddr),
)
if err != nil {
panic(err)
}
swLogger := logger.With("switch", i)
t := NewMConnTransport(swLogger, MConnConfig(cfg),
[]*ChannelDescriptor{}, MConnTransportOptions{})
// TODO: let the config be passed in?
sw := initSwitch(i, NewSwitch(cfg, t, opts...))
sw.SetLogger(swLogger)
sw.SetNodeKey(nodeKey)
if err := t.Listen(addr.Endpoint()); err != nil {
panic(err)
}
ni := nodeInfo
ni.Channels = []byte{}
for ch := range sw.reactorsByCh {
ni.Channels = append(ni.Channels, ch)
}
nodeInfo = ni
// TODO: We need to setup reactors ahead of time so the NodeInfo is properly
// populated and we don't have to do those awkward overrides and setters.
sw.SetNodeInfo(nodeInfo)
return sw
}
func testInboundPeerConn(
transport *MConnTransport,
conn net.Conn,
) (peerConn, error) {
return testPeerConn(transport, conn, false, false)
}
func testPeerConn(
transport *MConnTransport,
rawConn net.Conn,
outbound, persistent bool,
) (pc peerConn, err error) {
conn := newMConnConnection(transport.logger, rawConn, transport.mConnConfig, transport.channelDescs)
return newPeerConn(outbound, persistent, conn), nil
}
//----------------------------------------------------------------
// rand node info
func testNodeInfo(id NodeID, name string) NodeInfo {
return testNodeInfoWithNetwork(id, name, "testing")
}
func testNodeInfoWithNetwork(id NodeID, name, network string) NodeInfo {
return NodeInfo{
ProtocolVersion: defaultProtocolVersion,
NodeID: id,
ListenAddr: fmt.Sprintf("127.0.0.1:%d", getFreePort()),
Network: network,
Version: "1.2.3-rc0-deadbeef",
Channels: []byte{testCh},
Moniker: name,
Other: NodeInfoOther{
TxIndex: "on",
RPCAddress: fmt.Sprintf("127.0.0.1:%d", getFreePort()),
},
}
}
func getFreePort() int {
port, err := tmnet.GetFreePort()
if err != nil {
panic(err)
}
return port
}
type AddrBookMock struct {
Addrs map[string]struct{}
OurAddrs map[string]struct{}
PrivateAddrs map[string]struct{}
}
var _ AddrBook = (*AddrBookMock)(nil)
func (book *AddrBookMock) AddAddress(addr *NetAddress, src *NetAddress) error {
book.Addrs[addr.String()] = struct{}{}
return nil
}
func (book *AddrBookMock) AddOurAddress(addr *NetAddress) { book.OurAddrs[addr.String()] = struct{}{} }
func (book *AddrBookMock) OurAddress(addr *NetAddress) bool {
_, ok := book.OurAddrs[addr.String()]
return ok
}
func (book *AddrBookMock) MarkGood(NodeID) {}
func (book *AddrBookMock) HasAddress(addr *NetAddress) bool {
_, ok := book.Addrs[addr.String()]
return ok
}
func (book *AddrBookMock) RemoveAddress(addr *NetAddress) {
delete(book.Addrs, addr.String())
}
func (book *AddrBookMock) Save() {}
func (book *AddrBookMock) AddPrivateIDs(addrs []string) {
for _, addr := range addrs {
book.PrivateAddrs[addr] = struct{}{}
}
}