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{}{}
|
|
}
|
|
}
|