From a977da67d7448cef5d192ca14648abfec142fc18 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Tue, 30 Sep 2025 14:24:47 +0200 Subject: [PATCH 1/9] MConnTransport -> Transport WIP --- internal/p2p/address.go | 78 +---- internal/p2p/address_test.go | 143 +++------ internal/p2p/p2ptest/network.go | 4 +- internal/p2p/peermanager_scoring_test.go | 4 +- internal/p2p/peermanager_test.go | 231 ++++++-------- internal/p2p/pex/reactor_test.go | 4 +- internal/p2p/router.go | 10 +- internal/p2p/transport.go | 378 ++++++++++++++++++----- internal/p2p/transport_mconn.go | 342 -------------------- internal/p2p/transport_mconn_test.go | 43 +-- internal/p2p/transport_memory.go | 3 - internal/p2p/transport_test.go | 112 +------ 12 files changed, 488 insertions(+), 864 deletions(-) delete mode 100644 internal/p2p/transport_mconn.go delete mode 100644 internal/p2p/transport_memory.go diff --git a/internal/p2p/address.go b/internal/p2p/address.go index 18d95100b..a6efe686a 100644 --- a/internal/p2p/address.go +++ b/internal/p2p/address.go @@ -33,10 +33,8 @@ var ( // is expected to contain a node ID. type NodeAddress struct { NodeID types.NodeID - Protocol Protocol Hostname string Port uint16 - Path string } // ParseNodeAddress parses a node address URL into a NodeAddress, normalizing @@ -47,15 +45,13 @@ func ParseNodeAddress(urlString string) (NodeAddress, error) { url, err := url.Parse(urlString) if (err != nil || url.Scheme == "") && (!stringHasScheme(urlString) || reSchemeIsHost.MatchString(urlString)) { - url, err = url.Parse(string(defaultProtocol) + "://" + urlString) + url, err = url.Parse("mconn://" + urlString) } if err != nil { return NodeAddress{}, fmt.Errorf("invalid node address %q: %w", urlString, err) } - address := NodeAddress{ - Protocol: Protocol(strings.ToLower(url.Scheme)), - } + address := NodeAddress{} // Opaque URLs are expected to contain only a node ID. if url.Opaque != "" { @@ -77,45 +73,12 @@ func ParseNodeAddress(urlString string) (NodeAddress, error) { } address.Port = uint16(port64) } - - address.Path = url.Path - if url.RawQuery != "" { - address.Path += "?" + url.RawQuery - } - if url.Fragment != "" { - address.Path += "#" + url.Fragment - } - if address.Path != "" { - switch address.Path[0] { - case '/', '#', '?': - default: - address.Path = "/" + address.Path - } - } - return address, address.Validate() } // Resolve resolves a NodeAddress into a set of Endpoints, by expanding // out a DNS hostname to IP addresses. func (a NodeAddress) Resolve(ctx context.Context) ([]Endpoint, error) { - if a.Protocol == "" { - return nil, errors.New("address has no protocol") - } - - // If there is no hostname, this is an opaque URL in the form - // "scheme:opaque", and the opaque part is assumed to be node ID used as - // Path. - if a.Hostname == "" { - if a.NodeID == "" { - return nil, errors.New("local address has no node ID") - } - return []Endpoint{{ - Protocol: a.Protocol, - Path: string(a.NodeID), - }}, nil - } - ips, err := net.DefaultResolver.LookupIP(ctx, "ip", a.Hostname) if err != nil { return nil, err @@ -126,55 +89,34 @@ func (a NodeAddress) Resolve(ctx context.Context) ([]Endpoint, error) { if !ok { return nil, fmt.Errorf("LookupIP returned invalid IP %q", ip) } - endpoints[i] = Endpoint{ - Protocol: a.Protocol, - Addr: netip.AddrPortFrom(ip, a.Port), - Path: a.Path, - } + endpoints[i] = Endpoint{netip.AddrPortFrom(ip, a.Port)} } return endpoints, nil } // String formats the address as a URL string. func (a NodeAddress) String() string { - u := url.URL{Scheme: string(a.Protocol)} + u := url.URL{Scheme: "mconn"} if a.NodeID != "" { u.User = url.User(string(a.NodeID)) } - switch { - case a.Hostname != "": - if a.Port > 0 { - u.Host = net.JoinHostPort(a.Hostname, strconv.Itoa(int(a.Port))) - } else { - u.Host = a.Hostname - } - u.Path = a.Path - - case a.Protocol != "" && (a.Path == "" || a.Path == string(a.NodeID)): - u.User = nil - u.Opaque = string(a.NodeID) // e.g. memory:id - - case a.Path != "" && a.Path[0] != '/': - u.Path = "/" + a.Path // e.g. some/path - - default: - u.Path = a.Path // e.g. /some/path + if a.Port > 0 { + u.Host = net.JoinHostPort(a.Hostname, strconv.Itoa(int(a.Port))) + } else { + u.Host = a.Hostname } return strings.TrimPrefix(u.String(), "//") } // Validate validates a NodeAddress. func (a NodeAddress) Validate() error { - if a.Protocol == "" { - return errors.New("no protocol") - } if a.NodeID == "" { return errors.New("no peer ID") } else if err := a.NodeID.Validate(); err != nil { return fmt.Errorf("invalid peer ID: %w", err) } - if a.Port > 0 && a.Hostname == "" { - return errors.New("cannot specify port without hostname") + if a.Hostname == "" { + return errors.New("missing hostname") } return nil } diff --git a/internal/p2p/address_test.go b/internal/p2p/address_test.go index 6b660aac1..5a58b76b9 100644 --- a/internal/p2p/address_test.go +++ b/internal/p2p/address_test.go @@ -2,7 +2,6 @@ package p2p_test import ( "net/netip" - "strings" "testing" "github.com/tendermint/tendermint/crypto/ed25519" @@ -105,69 +104,49 @@ func TestParseNodeAddress(t *testing.T) { ok bool }{ // Valid addresses. - { - "mconn://" + user + "@127.0.0.1:26657/some/path?foo=bar", - p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "127.0.0.1", Port: 26657, Path: "/some/path?foo=bar"}, - true, - }, - { - "TCP://" + strings.ToUpper(user) + "@hostname.DOMAIN:8080/Path/%f0%9f%91%8B#Anchor", - p2p.NodeAddress{Protocol: "tcp", NodeID: id, Hostname: "hostname.domain", Port: 8080, Path: "/Path/👋#Anchor"}, - true, - }, { user + "@127.0.0.1", - p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "127.0.0.1"}, + p2p.NodeAddress{NodeID: id, Hostname: "127.0.0.1"}, true, }, { user + "@hostname.domain", - p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "hostname.domain"}, + p2p.NodeAddress{NodeID: id, Hostname: "hostname.domain"}, true, }, { user + "@hostname.domain:80", - p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "hostname.domain", Port: 80}, + p2p.NodeAddress{NodeID: id, Hostname: "hostname.domain", Port: 80}, true, }, { user + "@%F0%9F%91%8B", - p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "👋"}, + p2p.NodeAddress{NodeID: id, Hostname: "👋"}, true, }, { user + "@%F0%9F%91%8B:80/path", - p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "👋", Port: 80, Path: "/path"}, + p2p.NodeAddress{NodeID: id, Hostname: "👋", Port: 80}, true, }, { user + "@127.0.0.1:26657", - p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "127.0.0.1", Port: 26657}, - true, - }, - { - user + "@127.0.0.1:26657/path", - p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "127.0.0.1", Port: 26657, Path: "/path"}, + p2p.NodeAddress{NodeID: id, Hostname: "127.0.0.1", Port: 26657}, true, }, { user + "@0.0.0.0:0", - p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "0.0.0.0", Port: 0}, - true, - }, - { - "memory:" + user, - p2p.NodeAddress{Protocol: "memory", NodeID: id}, + p2p.NodeAddress{NodeID: id, Hostname: "0.0.0.0", Port: 0}, true, }, { user + "@[1::]", - p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "1::"}, + p2p.NodeAddress{NodeID: id, Hostname: "1::"}, true, }, { - "mconn://" + user + "@[fd80:b10c::2]:26657", - p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "fd80:b10c::2", Port: 26657}, + user + "@[fd80:b10c::2]:26657", + p2p.NodeAddress{NodeID: id, Hostname: "fd80:b10c::2", Port: 26657}, true, }, @@ -176,7 +155,6 @@ func TestParseNodeAddress(t *testing.T) { {"127.0.0.1", p2p.NodeAddress{}, false}, {"hostname", p2p.NodeAddress{}, false}, {"scheme:", p2p.NodeAddress{}, false}, - {"memory:foo", p2p.NodeAddress{}, false}, {user + "@%F%F0", p2p.NodeAddress{}, false}, {"//" + user + "@127.0.0.1", p2p.NodeAddress{}, false}, {"://" + user + "@127.0.0.1", p2p.NodeAddress{}, false}, @@ -198,8 +176,6 @@ func TestParseNodeAddress(t *testing.T) { } func TestNodeAddress_Resolve(t *testing.T) { - id := types.NodeID("00112233445566778899aabbccddeeff00112233") - testcases := []struct { address p2p.NodeAddress expect p2p.Endpoint @@ -207,56 +183,42 @@ func TestNodeAddress_Resolve(t *testing.T) { }{ // Valid networked addresses (with hostname). { - p2p.NodeAddress{Protocol: "tcp", Hostname: "127.0.0.1", Port: 80, Path: "/path"}, - p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(tcp.IPv4Loopback(), 80), Path: "/path"}, + p2p.NodeAddress{Hostname: "127.0.0.1", Port: 80}, + p2p.Endpoint{netip.AddrPortFrom(tcp.IPv4Loopback(), 80)}, true, }, { - p2p.NodeAddress{Protocol: "tcp", Hostname: "127.0.0.1"}, - p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(tcp.IPv4Loopback(), 0)}, + p2p.NodeAddress{Hostname: "127.0.0.1"}, + p2p.Endpoint{netip.AddrPortFrom(tcp.IPv4Loopback(), 0)}, true, }, { - p2p.NodeAddress{Protocol: "tcp", Hostname: "::1"}, - p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(netip.IPv6Loopback(), 0)}, + p2p.NodeAddress{Hostname: "::1"}, + p2p.Endpoint{netip.AddrPortFrom(netip.IPv6Loopback(), 0)}, true, }, { - p2p.NodeAddress{Protocol: "tcp", Hostname: "8.8.8.8"}, - p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(netip.AddrFrom4([4]byte{8, 8, 8, 8}), 0)}, + p2p.NodeAddress{Hostname: "8.8.8.8"}, + p2p.Endpoint{netip.AddrPortFrom(netip.AddrFrom4([4]byte{8, 8, 8, 8}), 0)}, true, }, { - p2p.NodeAddress{Protocol: "tcp", Hostname: "2001:0db8::ff00:0042:8329"}, - p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(netip.AddrFrom16([16]byte{ + p2p.NodeAddress{Hostname: "2001:0db8::ff00:0042:8329"}, + p2p.Endpoint{netip.AddrPortFrom(netip.AddrFrom16([16]byte{ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29}), 0)}, true, }, { - p2p.NodeAddress{Protocol: "tcp", Hostname: "some.missing.host.tendermint.com"}, + p2p.NodeAddress{Hostname: "some.missing.host.tendermint.com"}, p2p.Endpoint{}, false, }, - // Valid non-networked addresses. - { - p2p.NodeAddress{Protocol: "memory", NodeID: id}, - p2p.Endpoint{Protocol: "memory", Path: string(id)}, - true, - }, - { - p2p.NodeAddress{Protocol: "memory", NodeID: id, Path: string(id)}, - p2p.Endpoint{Protocol: "memory", Path: string(id)}, - true, - }, - // Invalid addresses. {p2p.NodeAddress{}, p2p.Endpoint{}, false}, {p2p.NodeAddress{Hostname: "127.0.0.1"}, p2p.Endpoint{}, false}, - {p2p.NodeAddress{Protocol: "tcp", Hostname: "127.0.0.1:80"}, p2p.Endpoint{}, false}, - {p2p.NodeAddress{Protocol: "memory"}, p2p.Endpoint{}, false}, - {p2p.NodeAddress{Protocol: "memory", Path: string(id)}, p2p.Endpoint{}, false}, - {p2p.NodeAddress{Protocol: "tcp", Hostname: "💥"}, p2p.Endpoint{}, false}, + {p2p.NodeAddress{Hostname: "127.0.0.1:80"}, p2p.Endpoint{}, false}, + {p2p.NodeAddress{Hostname: "💥"}, p2p.Endpoint{}, false}, } for _, tc := range testcases { t.Run(tc.address.String(), func(t *testing.T) { @@ -266,9 +228,9 @@ func TestNodeAddress_Resolve(t *testing.T) { return } ok := false - tc.expect.Addr = tcp.Norm(tc.expect.Addr) + tc.expect.AddrPort = tcp.Norm(tc.expect.AddrPort) for _, e := range endpoints { - e.Addr = tcp.Norm(e.Addr) + e.AddrPort = tcp.Norm(e.AddrPort) ok = ok || e == tc.expect } if !ok { @@ -277,14 +239,14 @@ func TestNodeAddress_Resolve(t *testing.T) { }) } t.Run("Resolve localhost", func(t *testing.T) { - addr := p2p.NodeAddress{Protocol: "tcp", Hostname: "localhost", Port: 80, Path: "/path"} + addr := p2p.NodeAddress{Hostname: "localhost", Port: 80} endpoints, err := addr.Resolve(t.Context()) require.NoError(t, err) require.True(t, len(endpoints) > 0) for _, got := range endpoints { - require.True(t, got.Addr.Addr().IsLoopback()) + require.True(t, got.Addr().IsLoopback()) // Any loopback address is acceptable, so ignore it in comparison. - want := p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(got.Addr.Addr(), 80), Path: "/path"} + want := p2p.Endpoint{netip.AddrPortFrom(got.Addr(), 80)} require.Equal(t, want, got) } }) @@ -299,51 +261,30 @@ func TestNodeAddress_String(t *testing.T) { }{ // Valid networked addresses (with hostname). { - p2p.NodeAddress{Protocol: "tcp", NodeID: id, Hostname: "host", Port: 80, Path: "/path/sub?foo=bar&x=y#anchor"}, - "tcp://" + user + "@host:80/path/sub%3Ffoo=bar&x=y%23anchor", + p2p.NodeAddress{NodeID: id, Hostname: "host", Port: 80}, + "mconn://" + user + "@host:80", }, { - p2p.NodeAddress{Protocol: "tcp", NodeID: id, Hostname: "host.domain"}, - "tcp://" + user + "@host.domain", + p2p.NodeAddress{NodeID: id, Hostname: "host.domain"}, + "mconn://" + user + "@host.domain", }, { - p2p.NodeAddress{NodeID: id, Hostname: "host", Port: 80, Path: "foo/bar"}, - user + "@host:80/foo/bar", - }, - - // Valid non-networked addresses (without hostname). - { - p2p.NodeAddress{Protocol: "memory", NodeID: id, Path: string(id)}, - "memory:" + user, - }, - { - p2p.NodeAddress{Protocol: "memory", NodeID: id}, - "memory:" + user, + p2p.NodeAddress{NodeID: id, Hostname: "host", Port: 80}, + "mconn://" + user + "@host:80/foo/bar", }, // Addresses with weird contents, which are technically fine (not harmful). { - p2p.NodeAddress{Protocol: "💬", NodeID: "👨", Hostname: "💻", Port: 80, Path: "🛣"}, - "💬://%F0%9F%91%A8@%F0%9F%92%BB:80/%F0%9F%9B%A3", + p2p.NodeAddress{NodeID: "👨", Hostname: "💻", Port: 80}, + "%F0%9F%91%A8@%F0%9F%92%BB:80", }, // Partial (invalid) addresses. {p2p.NodeAddress{}, ""}, {p2p.NodeAddress{NodeID: id}, user + "@"}, - {p2p.NodeAddress{Protocol: "tcp"}, "tcp:"}, {p2p.NodeAddress{Hostname: "host"}, "host"}, {p2p.NodeAddress{Port: 80}, ""}, - {p2p.NodeAddress{Path: "path"}, "/path"}, {p2p.NodeAddress{NodeID: id, Port: 80}, user + "@"}, - {p2p.NodeAddress{Protocol: "tcp", Hostname: "host"}, "tcp://host"}, - { - p2p.NodeAddress{Protocol: "memory", NodeID: id, Path: "path"}, - "memory://00112233445566778899aabbccddeeff00112233@/path", - }, - { - p2p.NodeAddress{Protocol: "memory", NodeID: id, Port: 80}, - "memory:00112233445566778899aabbccddeeff00112233", - }, } for _, tc := range testcases { t.Run(tc.address.String(), func(t *testing.T) { @@ -359,17 +300,15 @@ func TestNodeAddress_Validate(t *testing.T) { ok bool }{ // Valid addresses. - {p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "host", Port: 80, Path: "/path"}, true}, - {p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "host"}, true}, - {p2p.NodeAddress{Protocol: "mconn", NodeID: id, Path: "path"}, true}, - {p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "👋", Path: "👋"}, true}, + {p2p.NodeAddress{NodeID: id, Hostname: "host", Port: 80}, true}, + {p2p.NodeAddress{NodeID: id, Hostname: "host"}, true}, + {p2p.NodeAddress{NodeID: id}, true}, // Invalid addresses. {p2p.NodeAddress{}, false}, {p2p.NodeAddress{NodeID: "foo", Hostname: "host"}, false}, - {p2p.NodeAddress{Protocol: "mconn", NodeID: id}, true}, - {p2p.NodeAddress{Protocol: "mconn", NodeID: "foo", Hostname: "host"}, false}, - {p2p.NodeAddress{Protocol: "mconn", NodeID: id, Port: 80, Path: "path"}, false}, + {p2p.NodeAddress{NodeID: id}, true}, + {p2p.NodeAddress{NodeID: id, Port: 80}, false}, } for _, tc := range testcases { t.Run(tc.address.String(), func(t *testing.T) { diff --git a/internal/p2p/p2ptest/network.go b/internal/p2p/p2ptest/network.go index a9dfd9bbe..5c11c6021 100644 --- a/internal/p2p/p2ptest/network.go +++ b/internal/p2p/p2ptest/network.go @@ -217,7 +217,7 @@ type Node struct { PrivKey crypto.PrivKey Router *p2p.Router PeerManager *p2p.PeerManager - Transport *p2p.MConnTransport + Transport *p2p.Transport cancel context.CancelFunc } @@ -238,7 +238,7 @@ func (n *Network) MakeNode(t *testing.T, opts NodeOptions) *Node { nodeInfo := types.NodeInfo{ NodeID: nodeID, - ListenAddr: transport.Endpoint().Addr.String(), + ListenAddr: transport.Endpoint().String(), Moniker: string(nodeID), Network: "test", } diff --git a/internal/p2p/peermanager_scoring_test.go b/internal/p2p/peermanager_scoring_test.go index 1b3048f6a..fb007bafe 100644 --- a/internal/p2p/peermanager_scoring_test.go +++ b/internal/p2p/peermanager_scoring_test.go @@ -26,7 +26,7 @@ func TestPeerScoring(t *testing.T) { // create a fake node id := types.NodeID(strings.Repeat("a1", 20)) - added, err := peerManager.Add(NodeAddress{NodeID: id, Protocol: "memory"}) + added, err := peerManager.Add(NodeAddress{NodeID: id}) require.NoError(t, err) require.True(t, added) @@ -55,7 +55,7 @@ func TestPeerScoring(t *testing.T) { } // Dial failure should decrease score - addr := NodeAddress{NodeID: id, Protocol: "memory"} + addr := NodeAddress{NodeID: id} _ = peerManager.DialFailed(ctx, addr) _ = peerManager.DialFailed(ctx, addr) _ = peerManager.DialFailed(ctx, addr) diff --git a/internal/p2p/peermanager_test.go b/internal/p2p/peermanager_test.go index 04df86f82..bfe04c00f 100644 --- a/internal/p2p/peermanager_test.go +++ b/internal/p2p/peermanager_test.go @@ -134,20 +134,17 @@ func TestNewPeerManager(t *testing.T) { func TestNewPeerManager_Persistence(t *testing.T) { aID := types.NodeID(strings.Repeat("a", 40)) aAddresses := []p2p.NodeAddress{ - {Protocol: "tcp", NodeID: aID, Hostname: "127.0.0.1", Port: 26657, Path: "/path"}, - {Protocol: "memory", NodeID: aID}, + {NodeID: aID, Hostname: "127.0.0.1", Port: 26657}, } bID := types.NodeID(strings.Repeat("b", 40)) bAddresses := []p2p.NodeAddress{ - {Protocol: "tcp", NodeID: bID, Hostname: "b10c::1", Port: 26657, Path: "/path"}, - {Protocol: "memory", NodeID: bID}, + {NodeID: bID, Hostname: "b10c::1", Port: 26657}, } cID := types.NodeID(strings.Repeat("c", 40)) cAddresses := []p2p.NodeAddress{ - {Protocol: "tcp", NodeID: cID, Hostname: "host.domain", Port: 80}, - {Protocol: "memory", NodeID: cID}, + {NodeID: cID, Hostname: "host.domain", Port: 80}, } // Create an initial peer manager and add the peers. @@ -205,20 +202,17 @@ func TestNewPeerManager_Persistence(t *testing.T) { func TestNewPeerManager_Unconditional(t *testing.T) { aID := types.NodeID(strings.Repeat("a", 40)) aAddresses := []p2p.NodeAddress{ - {Protocol: "tcp", NodeID: aID, Hostname: "127.0.0.1", Port: 26657, Path: "/path"}, - {Protocol: "memory", NodeID: aID}, + {NodeID: aID, Hostname: "127.0.0.1", Port: 26657}, } bID := types.NodeID(strings.Repeat("b", 40)) bAddresses := []p2p.NodeAddress{ - {Protocol: "tcp", NodeID: bID, Hostname: "b10c::1", Port: 26657, Path: "/path"}, - {Protocol: "memory", NodeID: bID}, + {NodeID: bID, Hostname: "b10c::1", Port: 26657}, } cID := types.NodeID(strings.Repeat("c", 40)) cAddresses := []p2p.NodeAddress{ - {Protocol: "tcp", NodeID: cID, Hostname: "host.domain", Port: 80}, - {Protocol: "memory", NodeID: cID}, + {NodeID: cID, Hostname: "host.domain", Port: 80}, } // Create an initial peer manager and add the peers. @@ -264,8 +258,8 @@ func TestNewPeerManager_Unconditional(t *testing.T) { } func TestNewPeerManager_SelfIDChange(t *testing.T) { - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} db := dbm.NewMemDB() peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, db, p2p.PeerManagerOptions{}, p2p.NopMetrics()) @@ -300,8 +294,7 @@ func TestPeerManager_Add(t *testing.T) { // Adding a couple of addresses should work. aAddresses := []p2p.NodeAddress{ - {Protocol: "tcp", NodeID: aID, Hostname: "localhost"}, - {Protocol: "memory", NodeID: aID}, + {NodeID: aID, Hostname: "localhost"}, } for _, addr := range aAddresses { added, err := peerManager.Add(addr) @@ -311,7 +304,7 @@ func TestPeerManager_Add(t *testing.T) { require.ElementsMatch(t, aAddresses, peerManager.Addresses(aID)) // Adding a different peer should be fine. - bAddress := p2p.NodeAddress{Protocol: "tcp", NodeID: bID, Hostname: "localhost"} + bAddress := p2p.NodeAddress{NodeID: bID, Hostname: "localhost"} added, err := peerManager.Add(bAddress) require.NoError(t, err) require.True(t, added) @@ -326,26 +319,20 @@ func TestPeerManager_Add(t *testing.T) { // Adding a third peer with MaxPeers=2 should cause bID, which is // the lowest-scored peer (not in PersistentPeers), to be removed. - added, err = peerManager.Add(p2p.NodeAddress{ - Protocol: "tcp", NodeID: cID, Hostname: "localhost"}) + added, err = peerManager.Add(p2p.NodeAddress{NodeID: cID, Hostname: "localhost"}) require.NoError(t, err) require.True(t, added) require.ElementsMatch(t, []types.NodeID{aID, cID}, peerManager.Peers()) // Adding an invalid address should error. - _, err = peerManager.Add(p2p.NodeAddress{Path: "foo"}) + _, err = peerManager.Add(p2p.NodeAddress{}) require.Error(t, err) - - // Adding self be fine - added, err = peerManager.Add(p2p.NodeAddress{Protocol: "memory", NodeID: selfID}) - require.Nil(t, err) - require.False(t, added) } func TestPeerManager_DialNext(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -370,7 +357,7 @@ func TestPeerManager_DialNext(t *testing.T) { func TestPeerManager_DialNext_Retry(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} options := p2p.PeerManagerOptions{ MinRetryTime: 100 * time.Millisecond, @@ -404,7 +391,7 @@ func TestPeerManager_DialNext_Retry(t *testing.T) { func TestPeerManagerDeleteOnMaxRetries(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} options := p2p.PeerManagerOptions{ MinRetryTime: 100 * time.Millisecond, @@ -450,8 +437,8 @@ func TestPeerManager_DialNext_WakeOnDialFailed(t *testing.T) { }, p2p.NopMetrics()) require.NoError(t, err) - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} // Add and dial a. added, err := peerManager.Add(a) @@ -493,7 +480,7 @@ func TestPeerManager_DialNext_WakeOnDialFailedRetry(t *testing.T) { peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), options, p2p.NopMetrics()) require.NoError(t, err) - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} // Add a, dial it, and mark it a failure. This will start a retry timer. added, err := peerManager.Add(a) @@ -518,7 +505,7 @@ func TestPeerManager_DialNext_WakeOnDialFailedRetry(t *testing.T) { func TestPeerManager_DialNext_WakeOnDisconnected(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -548,9 +535,9 @@ func TestPeerManager_DialNext_WakeOnDisconnected(t *testing.T) { } func TestPeerManager_TryDialNext_MaxConnected(t *testing.T) { - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("c", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 2, @@ -586,11 +573,11 @@ func TestPeerManager_TryDialNext_MaxConnected(t *testing.T) { func TestPeerManager_TryDialNext_MaxConnectedUpgrade(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("c", 40))} - d := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("d", 40))} - e := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("e", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} + d := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("d", 40))} + e := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("e", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ PeerScores: map[types.NodeID]p2p.PeerScore{ @@ -670,9 +657,9 @@ func TestPeerManager_TryDialNext_MaxConnectedUpgrade(t *testing.T) { } func TestPeerManager_TryDialNext_UpgradeReservesPeer(t *testing.T) { - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("c", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ PeerScores: map[types.NodeID]p2p.PeerScore{b.NodeID: p2p.DefaultMutableScore + 1, c.NodeID: p2p.DefaultMutableScore + 1}, @@ -710,11 +697,11 @@ func TestPeerManager_TryDialNext_UpgradeReservesPeer(t *testing.T) { func TestPeerManager_TryDialNext_DialingConnected(t *testing.T) { aID := types.NodeID(strings.Repeat("a", 40)) - a := p2p.NodeAddress{Protocol: "memory", NodeID: aID} - aTCP := p2p.NodeAddress{Protocol: "tcp", NodeID: aID, Hostname: "localhost"} + a := p2p.NodeAddress{NodeID: aID} + aTCP := p2p.NodeAddress{NodeID: aID, Hostname: "localhost"} bID := types.NodeID(strings.Repeat("b", 40)) - b := p2p.NodeAddress{Protocol: "memory", NodeID: bID} + b := p2p.NodeAddress{NodeID: bID} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 2, @@ -759,10 +746,10 @@ func TestPeerManager_TryDialNext_Multiple(t *testing.T) { aID := types.NodeID(strings.Repeat("a", 40)) bID := types.NodeID(strings.Repeat("b", 40)) addresses := []p2p.NodeAddress{ - {Protocol: "memory", NodeID: aID}, - {Protocol: "memory", NodeID: bID}, - {Protocol: "tcp", NodeID: aID, Hostname: "127.0.0.1"}, - {Protocol: "tcp", NodeID: bID, Hostname: "::1"}, + {NodeID: aID}, + {NodeID: bID}, + {NodeID: aID, Hostname: "127.0.0.1"}, + {NodeID: bID, Hostname: "::1"}, } peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) @@ -794,9 +781,9 @@ func TestPeerManager_DialFailed(t *testing.T) { // DialFailed is tested through other tests, we'll just check a few basic // things here, e.g. reporting unknown addresses. aID := types.NodeID(strings.Repeat("a", 40)) - a := p2p.NodeAddress{Protocol: "memory", NodeID: aID} + a := p2p.NodeAddress{NodeID: aID} bID := types.NodeID(strings.Repeat("b", 40)) - b := p2p.NodeAddress{Protocol: "memory", NodeID: bID} + b := p2p.NodeAddress{NodeID: bID} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -814,7 +801,7 @@ func TestPeerManager_DialFailed(t *testing.T) { require.NoError(t, err) require.Equal(t, a, dial) require.NoError(t, peerManager.DialFailed(ctx, p2p.NodeAddress{ - Protocol: "tcp", NodeID: aID, Hostname: "localhost"})) + NodeID: aID, Hostname: "localhost"})) require.Equal(t, []p2p.NodeAddress{a}, peerManager.Addresses(aID)) dial, err = peerManager.TryDialNext() @@ -833,9 +820,9 @@ func TestPeerManager_DialFailed(t *testing.T) { func TestPeerManager_DialFailed_UnreservePeer(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("c", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} logger, _ := log.NewDefaultLogger("plain", "debug") peerManager, err := p2p.NewPeerManager(logger, selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ @@ -880,8 +867,8 @@ func TestPeerManager_DialFailed_UnreservePeer(t *testing.T) { } func TestPeerManager_Dialed_Connected(t *testing.T) { - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -914,14 +901,14 @@ func TestPeerManager_Dialed_Self(t *testing.T) { require.NoError(t, err) // Dialing self should error. - added, err := peerManager.Add(p2p.NodeAddress{Protocol: "memory", NodeID: selfID}) + added, err := peerManager.Add(p2p.NodeAddress{NodeID: selfID}) require.Nil(t, err) require.False(t, added) } func TestPeerManager_Dialed_MaxConnected(t *testing.T) { - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 1, @@ -948,10 +935,10 @@ func TestPeerManager_Dialed_MaxConnected(t *testing.T) { } func TestPeerManager_Dialed_MaxConnectedUpgrade(t *testing.T) { - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("c", 40))} - d := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("d", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} + d := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("d", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 2, @@ -1000,7 +987,7 @@ func TestPeerManager_Dialed_MaxConnectedUpgrade(t *testing.T) { } func TestPeerManager_Dialed_Unknown(t *testing.T) { - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1010,9 +997,9 @@ func TestPeerManager_Dialed_Unknown(t *testing.T) { } func TestPeerManager_Dialed_Upgrade(t *testing.T) { - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("c", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 1, @@ -1056,10 +1043,10 @@ func TestPeerManager_Dialed_Upgrade(t *testing.T) { func TestPeerManager_Dialed_UpgradeEvenLower(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("c", 40))} - d := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("d", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} + d := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("d", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 2, @@ -1114,9 +1101,9 @@ func TestPeerManager_Dialed_UpgradeEvenLower(t *testing.T) { func TestPeerManager_Dialed_UpgradeNoEvict(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("c", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 2, @@ -1161,10 +1148,10 @@ func TestPeerManager_Dialed_UpgradeNoEvict(t *testing.T) { } func TestPeerManager_Accepted(t *testing.T) { - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("c", 40))} - d := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("d", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} + d := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("d", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1208,9 +1195,9 @@ func TestPeerManager_Accepted(t *testing.T) { } func TestPeerManager_Accepted_MaxConnected(t *testing.T) { - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("c", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 2, @@ -1236,10 +1223,10 @@ func TestPeerManager_Accepted_MaxConnected(t *testing.T) { } func TestPeerManager_Accepted_MaxConnectedUpgrade(t *testing.T) { - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("c", 40))} - d := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("d", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} + d := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("d", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ PeerScores: map[types.NodeID]p2p.PeerScore{ @@ -1284,9 +1271,9 @@ func TestPeerManager_Accepted_MaxConnectedUpgrade(t *testing.T) { func TestPeerManager_Accepted_Upgrade(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("c", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ PeerScores: map[types.NodeID]p2p.PeerScore{ @@ -1329,9 +1316,9 @@ func TestPeerManager_Accepted_Upgrade(t *testing.T) { } func TestPeerManager_Accepted_UpgradeDialing(t *testing.T) { - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("c", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ PeerScores: map[types.NodeID]p2p.PeerScore{ @@ -1378,8 +1365,8 @@ func TestPeerManager_Accepted_UpgradeDialing(t *testing.T) { } func TestPeerManager_Ready(t *testing.T) { - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} ctx := t.Context() @@ -1421,7 +1408,7 @@ func TestPeerManager_Ready_Channels(t *testing.T) { sub := pm.Subscribe(ctx) - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} added, err := pm.Add(a) require.NoError(t, err) require.True(t, added) @@ -1439,7 +1426,7 @@ func TestPeerManager_Ready_Channels(t *testing.T) { func TestPeerManager_EvictNext(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1474,7 +1461,7 @@ func TestPeerManager_EvictNext(t *testing.T) { func TestPeerManager_EvictNext_WakeOnError(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1502,8 +1489,8 @@ func TestPeerManager_EvictNext_WakeOnError(t *testing.T) { func TestPeerManager_EvictNext_WakeOnUpgradeDialed(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 1, @@ -1542,8 +1529,8 @@ func TestPeerManager_EvictNext_WakeOnUpgradeDialed(t *testing.T) { func TestPeerManager_EvictNext_WakeOnUpgradeAccepted(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 1, @@ -1575,7 +1562,7 @@ func TestPeerManager_EvictNext_WakeOnUpgradeAccepted(t *testing.T) { func TestPeerManager_TryEvictNext(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1613,7 +1600,7 @@ func TestPeerManager_TryEvictNext(t *testing.T) { } func TestPeerManager_Disconnected(t *testing.T) { - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1670,7 +1657,7 @@ func TestPeerManager_Disconnected(t *testing.T) { func TestPeerManager_Errored(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1710,7 +1697,7 @@ func TestPeerManager_Errored(t *testing.T) { func TestPeerManager_Subscribe(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1774,7 +1761,7 @@ func TestPeerManager_Subscribe(t *testing.T) { func TestPeerManager_Subscribe_Close(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1803,7 +1790,7 @@ func TestPeerManager_Subscribe_Broadcast(t *testing.T) { t.Cleanup(leaktest.Check(t)) - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1850,7 +1837,7 @@ func TestPeerManager_Close(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MinRetryTime: 10 * time.Second, @@ -1874,16 +1861,13 @@ func TestPeerManager_Close(t *testing.T) { func TestPeerManager_Advertise(t *testing.T) { aID := types.NodeID(strings.Repeat("a", 40)) - aTCP := p2p.NodeAddress{Protocol: "tcp", NodeID: aID, Hostname: "127.0.0.1", Port: 26657, Path: "/path"} - aMem := p2p.NodeAddress{Protocol: "memory", NodeID: aID} + aTCP := p2p.NodeAddress{NodeID: aID, Hostname: "127.0.0.1", Port: 26657} bID := types.NodeID(strings.Repeat("b", 40)) - bTCP := p2p.NodeAddress{Protocol: "tcp", NodeID: bID, Hostname: "b10c::1", Port: 26657, Path: "/path"} - bMem := p2p.NodeAddress{Protocol: "memory", NodeID: bID} + bTCP := p2p.NodeAddress{NodeID: bID, Hostname: "b10c::1", Port: 26657} cID := types.NodeID(strings.Repeat("c", 40)) - cTCP := p2p.NodeAddress{Protocol: "tcp", NodeID: cID, Hostname: "host.domain", Port: 80} - cMem := p2p.NodeAddress{Protocol: "memory", NodeID: cID} + cTCP := p2p.NodeAddress{NodeID: cID, Hostname: "host.domain", Port: 80} dID := types.NodeID(strings.Repeat("d", 40)) @@ -1896,30 +1880,21 @@ func TestPeerManager_Advertise(t *testing.T) { added, err := peerManager.Add(aTCP) require.NoError(t, err) require.True(t, added) - added, err = peerManager.Add(aMem) - require.NoError(t, err) - require.True(t, added) added, err = peerManager.Add(bTCP) require.NoError(t, err) require.True(t, added) - added, err = peerManager.Add(bMem) - require.NoError(t, err) - require.True(t, added) added, err = peerManager.Add(cTCP) require.NoError(t, err) require.True(t, added) - added, err = peerManager.Add(cMem) - require.NoError(t, err) - require.True(t, added) // d should get all addresses. require.ElementsMatch(t, []p2p.NodeAddress{ - aTCP, aMem, bTCP, bMem, cTCP, cMem, + aTCP, bTCP, cTCP, }, peerManager.Advertise(dID, 100)) // a should not get its own addresses. require.ElementsMatch(t, []p2p.NodeAddress{ - bTCP, bMem, cTCP, cMem, + bTCP, cTCP, }, peerManager.Advertise(aID, 100)) // Asking for 0 addresses should return, well, 0. @@ -1927,14 +1902,14 @@ func TestPeerManager_Advertise(t *testing.T) { // Asking for 2 addresses should get the highest-rated ones, i.e. a. require.ElementsMatch(t, []p2p.NodeAddress{ - aTCP, aMem, + aTCP, }, peerManager.Advertise(dID, 2)) } func TestPeerManager_Advertise_Self(t *testing.T) { dID := types.NodeID(strings.Repeat("d", 40)) - self := p2p.NodeAddress{Protocol: "tcp", NodeID: selfID, Hostname: "2001:db8::1", Port: 26657} + self := p2p.NodeAddress{NodeID: selfID, Hostname: "2001:db8::1", Port: 26657} // Create a peer manager with SelfAddress defined. peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ diff --git a/internal/p2p/pex/reactor_test.go b/internal/p2p/pex/reactor_test.go index 4088e9036..d730abb90 100644 --- a/internal/p2p/pex/reactor_test.go +++ b/internal/p2p/pex/reactor_test.go @@ -146,14 +146,14 @@ func TestReactorErrorsOnReceivingTooManyPeers(t *testing.T) { ctx := t.Context() r := setupSingle(ctx, t) - peer := p2p.NodeAddress{Protocol: p2p.MConnProtocol, NodeID: randomNodeID()} + peer := p2p.NodeAddress{NodeID: randomNodeID()} added, err := r.manager.Add(peer) require.NoError(t, err) require.True(t, added) addresses := make([]p2pproto.PexAddress, 101) for i := range addresses { - nodeAddress := p2p.NodeAddress{Protocol: p2p.MConnProtocol, NodeID: randomNodeID()} + nodeAddress := p2p.NodeAddress{NodeID: randomNodeID()} addresses[i] = p2pproto.PexAddress{ URL: nodeAddress.String(), } diff --git a/internal/p2p/router.go b/internal/p2p/router.go index 9929d8ca4..faa03b2e5 100644 --- a/internal/p2p/router.go +++ b/internal/p2p/router.go @@ -145,7 +145,7 @@ type Router struct { privKey crypto.PrivKey peerManager *PeerManager chDescs []*ChannelDescriptor - transport Transport + transport *Transport connTracker connectionTracker peerStates utils.RWMutex[map[types.NodeID]*peerState] @@ -177,7 +177,7 @@ func NewRouter( privKey crypto.PrivKey, peerManager *PeerManager, nodeInfoProducer func() *types.NodeInfo, - transport Transport, + transport *Transport, dynamicIDFilterer func(context.Context, types.NodeID) error, options RouterOptions, ) (*Router, error) { @@ -399,14 +399,14 @@ func (r *Router) dialSleep(ctx context.Context) error { // acceptPeers accepts inbound connections from peers on the given transport, // and spawns goroutines that route messages to/from them. -func (r *Router) acceptPeers(ctx context.Context, transport Transport) error { +func (r *Router) acceptPeers(ctx context.Context, transport *Transport) error { for { conn, err := transport.Accept(ctx) if err != nil { return fmt.Errorf("failed to accept connection: %w", err) } r.metrics.NewConnections.With("direction", "in").Add(1) - incomingAddr := conn.RemoteEndpoint().Addr + incomingAddr := conn.RemoteEndpoint().AddrPort if err := r.connTracker.AddConn(incomingAddr); err != nil { closeErr := conn.Close() r.logger.Error("rate limiting incoming peer", @@ -428,7 +428,7 @@ func (r *Router) acceptPeers(ctx context.Context, transport Transport) error { func (r *Router) openConnection(ctx context.Context, conn Connection) error { defer conn.Close() - incomingAddr := conn.RemoteEndpoint().Addr + incomingAddr := conn.RemoteEndpoint().AddrPort defer r.connTracker.RemoveConn(incomingAddr) if err := r.filterPeersIP(ctx, incomingAddr); err != nil { diff --git a/internal/p2p/transport.go b/internal/p2p/transport.go index e78dd9e65..adec098d9 100644 --- a/internal/p2p/transport.go +++ b/internal/p2p/transport.go @@ -4,54 +4,306 @@ import ( "context" "errors" "fmt" + "math" + "net" "net/netip" + "sync" + "sync/atomic" + + "golang.org/x/net/netutil" "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/internal/libs/protoio" + "github.com/tendermint/tendermint/internal/p2p/conn" + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/libs/utils" + "github.com/tendermint/tendermint/libs/utils/scope" + "github.com/tendermint/tendermint/libs/utils/tcp" + p2pproto "github.com/tendermint/tendermint/proto/tendermint/p2p" "github.com/tendermint/tendermint/types" ) -//go:generate ../../scripts/mockery_generate.sh Transport|Connection +//go:generate ../../scripts/mockery_generate.sh Connection -const ( - // defaultProtocol is the default protocol used for NodeAddress when - // a protocol isn't explicitly given as a URL scheme. - defaultProtocol Protocol = MConnProtocol -) +// TransportOptions sets options for Transport. +type TransportOptions struct { + // MaxAcceptedConnections is the maximum number of simultaneous accepted + // (incoming) connections. Beyond this, new connections will block until + // a slot is free. 0 means unlimited. + // + // FIXME: We may want to replace this with connection accounting in the + // Router, since it will need to do e.g. rate limiting and such as well. + // But it might also make sense to have per-transport limits. + MaxAcceptedConnections uint32 +} -// Protocol identifies a transport protocol. -type Protocol string +// TestTransport creates a new Transport for tests. +func TestTransport(logger log.Logger, nodeID types.NodeID) *Transport { + return NewTransport( + logger.With("local", nodeID), + Endpoint{tcp.TestReserveAddr()}, + conn.DefaultMConnConfig(), + []*ChannelDescriptor{}, + TransportOptions{}, + ) +} -// Transport is a connection-oriented mechanism for exchanging data with a peer. -type Transport interface { - // Run executes the background tasks of transport. - Run(ctx context.Context) error - // Protocols returns the protocols supported by the transport. The Router - // uses this to pick a transport for an Endpoint. - Protocols() []Protocol +// Transport is a Transport implementation using the current multiplexed +// Tendermint protocol ("MConn"). +type Transport struct { + logger log.Logger + endpoint Endpoint + options TransportOptions + mConnConfig conn.MConnConfig + channelDescs []*ChannelDescriptor + started chan struct{} + listener chan *mConnConnection +} - // Endpoints returns the local endpoints the transport is listening on. - Endpoint() Endpoint +// NewTransport sets up a new MConnection transport. This uses the +// proprietary Tendermint MConnection protocol, which is implemented as +// conn.MConnection. +func NewTransport( + logger log.Logger, + endpoint Endpoint, + mConnConfig conn.MConnConfig, + channelDescs []*ChannelDescriptor, + options TransportOptions, +) *Transport { + return &Transport{ + logger: logger, + endpoint: endpoint, + options: options, + mConnConfig: mConnConfig, + channelDescs: channelDescs, + // This is rendezvous channel, so that no unclosed connections get stuck inside + // when transport is closing. + started: make(chan struct{}), + listener: make(chan *mConnConnection), + } +} - // Accept waits for the next inbound connection on a listening endpoint, blocking - // until either a connection is available or the transport is closed. On closure, - // io.EOF is returned and further Accept calls are futile. - Accept(context.Context) (Connection, error) +// WaitForStart waits until transport starts listening for incoming connections. +func (m *Transport) WaitForStart(ctx context.Context) error { + _, _, err := utils.RecvOrClosed(ctx, m.started) + return err +} - // Dial creates an outbound connection to an endpoint. - Dial(context.Context, Endpoint) (Connection, error) +func (m *Transport) Endpoint() Endpoint { + return m.endpoint +} - // AddChannelDescriptors is only part of this interface - // temporarily - AddChannelDescriptors([]*ChannelDescriptor) +func (m *Transport) Run(ctx context.Context) error { + if err := m.endpoint.Validate(); err != nil { + return err + } + listener, err := tcp.Listen(m.endpoint.AddrPort) + if err != nil { + return fmt.Errorf("net.Listen(): %w", err) + } + close(m.started) // signal that we are listening + if m.options.MaxAcceptedConnections > 0 { + // FIXME: This will establish the inbound connection but simply hang it + // until another connection is released. It would probably be better to + // return an error to the remote peer or close the connection. This is + // also a DoS vector since the connection will take up kernel resources. + // This was just carried over from the legacy P2P stack. + listener = netutil.LimitListener(listener, int(m.options.MaxAcceptedConnections)) + } + return scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { + s.Spawn(func() error { + <-ctx.Done() + listener.Close() + return nil + }) + for { + conn, err := listener.Accept() + if err != nil { + if errors.Is(err, net.ErrClosed) { + return nil + } + return err + } + mconn := newMConnConnection(m.logger, conn, m.mConnConfig, m.channelDescs) + if err := utils.Send(ctx, m.listener, mconn); err != nil { + mconn.Close() + return err + } + } + }) +} - // Stringer is used to display the transport, e.g. in logs. - // - // Without this, the logger may use reflection to access and display - // internal fields. These can be written to concurrently, which can trigger - // the race detector or even cause a panic. - fmt.Stringer +// String implements Transport. +func (m *Transport) String() string { + return "transport" +} + +// Accept implements Transport. +func (m *Transport) Accept(ctx context.Context) (Connection, error) { + return utils.Recv(ctx, m.listener) +} + +// Dial implements Transport. +func (m *Transport) Dial(ctx context.Context, endpoint Endpoint) (Connection, error) { + if err := endpoint.Validate(); err != nil { + return nil, err + } + if endpoint.Port() == 0 { + endpoint.AddrPort = netip.AddrPortFrom(endpoint.Addr(), 26657) + } + dialer := net.Dialer{} + tcpConn, err := dialer.DialContext(ctx, "tcp", endpoint.String()) + if err != nil { + return nil, fmt.Errorf("dialer.DialContext(%v): %w", endpoint, err) + } + return newMConnConnection(m.logger, tcpConn, m.mConnConfig, m.channelDescs), nil +} + +// SetChannels sets the channel descriptors to be used when +// establishing a connection. +// +// FIXME: To be removed when the legacy p2p stack is removed. Channel +// descriptors should be managed by the router. The underlying transport and +// connections should be agnostic to everything but the channel ID's which are +// initialized in the handshake. +func (m *Transport) AddChannelDescriptors(channelDesc []*ChannelDescriptor) { + m.channelDescs = append(m.channelDescs, channelDesc...) +} + +type InvalidEndpointErr struct{ error } + +// mConnConnection implements Connection for Transport. +type mConnConnection struct { + logger log.Logger + conn net.Conn + mConnConfig conn.MConnConfig + channelDescs []*ChannelDescriptor + errorCh chan error + doneCh chan struct{} + closeOnce sync.Once + + mconn *conn.MConnection // set during Handshake() +} + +// newMConnConnection creates a new mConnConnection. +func newMConnConnection( + logger log.Logger, + conn net.Conn, + mConnConfig conn.MConnConfig, + channelDescs []*ChannelDescriptor, +) *mConnConnection { + return &mConnConnection{ + logger: logger, + conn: conn, + mConnConfig: mConnConfig, + channelDescs: channelDescs, + errorCh: make(chan error, 1), // buffered to avoid onError leak + doneCh: make(chan struct{}), + } } +// Handshake implements Connection. +func (c *mConnConnection) Handshake( + ctx context.Context, + nodeInfo types.NodeInfo, + privKey crypto.PrivKey, +) (types.NodeInfo, error) { + if c.mconn != nil { + return types.NodeInfo{}, errors.New("connection is already handshaked") + } + var peerInfo types.NodeInfo + var secretConn *conn.SecretConnection + var ok atomic.Bool + err := scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { + s.SpawnBg(func() error { + <-ctx.Done() + // Close the connection if handshake did not complete. + if !ok.Load() { + c.conn.Close() + } + return nil + }) + var err error + secretConn, err = conn.MakeSecretConnection(c.conn, privKey) + if err != nil { + return err + } + s.Spawn(func() error { + _, err := protoio.NewDelimitedWriter(secretConn).WriteMsg(nodeInfo.ToProto()) + return err + }) + var pbPeerInfo p2pproto.NodeInfo + if _, err := protoio.NewDelimitedReader(secretConn, types.MaxNodeInfoSize()).ReadMsg(&pbPeerInfo); err != nil { + return err + } + peerInfo, err = types.NodeInfoFromProto(&pbPeerInfo) + if err != nil { + return fmt.Errorf("error reading NodeInfo: %w", err) + } + // Authenticate the peer first. + peerID := types.NodeIDFromPubKey(secretConn.RemotePubKey()) + if peerID != peerInfo.NodeID { + return fmt.Errorf("peer's public key did not match its node ID %q (expected %q)", + peerInfo.NodeID, peerID) + } + if err := peerInfo.Validate(); err != nil { + return fmt.Errorf("invalid handshake NodeInfo: %w", err) + } + ok.Store(true) + return nil + }) + if err != nil { + return types.NodeInfo{}, err + } + // mconn takes ownership of conn. + c.mconn = conn.SpawnMConnection( + c.logger.With("peer", c.RemoteEndpoint().NodeAddress(peerInfo.NodeID)), + secretConn, + c.channelDescs, + c.mConnConfig, + ) + return peerInfo, nil +} + +// String displays connection information. +func (c *mConnConnection) String() string { + return c.RemoteEndpoint().String() +} + +// SendMessage implements Connection. +func (c *mConnConnection) SendMessage(ctx context.Context, chID ChannelID, msg []byte) error { + if chID > math.MaxUint8 { + return fmt.Errorf("MConnection only supports 1-byte channel IDs (got %v)", chID) + } + return c.mconn.Send(ctx, chID, msg) +} + +// ReceiveMessage implements Connection. +func (c *mConnConnection) ReceiveMessage(ctx context.Context) (ChannelID, []byte, error) { + return c.mconn.Recv(ctx) +} + +// LocalEndpoint implements Connection. +func (c *mConnConnection) LocalEndpoint() Endpoint { + return Endpoint{c.conn.LocalAddr().(*net.TCPAddr).AddrPort()} +} + +// RemoteEndpoint implements Connection. +func (c *mConnConnection) RemoteEndpoint() Endpoint { + return Endpoint{c.conn.RemoteAddr().(*net.TCPAddr).AddrPort()} +} + +// Close implements Connection. +func (c *mConnConnection) Close() error { + if c.mconn == nil { + return c.conn.Close() + } + return c.mconn.Close() +} + +// Protocol identifies a transport protocol. +type Protocol string + // Connection represents an established connection between two endpoints. // // FIXME: This is a temporary interface for backwards-compatibility with the @@ -101,72 +353,28 @@ type Connection interface { } // Endpoint represents a transport connection endpoint, either local or remote. -// -// Endpoints are not necessarily networked (see e.g. MemoryTransport) but all -// networked endpoints must use IP as the underlying transport protocol to allow -// e.g. IP address filtering. Either IP or Path (or both) must be set. -type Endpoint struct { - // Protocol specifies the transport protocol. - Protocol Protocol - // TCP endpoint address. - Addr netip.AddrPort - - // Path is an optional transport-specific path or identifier. - Path string -} +// It is a TCP endpoint address. +type Endpoint struct { netip.AddrPort } // NewEndpoint constructs an Endpoint from a types.NetAddress structure. func NewEndpoint(addr string) (Endpoint, error) { - addrPort, err := types.ParseAddressString(addr) - if err != nil { - return Endpoint{}, err - } - - return Endpoint{ - Protocol: MConnProtocol, - Addr: addrPort, - }, nil + e,err := types.ParseAddressString(addr) + return Endpoint{e}, err } // NodeAddress converts the endpoint into a NodeAddress for the given node ID. func (e Endpoint) NodeAddress(nodeID types.NodeID) NodeAddress { - address := NodeAddress{ + return NodeAddress{ NodeID: nodeID, - Protocol: e.Protocol, - Path: e.Path, - } - if e.Addr != (netip.AddrPort{}) { - address.Hostname = e.Addr.Addr().String() - address.Port = e.Addr.Port() - } - return address -} - -// String formats the endpoint as a URL string. -func (e Endpoint) String() string { - // If this is a non-networked endpoint with a valid node ID as a path, - // assume that path is a node ID (to handle opaque URLs of the form - // scheme:id). - if e.Addr == (netip.AddrPort{}) { - if nodeID, err := types.NewNodeID(e.Path); err == nil { - return e.NodeAddress(nodeID).String() - } + Hostname: e.Addr().String(), + Port: e.Port(), } - return e.NodeAddress("").String() } // Validate validates the endpoint. func (e Endpoint) Validate() error { - if e.Protocol == "" { - return errors.New("endpoint has no protocol") - } - if (e.Addr == netip.AddrPort{}) && (e.Path == "") { - return errors.New("endpoint has neither path nor IP") - } - if e.Addr != (netip.AddrPort{}) { - if !e.Addr.IsValid() { - return fmt.Errorf("endpoint has invalid address %q", e.Addr.String()) - } + if !e.IsValid() { + return InvalidEndpointErr{fmt.Errorf("endpoint has invalid address %q", e.String())} } return nil } diff --git a/internal/p2p/transport_mconn.go b/internal/p2p/transport_mconn.go deleted file mode 100644 index 1798f5ddd..000000000 --- a/internal/p2p/transport_mconn.go +++ /dev/null @@ -1,342 +0,0 @@ -package p2p - -import ( - "context" - "errors" - "fmt" - "math" - "net" - "net/netip" - "sync" - "sync/atomic" - - "golang.org/x/net/netutil" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/internal/libs/protoio" - "github.com/tendermint/tendermint/internal/p2p/conn" - "github.com/tendermint/tendermint/libs/log" - "github.com/tendermint/tendermint/libs/utils" - "github.com/tendermint/tendermint/libs/utils/scope" - "github.com/tendermint/tendermint/libs/utils/tcp" - p2pproto "github.com/tendermint/tendermint/proto/tendermint/p2p" - "github.com/tendermint/tendermint/types" -) - -const ( - MConnProtocol Protocol = "mconn" - TCPProtocol Protocol = "tcp" -) - -// MConnTransportOptions sets options for MConnTransport. -type MConnTransportOptions struct { - // MaxAcceptedConnections is the maximum number of simultaneous accepted - // (incoming) connections. Beyond this, new connections will block until - // a slot is free. 0 means unlimited. - // - // FIXME: We may want to replace this with connection accounting in the - // Router, since it will need to do e.g. rate limiting and such as well. - // But it might also make sense to have per-transport limits. - MaxAcceptedConnections uint32 -} - -// TestTransport creates a new Transport for tests. -func TestTransport(logger log.Logger, nodeID types.NodeID) *MConnTransport { - return NewMConnTransport( - logger.With("local", nodeID), - Endpoint{ - Protocol: MConnProtocol, - Addr: tcp.TestReserveAddr(), - }, - conn.DefaultMConnConfig(), - []*ChannelDescriptor{}, - MConnTransportOptions{}, - ) -} - -// MConnTransport is a Transport implementation using the current multiplexed -// Tendermint protocol ("MConn"). -type MConnTransport struct { - logger log.Logger - endpoint Endpoint - options MConnTransportOptions - mConnConfig conn.MConnConfig - channelDescs []*ChannelDescriptor - started chan struct{} - listener chan *mConnConnection -} - -// NewMConnTransport sets up a new MConnection transport. This uses the -// proprietary Tendermint MConnection protocol, which is implemented as -// conn.MConnection. -func NewMConnTransport( - logger log.Logger, - endpoint Endpoint, - mConnConfig conn.MConnConfig, - channelDescs []*ChannelDescriptor, - options MConnTransportOptions, -) *MConnTransport { - return &MConnTransport{ - logger: logger, - endpoint: endpoint, - options: options, - mConnConfig: mConnConfig, - channelDescs: channelDescs, - // This is rendezvous channel, so that no unclosed connections get stuck inside - // when transport is closing. - started: make(chan struct{}), - listener: make(chan *mConnConnection), - } -} - -// WaitForStart waits until transport starts listening for incoming connections. -func (m *MConnTransport) WaitForStart(ctx context.Context) error { - _, _, err := utils.RecvOrClosed(ctx, m.started) - return err -} - -func (m *MConnTransport) Endpoint() Endpoint { - return m.endpoint -} - -func (m *MConnTransport) Run(ctx context.Context) error { - if err := m.validateEndpoint(m.endpoint); err != nil { - return err - } - listener, err := tcp.Listen(m.endpoint.Addr) - if err != nil { - return fmt.Errorf("net.Listen(): %w", err) - } - close(m.started) // signal that we are listening - if m.options.MaxAcceptedConnections > 0 { - // FIXME: This will establish the inbound connection but simply hang it - // until another connection is released. It would probably be better to - // return an error to the remote peer or close the connection. This is - // also a DoS vector since the connection will take up kernel resources. - // This was just carried over from the legacy P2P stack. - listener = netutil.LimitListener(listener, int(m.options.MaxAcceptedConnections)) - } - return scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { - s.Spawn(func() error { - <-ctx.Done() - listener.Close() - return nil - }) - for { - conn, err := listener.Accept() - if err != nil { - if errors.Is(err, net.ErrClosed) { - return nil - } - return err - } - mconn := newMConnConnection(m.logger, conn, m.mConnConfig, m.channelDescs) - if err := utils.Send(ctx, m.listener, mconn); err != nil { - mconn.Close() - return err - } - } - }) -} - -// String implements Transport. -func (m *MConnTransport) String() string { - return string(MConnProtocol) -} - -// Protocols implements Transport. We support tcp for backwards-compatibility. -func (m *MConnTransport) Protocols() []Protocol { - return []Protocol{MConnProtocol, TCPProtocol} -} - -// Accept implements Transport. -func (m *MConnTransport) Accept(ctx context.Context) (Connection, error) { - return utils.Recv(ctx, m.listener) -} - -// Dial implements Transport. -func (m *MConnTransport) Dial(ctx context.Context, endpoint Endpoint) (Connection, error) { - if err := m.validateEndpoint(endpoint); err != nil { - return nil, err - } - if endpoint.Addr.Port() == 0 { - endpoint.Addr = netip.AddrPortFrom(endpoint.Addr.Addr(), 26657) - } - dialer := net.Dialer{} - tcpConn, err := dialer.DialContext(ctx, "tcp", endpoint.Addr.String()) - if err != nil { - return nil, fmt.Errorf("dialer.DialContext(%v): %w", endpoint.Addr, err) - } - return newMConnConnection(m.logger, tcpConn, m.mConnConfig, m.channelDescs), nil -} - -// SetChannels sets the channel descriptors to be used when -// establishing a connection. -// -// FIXME: To be removed when the legacy p2p stack is removed. Channel -// descriptors should be managed by the router. The underlying transport and -// connections should be agnostic to everything but the channel ID's which are -// initialized in the handshake. -func (m *MConnTransport) AddChannelDescriptors(channelDesc []*ChannelDescriptor) { - m.channelDescs = append(m.channelDescs, channelDesc...) -} - -type InvalidEndpointErr struct{ error } - -// validateEndpoint validates an endpoint. -func (m *MConnTransport) validateEndpoint(endpoint Endpoint) error { - if err := endpoint.Validate(); err != nil { - return InvalidEndpointErr{err} - } - if endpoint.Protocol != MConnProtocol && endpoint.Protocol != TCPProtocol { - return InvalidEndpointErr{fmt.Errorf("unsupported protocol %q", endpoint.Protocol)} - } - if !endpoint.Addr.IsValid() { - return InvalidEndpointErr{errors.New("endpoint has invalid address")} - } - if endpoint.Path != "" { - return InvalidEndpointErr{fmt.Errorf("endpoints with path not supported (got %q)", endpoint.Path)} - } - return nil -} - -// mConnConnection implements Connection for MConnTransport. -type mConnConnection struct { - logger log.Logger - conn net.Conn - mConnConfig conn.MConnConfig - channelDescs []*ChannelDescriptor - errorCh chan error - doneCh chan struct{} - closeOnce sync.Once - - mconn *conn.MConnection // set during Handshake() -} - -// newMConnConnection creates a new mConnConnection. -func newMConnConnection( - logger log.Logger, - conn net.Conn, - mConnConfig conn.MConnConfig, - channelDescs []*ChannelDescriptor, -) *mConnConnection { - return &mConnConnection{ - logger: logger, - conn: conn, - mConnConfig: mConnConfig, - channelDescs: channelDescs, - errorCh: make(chan error, 1), // buffered to avoid onError leak - doneCh: make(chan struct{}), - } -} - -// Handshake implements Connection. -func (c *mConnConnection) Handshake( - ctx context.Context, - nodeInfo types.NodeInfo, - privKey crypto.PrivKey, -) (types.NodeInfo, error) { - if c.mconn != nil { - return types.NodeInfo{}, errors.New("connection is already handshaked") - } - var peerInfo types.NodeInfo - var secretConn *conn.SecretConnection - var ok atomic.Bool - err := scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { - s.SpawnBg(func() error { - <-ctx.Done() - // Close the connection if handshake did not complete. - if !ok.Load() { - c.conn.Close() - } - return nil - }) - var err error - secretConn, err = conn.MakeSecretConnection(c.conn, privKey) - if err != nil { - return err - } - s.Spawn(func() error { - _, err := protoio.NewDelimitedWriter(secretConn).WriteMsg(nodeInfo.ToProto()) - return err - }) - var pbPeerInfo p2pproto.NodeInfo - if _, err := protoio.NewDelimitedReader(secretConn, types.MaxNodeInfoSize()).ReadMsg(&pbPeerInfo); err != nil { - return err - } - peerInfo, err = types.NodeInfoFromProto(&pbPeerInfo) - if err != nil { - return fmt.Errorf("error reading NodeInfo: %w", err) - } - // Authenticate the peer first. - peerID := types.NodeIDFromPubKey(secretConn.RemotePubKey()) - if peerID != peerInfo.NodeID { - return fmt.Errorf("peer's public key did not match its node ID %q (expected %q)", - peerInfo.NodeID, peerID) - } - if err := peerInfo.Validate(); err != nil { - return fmt.Errorf("invalid handshake NodeInfo: %w", err) - } - ok.Store(true) - return nil - }) - if err != nil { - return types.NodeInfo{}, err - } - // mconn takes ownership of conn. - c.mconn = conn.SpawnMConnection( - c.logger.With("peer", c.RemoteEndpoint().NodeAddress(peerInfo.NodeID)), - secretConn, - c.channelDescs, - c.mConnConfig, - ) - return peerInfo, nil -} - -// String displays connection information. -func (c *mConnConnection) String() string { - return c.RemoteEndpoint().String() -} - -// SendMessage implements Connection. -func (c *mConnConnection) SendMessage(ctx context.Context, chID ChannelID, msg []byte) error { - if chID > math.MaxUint8 { - return fmt.Errorf("MConnection only supports 1-byte channel IDs (got %v)", chID) - } - return c.mconn.Send(ctx, chID, msg) -} - -// ReceiveMessage implements Connection. -func (c *mConnConnection) ReceiveMessage(ctx context.Context) (ChannelID, []byte, error) { - return c.mconn.Recv(ctx) -} - -// LocalEndpoint implements Connection. -func (c *mConnConnection) LocalEndpoint() Endpoint { - endpoint := Endpoint{ - Protocol: MConnProtocol, - } - if addr, ok := c.conn.LocalAddr().(*net.TCPAddr); ok { - endpoint.Addr = addr.AddrPort() - } - return endpoint -} - -// RemoteEndpoint implements Connection. -func (c *mConnConnection) RemoteEndpoint() Endpoint { - endpoint := Endpoint{ - Protocol: MConnProtocol, - } - if addr, ok := c.conn.RemoteAddr().(*net.TCPAddr); ok { - endpoint.Addr = addr.AddrPort() - } - return endpoint -} - -// Close implements Connection. -func (c *mConnConnection) Close() error { - if c.mconn == nil { - return c.conn.Close() - } - return c.mconn.Close() -} diff --git a/internal/p2p/transport_mconn_test.go b/internal/p2p/transport_mconn_test.go index b22c38e9b..57087a4c9 100644 --- a/internal/p2p/transport_mconn_test.go +++ b/internal/p2p/transport_mconn_test.go @@ -39,18 +39,15 @@ func makeKeyAndInfo() (crypto.PrivKey, types.NodeInfo) { // Transports are mainly tested by common tests in transport_test.go, we // register a transport factory here to get included in those tests. func init() { - testTransports["mconn"] = func() func(context.Context) p2p.Transport { + testTransports["mconn"] = func() func(context.Context) *p2p.Transport { return func(ctx context.Context) p2p.Transport { logger, _ := log.NewDefaultLogger("plain", "info") - transport := p2p.NewMConnTransport( + transport := p2p.NewTransport( logger, - p2p.Endpoint{ - Protocol: p2p.MConnProtocol, - Addr: tcp.TestReserveAddr(), - }, + p2p.Endpoint{tcp.TestReserveAddr()}, conn.DefaultMConnConfig(), []*p2p.ChannelDescriptor{{ID: chID, Priority: 1}}, - p2p.MConnTransportOptions{}, + p2p.TransportOptions{}, ) go func() { if err := transport.Run(ctx); err != nil { @@ -67,7 +64,7 @@ func init() { // Establishes a connection to the transport. // Returns both ends of the connection. -func connect(ctx context.Context, tr *p2p.MConnTransport) (c1 p2p.Connection, c2 p2p.Connection, err error) { +func connect(ctx context.Context, tr *p2p.Transport) (c1 p2p.Connection, c2 p2p.Connection, err error) { defer func() { if err != nil { if c1 != nil { @@ -78,7 +75,7 @@ func connect(ctx context.Context, tr *p2p.MConnTransport) (c1 p2p.Connection, c2 } } }() - // Here we are utilizing the fact that MConnTransport accepts connection proactively + // Here we are utilizing the fact that Transport accepts connection proactively // before Accept is called. c1, err = tr.Dial(ctx, tr.Endpoint()) if err != nil { @@ -97,17 +94,14 @@ func connect(ctx context.Context, tr *p2p.MConnTransport) (c1 p2p.Connection, c2 return c1, c2, nil } -func TestMConnTransport_AcceptMaxAcceptedConnections(t *testing.T) { +func TestTransport_AcceptMaxAcceptedConnections(t *testing.T) { ctx := t.Context() - transport := p2p.NewMConnTransport( + transport := p2p.NewTransport( log.NewNopLogger(), - p2p.Endpoint{ - Protocol: p2p.MConnProtocol, - Addr: tcp.TestReserveAddr(), - }, + p2p.Endpoint{tcp.TestReserveAddr()}, conn.DefaultMConnConfig(), []*p2p.ChannelDescriptor{{ID: chID, Priority: 1}}, - p2p.MConnTransportOptions{ + p2p.TransportOptions{ MaxAcceptedConnections: 2, }, ) @@ -164,7 +158,7 @@ func TestMConnTransport_AcceptMaxAcceptedConnections(t *testing.T) { } } -func TestMConnTransport_Listen(t *testing.T) { +func TestTransport_Listen(t *testing.T) { reservePort := func(ip netip.Addr) netip.AddrPort { addr := tcp.TestReserveAddr() return netip.AddrPortFrom(ip, addr.Port()) @@ -175,16 +169,13 @@ func TestMConnTransport_Listen(t *testing.T) { ok bool }{ // Valid v4 and v6 addresses, with mconn and tcp protocols. - {p2p.Endpoint{Protocol: p2p.MConnProtocol, Addr: reservePort(netip.IPv4Unspecified())}, true}, - {p2p.Endpoint{Protocol: p2p.MConnProtocol, Addr: reservePort(tcp.IPv4Loopback())}, true}, - {p2p.Endpoint{Protocol: p2p.MConnProtocol, Addr: reservePort(netip.IPv6Unspecified())}, true}, - {p2p.Endpoint{Protocol: p2p.MConnProtocol, Addr: reservePort(netip.IPv6Loopback())}, true}, - {p2p.Endpoint{Protocol: p2p.TCPProtocol, Addr: reservePort(netip.IPv4Unspecified())}, true}, + {p2p.Endpoint{reservePort(netip.IPv4Unspecified())}, true}, + {p2p.Endpoint{reservePort(tcp.IPv4Loopback())}, true}, + {p2p.Endpoint{reservePort(netip.IPv6Unspecified())}, true}, + {p2p.Endpoint{reservePort(netip.IPv6Loopback())}, true}, // Invalid endpoints. {p2p.Endpoint{}, false}, - {p2p.Endpoint{Protocol: p2p.MConnProtocol, Path: "foo"}, false}, - {p2p.Endpoint{Protocol: p2p.MConnProtocol, Addr: reservePort(netip.IPv4Unspecified()), Path: "foo"}, false}, } aKey, aInfo := makeKeyAndInfo() @@ -194,12 +185,12 @@ func TestMConnTransport_Listen(t *testing.T) { ctx := t.Context() t.Cleanup(leaktest.Check(t)) - transport := p2p.NewMConnTransport( + transport := p2p.NewTransport( log.NewNopLogger(), tc.endpoint, conn.DefaultMConnConfig(), []*p2p.ChannelDescriptor{{ID: chID, Priority: 1}}, - p2p.MConnTransportOptions{}, + p2p.TransportOptions{}, ) if got, want := transport.Endpoint(), tc.endpoint; got != want { t.Fatalf("transport.Endpoint() = %v, want %v", got, want) diff --git a/internal/p2p/transport_memory.go b/internal/p2p/transport_memory.go deleted file mode 100644 index b73293d10..000000000 --- a/internal/p2p/transport_memory.go +++ /dev/null @@ -1,3 +0,0 @@ -package p2p - - diff --git a/internal/p2p/transport_test.go b/internal/p2p/transport_test.go index 04f574723..da05f8595 100644 --- a/internal/p2p/transport_test.go +++ b/internal/p2p/transport_test.go @@ -18,7 +18,7 @@ import ( ) // transportFactory is used to set up transports for tests. -type transportFactory = func(ctx context.Context) p2p.Transport +type transportFactory = func(ctx context.Context) *p2p.Transport // testTransports is a registry of transport factories for withTransports(). var testTransports = map[string](func() transportFactory){} @@ -72,23 +72,11 @@ func TestTransport_DialEndpoints(t *testing.T) { _, err = a.Dial(ctx, p2p.Endpoint{}) require.Error(t, err) - // Dialing without protocol should error. - noProtocol := endpoint - noProtocol.Protocol = "" - _, err = a.Dial(ctx, noProtocol) - require.Error(t, err) - - // Dialing with invalid protocol should error. - fooProtocol := endpoint - fooProtocol.Protocol = "foo" - _, err = a.Dial(ctx, fooProtocol) - require.Error(t, err) - // Tests for networked endpoints (with IP). for _, tc := range ipTestCases { t.Run(tc.ip.String(), func(t *testing.T) { e := endpoint - e.Addr = netip.AddrPortFrom(tc.ip, endpoint.Addr.Port()) + e.AddrPort = netip.AddrPortFrom(tc.ip, endpoint.Port()) conn, err := a.Dial(ctx, e) if tc.ok { require.NoError(t, err) @@ -98,13 +86,6 @@ func TestTransport_DialEndpoints(t *testing.T) { } }) } - - // Non-networked endpoints should error. - noIP := endpoint - noIP.Addr = netip.AddrPort{} - noIP.Path = "foo" - _, err = a.Dial(ctx, noIP) - require.Error(t, err) }) } @@ -125,17 +106,6 @@ func TestTransport_Endpoints(t *testing.T) { }) } -func TestTransport_Protocols(t *testing.T) { - withTransports(t, func(t *testing.T, makeTransport transportFactory) { - ctx := t.Context() - a := makeTransport(ctx) - protocols := a.Protocols() - endpoint := a.Endpoint() - require.NotEmpty(t, protocols) - require.Contains(t, protocols, endpoint.Protocol) - }) -} - func TestTransport_String(t *testing.T) { withTransports(t, func(t *testing.T, makeTransport transportFactory) { a := makeTransport(t.Context()) @@ -326,27 +296,17 @@ func TestEndpoint_NodeAddress(t *testing.T) { }{ // Valid endpoints. { - p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(ip4, 8080), Path: "path"}, - p2p.NodeAddress{Protocol: "tcp", Hostname: "1.2.3.4", Port: 8080, Path: "path"}, - }, - { - p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(ip6, 8080), Path: "path"}, - p2p.NodeAddress{Protocol: "tcp", Hostname: "b10c::1", Port: 8080, Path: "path"}, + p2p.Endpoint{netip.AddrPortFrom(ip4, 8080)}, + p2p.NodeAddress{Hostname: "1.2.3.4", Port: 8080}, }, { - p2p.Endpoint{Protocol: "memory", Path: "foo"}, - p2p.NodeAddress{Protocol: "memory", Path: "foo"}, - }, - { - p2p.Endpoint{Protocol: "memory", Path: string(id)}, - p2p.NodeAddress{Protocol: "memory", Path: string(id)}, + p2p.Endpoint{netip.AddrPortFrom(ip6, 8080)}, + p2p.NodeAddress{Hostname: "b10c::1", Port: 8080}, }, // Partial (invalid) endpoints. {p2p.Endpoint{}, p2p.NodeAddress{}}, - {p2p.Endpoint{Protocol: "tcp"}, p2p.NodeAddress{Protocol: "tcp"}}, - {p2p.Endpoint{Addr: netip.AddrPortFrom(ip4, 0)}, p2p.NodeAddress{Hostname: "1.2.3.4"}}, - {p2p.Endpoint{Path: "path"}, p2p.NodeAddress{Path: "path"}}, + {p2p.Endpoint{netip.AddrPortFrom(ip4, 0)}, p2p.NodeAddress{Hostname: "1.2.3.4"}}, } for _, tc := range testcases { t.Run(tc.endpoint.String(), func(t *testing.T) { @@ -361,49 +321,6 @@ func TestEndpoint_NodeAddress(t *testing.T) { } } -func TestEndpoint_String(t *testing.T) { - var ( - ip4 = netip.AddrFrom4([4]byte{1, 2, 3, 4}) - ip6 = netip.AddrFrom16([16]byte{0xb1, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}) - nodeID = types.NodeID("00112233445566778899aabbccddeeff00112233") - ) - - testcases := []struct { - endpoint p2p.Endpoint - expect string - }{ - // Non-networked endpoints. - {p2p.Endpoint{Protocol: "memory", Path: string(nodeID)}, "memory:" + string(nodeID)}, - {p2p.Endpoint{Protocol: "file", Path: "foo"}, "file:///foo"}, - {p2p.Endpoint{Protocol: "file", Path: "👋"}, "file:///%F0%9F%91%8B"}, - - // IPv4 endpoints. - {p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(ip4, 0)}, "tcp://1.2.3.4"}, - {p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(ip4, 8080)}, "tcp://1.2.3.4:8080"}, - {p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(ip4, 8080), Path: "/path"}, "tcp://1.2.3.4:8080/path"}, - {p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(ip4, 0), Path: "path/👋"}, "tcp://1.2.3.4/path/%F0%9F%91%8B"}, - - // IPv6 endpoints. - {p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(ip6, 0)}, "tcp://b10c::1"}, - {p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(ip6, 8080)}, "tcp://[b10c::1]:8080"}, - {p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(ip6, 8080), Path: "/path"}, "tcp://[b10c::1]:8080/path"}, - {p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(ip6, 0), Path: "path/👋"}, "tcp://b10c::1/path/%F0%9F%91%8B"}, - - // Partial (invalid) endpoints. - {p2p.Endpoint{}, ""}, - {p2p.Endpoint{Protocol: "tcp"}, "tcp:"}, - {p2p.Endpoint{Addr: netip.AddrPortFrom(ip4, 0)}, "1.2.3.4"}, - {p2p.Endpoint{Addr: netip.AddrPortFrom(ip6, 0)}, "b10c::1"}, - {p2p.Endpoint{Addr: netip.AddrPortFrom(netip.IPv4Unspecified(), 8080)}, "0.0.0.0:8080"}, - {p2p.Endpoint{Path: "foo"}, "/foo"}, - } - for _, tc := range testcases { - t.Run(tc.expect, func(t *testing.T) { - require.Equal(t, tc.expect, tc.endpoint.String()) - }) - } -} - func TestEndpoint_Validate(t *testing.T) { ip4 := netip.AddrFrom4([4]byte{1, 2, 3, 4}) ip6 := netip.AddrFrom16([16]byte{0xb1, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}) @@ -413,16 +330,13 @@ func TestEndpoint_Validate(t *testing.T) { expectValid bool }{ // Valid endpoints. - {p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(ip4, 0)}, true}, - {p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(ip6, 0)}, true}, - {p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(ip4, 8008)}, true}, - {p2p.Endpoint{Protocol: "tcp", Addr: netip.AddrPortFrom(ip4, 8080), Path: "path"}, true}, - {p2p.Endpoint{Protocol: "memory", Path: "path"}, true}, + {p2p.Endpoint{netip.AddrPortFrom(ip4, 0)}, true}, + {p2p.Endpoint{netip.AddrPortFrom(ip6, 0)}, true}, + {p2p.Endpoint{netip.AddrPortFrom(ip4, 8008)}, true}, // Invalid endpoints. {p2p.Endpoint{}, false}, - {p2p.Endpoint{Addr: netip.AddrPortFrom(ip4, 0)}, false}, - {p2p.Endpoint{Protocol: "tcp"}, false}, + {p2p.Endpoint{netip.AddrPortFrom(ip4, 0)}, false}, } for _, tc := range testcases { t.Run(tc.endpoint.String(), func(t *testing.T) { @@ -438,7 +352,7 @@ func TestEndpoint_Validate(t *testing.T) { // dialAccept is a helper that dials b from a and returns both sides of the // connection. -func dialAccept(ctx context.Context, t *testing.T, a, b p2p.Transport) (p2p.Connection, p2p.Connection) { +func dialAccept(ctx context.Context, t *testing.T, a, b *p2p.Transport) (p2p.Connection, p2p.Connection) { defer t.Logf("dialAccept DONE") t.Helper() @@ -469,7 +383,7 @@ func dialAccept(ctx context.Context, t *testing.T, a, b p2p.Transport) (p2p.Conn // dialAcceptHandshake is a helper that dials and handshakes b from a and // returns both sides of the connection. -func dialAcceptHandshake(ctx context.Context, t *testing.T, a, b p2p.Transport) (p2p.Connection, p2p.Connection) { +func dialAcceptHandshake(ctx context.Context, t *testing.T, a, b *p2p.Transport) (p2p.Connection, p2p.Connection) { defer t.Logf("dialAcceptHandshake DONE") t.Helper() From b4a58ab93dce23ab4414bc4bedbd4a3c76b5b34d Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Tue, 30 Sep 2025 15:07:52 +0200 Subject: [PATCH 2/9] removing mocks --- internal/p2p/mocks/transport.go | 171 ------------------ internal/p2p/router_test.go | 119 +------------ internal/p2p/transport.go | 4 - internal/p2p/transport_mconn_test.go | 253 --------------------------- internal/p2p/transport_test.go | 237 ++++++++++++++++++++++++- 5 files changed, 235 insertions(+), 549 deletions(-) delete mode 100644 internal/p2p/mocks/transport.go delete mode 100644 internal/p2p/transport_mconn_test.go diff --git a/internal/p2p/mocks/transport.go b/internal/p2p/mocks/transport.go deleted file mode 100644 index e2ee3c913..000000000 --- a/internal/p2p/mocks/transport.go +++ /dev/null @@ -1,171 +0,0 @@ -// Code generated by mockery. DO NOT EDIT. - -package mocks - -import ( - context "context" - - conn "github.com/tendermint/tendermint/internal/p2p/conn" - - mock "github.com/stretchr/testify/mock" - - p2p "github.com/tendermint/tendermint/internal/p2p" -) - -// Transport is an autogenerated mock type for the Transport type -type Transport struct { - mock.Mock -} - -// Accept provides a mock function with given fields: _a0 -func (_m *Transport) Accept(_a0 context.Context) (p2p.Connection, error) { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for Accept") - } - - var r0 p2p.Connection - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (p2p.Connection, error)); ok { - return rf(_a0) - } - if rf, ok := ret.Get(0).(func(context.Context) p2p.Connection); ok { - r0 = rf(_a0) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(p2p.Connection) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(_a0) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AddChannelDescriptors provides a mock function with given fields: _a0 -func (_m *Transport) AddChannelDescriptors(_a0 []*conn.ChannelDescriptor) { - _m.Called(_a0) -} - -// Dial provides a mock function with given fields: _a0, _a1 -func (_m *Transport) Dial(_a0 context.Context, _a1 p2p.Endpoint) (p2p.Connection, error) { - ret := _m.Called(_a0, _a1) - - if len(ret) == 0 { - panic("no return value specified for Dial") - } - - var r0 p2p.Connection - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, p2p.Endpoint) (p2p.Connection, error)); ok { - return rf(_a0, _a1) - } - if rf, ok := ret.Get(0).(func(context.Context, p2p.Endpoint) p2p.Connection); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(p2p.Connection) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, p2p.Endpoint) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Endpoint provides a mock function with no fields -func (_m *Transport) Endpoint() p2p.Endpoint { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Endpoint") - } - - var r0 p2p.Endpoint - if rf, ok := ret.Get(0).(func() p2p.Endpoint); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(p2p.Endpoint) - } - - return r0 -} - -// Protocols provides a mock function with no fields -func (_m *Transport) Protocols() []p2p.Protocol { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Protocols") - } - - var r0 []p2p.Protocol - if rf, ok := ret.Get(0).(func() []p2p.Protocol); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]p2p.Protocol) - } - } - - return r0 -} - -// Run provides a mock function with given fields: ctx -func (_m *Transport) Run(ctx context.Context) error { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for Run") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(ctx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// String provides a mock function with no fields -func (_m *Transport) String() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for String") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// NewTransport creates a new instance of Transport. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewTransport(t interface { - mock.TestingT - Cleanup(func()) -}) *Transport { - mock := &Transport{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/p2p/router_test.go b/internal/p2p/router_test.go index e27f960be..b61879a6b 100644 --- a/internal/p2p/router_test.go +++ b/internal/p2p/router_test.go @@ -358,23 +358,6 @@ func TestRouter_AcceptPeers(t *testing.T) { t.Cleanup(leaktest.Check(t)) - // Set up a mock transport that handshakes. - connCtx, connCancel := context.WithCancel(ctx) - mockConnection := &mocks.Connection{} - mockConnection.On("String").Maybe().Return("mock") - mockConnection.On("Handshake", mock.Anything, selfInfo, selfKey). - Return(tc.peerInfo, nil) - mockConnection.On("Close").Run(func(_ mock.Arguments) { connCancel() }).Return(nil).Maybe() - mockConnection.On("RemoteEndpoint").Return(p2p.Endpoint{}) - if tc.ok { - mockConnection.On("ReceiveMessage", mock.Anything).Return(chID, nil, io.EOF).Maybe() - } - - mockTransport := &mocks.Transport{} - mockTransport.On("Accept", mock.Anything).Once().Return(mockConnection, nil) - mockTransport.On("Accept", mock.Anything).Maybe().Return(nil, context.Canceled) - mockTransport.On("Run", mock.Anything).Return(nil) - // Set up and start the router. peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -411,8 +394,6 @@ func TestRouter_AcceptPeers(t *testing.T) { } router.Stop() - mockTransport.AssertExpectations(t) - mockConnection.AssertExpectations(t) }) } } @@ -425,12 +406,6 @@ func TestRouter_AcceptPeers_Errors(t *testing.T) { ctx := t.Context() - // Set up a mock transport that returns io.EOF once, which should prevent - // the router from calling Accept again. - mockTransport := &mocks.Transport{} - mockTransport.On("Accept", mock.Anything).Once().Return(nil, context.Canceled) - mockTransport.On("Run", mock.Anything).Return(nil) - // Set up and start the router. peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -450,9 +425,6 @@ func TestRouter_AcceptPeers_Errors(t *testing.T) { require.NoError(t, router.Start(ctx)) time.Sleep(time.Second) router.Stop() - - mockTransport.AssertExpectations(t) - }) } @@ -469,20 +441,6 @@ func TestRouter_AcceptPeers_HeadOfLineBlocking(t *testing.T) { acceptCh := make(chan bool, 3) closeCh := make(chan time.Time) - mockConnection := &mocks.Connection{} - mockConnection.On("String").Maybe().Return("mock") - mockConnection.On("Handshake", mock.Anything, selfInfo, selfKey). - WaitUntil(closeCh).Return(types.NodeInfo{}, io.EOF) - mockConnection.On("Close").Return(nil) - mockConnection.On("RemoteEndpoint").Return(p2p.Endpoint{}) - - mockTransport := &mocks.Transport{} - mockTransport.On("Accept", mock.Anything).Times(3).Run(func(_ mock.Arguments) { - acceptCh <- true - }).Return(mockConnection, nil) - mockTransport.On("Accept", mock.Anything).Once().Return(nil, context.Canceled) - mockTransport.On("Run", mock.Anything).Return(nil) - // Set up and start the router. peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -540,36 +498,7 @@ func TestRouter_DialPeers(t *testing.T) { t.Cleanup(leaktest.Check(t)) ctx := t.Context() - address := p2p.NodeAddress{Protocol: "mock", NodeID: tc.dialID} - endpoint := p2p.Endpoint{Protocol: "mock", Path: string(tc.dialID)} - - // Set up a mock transport that handshakes. - connCtx, connCancel := context.WithCancel(ctx) - defer connCancel() - mockConnection := &mocks.Connection{} - mockConnection.On("String").Maybe().Return("mock") - if tc.dialErr == nil { - mockConnection.On("Handshake", mock.Anything, selfInfo, selfKey). - Return(tc.peerInfo, nil) - mockConnection.On("Close").Run(func(_ mock.Arguments) { connCancel() }).Return(nil).Maybe() - } - if tc.ok { - mockConnection.On("ReceiveMessage", mock.Anything).Return(chID, nil, io.EOF).Maybe() - } - - mockTransport := &mocks.Transport{} - mockTransport.On("Run", mock.Anything).Return(nil) - mockTransport.On("Accept", mock.Anything).Maybe().Return(nil, context.Canceled) - if tc.dialErr == nil { - mockTransport.On("Dial", mock.Anything, endpoint).Once().Return(mockConnection, nil) - // This handles the retry when a dialed connection gets closed after ReceiveMessage - // returns io.EOF above. - mockTransport.On("Dial", mock.Anything, endpoint).Maybe().Return(nil, io.EOF) - } else { - mockTransport.On("Dial", mock.Anything, endpoint).Once(). - Run(func(_ mock.Arguments) { connCancel() }). - Return(nil, tc.dialErr) - } + address := p2p.NodeAddress{NodeID: tc.dialID} // Set up and start the router. peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) @@ -610,8 +539,6 @@ func TestRouter_DialPeers(t *testing.T) { } router.Stop() - mockTransport.AssertExpectations(t) - mockConnection.AssertExpectations(t) }) } } @@ -621,31 +548,15 @@ func TestRouter_DialPeers_Parallel(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{Protocol: "mock", NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{Protocol: "mock", NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{Protocol: "mock", NodeID: types.NodeID(strings.Repeat("c", 40))} + a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} // Set up a mock transport that returns a connection that blocks during the // handshake. It should dial all peers in parallel. dialCh := make(chan bool, 3) closeCh := make(chan time.Time) - mockConnection := &mocks.Connection{} - mockConnection.On("String").Maybe().Return("mock") - mockConnection.On("Handshake", mock.Anything, selfInfo, selfKey). - WaitUntil(closeCh).Return(types.NodeInfo{}, nil, io.EOF) - mockConnection.On("Close").Return(nil) - - mockTransport := &mocks.Transport{} - mockTransport.On("Run", mock.Anything).Return(nil) - mockTransport.On("Accept", mock.Anything).Once().Return(nil, context.Canceled) - for _, address := range []p2p.NodeAddress{a, b, c} { - endpoint := p2p.Endpoint{Protocol: address.Protocol, Path: string(address.NodeID)} - mockTransport.On("Dial", mock.Anything, endpoint).Run(func(_ mock.Arguments) { - dialCh <- true - }).Return(mockConnection, nil) - } - // Set up and start the router. peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -697,8 +608,6 @@ func TestRouter_DialPeers_Parallel(t *testing.T) { time.Sleep(500 * time.Millisecond) router.Stop() - mockTransport.AssertExpectations(t) - mockConnection.AssertExpectations(t) } func TestRouter_EvictPeers(t *testing.T) { @@ -711,23 +620,6 @@ func TestRouter_EvictPeers(t *testing.T) { closeCh := make(chan time.Time) closeOnce := sync.Once{} - mockConnection := &mocks.Connection{} - mockConnection.On("String").Maybe().Return("mock") - mockConnection.On("Handshake", mock.Anything, selfInfo, selfKey). - Return(peerInfo, nil) - mockConnection.On("ReceiveMessage", mock.Anything).WaitUntil(closeCh).Return(chID, nil, io.EOF) - mockConnection.On("RemoteEndpoint").Return(p2p.Endpoint{}) - mockConnection.On("Close").Run(func(_ mock.Arguments) { - closeOnce.Do(func() { - close(closeCh) - }) - }).Return(nil) - - mockTransport := &mocks.Transport{} - mockTransport.On("Accept", mock.Anything).Once().Return(mockConnection, nil) - mockTransport.On("Accept", mock.Anything).Maybe().Return(nil, context.Canceled) - mockTransport.On("Run", mock.Anything).Return(nil) - // Set up and start the router. peerManager, err := p2p.NewPeerManager(logger, selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -740,7 +632,6 @@ func TestRouter_EvictPeers(t *testing.T) { selfKey, peerManager, func() *types.NodeInfo { return &selfInfo }, - mockTransport, nil, p2p.RouterOptions{}, ) @@ -761,8 +652,6 @@ func TestRouter_EvictPeers(t *testing.T) { }) router.Stop() - mockTransport.AssertExpectations(t) - mockConnection.AssertExpectations(t) } func TestRouter_ChannelCompatability(t *testing.T) { diff --git a/internal/p2p/transport.go b/internal/p2p/transport.go index adec098d9..cc6866916 100644 --- a/internal/p2p/transport.go +++ b/internal/p2p/transport.go @@ -300,10 +300,6 @@ func (c *mConnConnection) Close() error { } return c.mconn.Close() } - -// Protocol identifies a transport protocol. -type Protocol string - // Connection represents an established connection between two endpoints. // // FIXME: This is a temporary interface for backwards-compatibility with the diff --git a/internal/p2p/transport_mconn_test.go b/internal/p2p/transport_mconn_test.go deleted file mode 100644 index 57087a4c9..000000000 --- a/internal/p2p/transport_mconn_test.go +++ /dev/null @@ -1,253 +0,0 @@ -package p2p_test - -import ( - "context" - "errors" - "fmt" - "io" - "net/netip" - "testing" - "time" - - "github.com/fortytw2/leaktest" - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/libs/utils" - "github.com/tendermint/tendermint/libs/utils/scope" - "github.com/tendermint/tendermint/libs/utils/tcp" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/internal/p2p" - "github.com/tendermint/tendermint/internal/p2p/conn" - "github.com/tendermint/tendermint/libs/log" - "github.com/tendermint/tendermint/types" -) - -func makeKeyAndInfo() (crypto.PrivKey, types.NodeInfo) { - peerKey := ed25519.GenPrivKey() - nodeID := types.NodeIDFromPubKey(peerKey.PubKey()) - peerInfo := types.NodeInfo{ - NodeID: nodeID, - ListenAddr: "0.0.0.0:0", - Network: "test", - Moniker: string(nodeID), - Channels: []byte{0x01, 0x02}, - } - return peerKey, peerInfo -} - -// Transports are mainly tested by common tests in transport_test.go, we -// register a transport factory here to get included in those tests. -func init() { - testTransports["mconn"] = func() func(context.Context) *p2p.Transport { - return func(ctx context.Context) p2p.Transport { - logger, _ := log.NewDefaultLogger("plain", "info") - transport := p2p.NewTransport( - logger, - p2p.Endpoint{tcp.TestReserveAddr()}, - conn.DefaultMConnConfig(), - []*p2p.ChannelDescriptor{{ID: chID, Priority: 1}}, - p2p.TransportOptions{}, - ) - go func() { - if err := transport.Run(ctx); err != nil { - panic(err) - } - }() - if err := transport.WaitForStart(ctx); err != nil { - panic(err) - } - return transport - } - } -} - -// Establishes a connection to the transport. -// Returns both ends of the connection. -func connect(ctx context.Context, tr *p2p.Transport) (c1 p2p.Connection, c2 p2p.Connection, err error) { - defer func() { - if err != nil { - if c1 != nil { - c1.Close() - } - if c2 != nil { - c2.Close() - } - } - }() - // Here we are utilizing the fact that Transport accepts connection proactively - // before Accept is called. - c1, err = tr.Dial(ctx, tr.Endpoint()) - if err != nil { - return nil, nil, fmt.Errorf("Dial(): %w", err) - } - c2, err = tr.Accept(ctx) - if err != nil { - return nil, nil, fmt.Errorf("Accept(): %w", err) - } - if got, want := c1.LocalEndpoint(), c2.RemoteEndpoint(); got != want { - return nil, nil, fmt.Errorf("c1.LocalEndpoint() = %v, want %v", got, want) - } - if got, want := c1.RemoteEndpoint(), c2.LocalEndpoint(); got != want { - return nil, nil, fmt.Errorf("c1.RemoteEndpoint() = %v, want %v", got, want) - } - return c1, c2, nil -} - -func TestTransport_AcceptMaxAcceptedConnections(t *testing.T) { - ctx := t.Context() - transport := p2p.NewTransport( - log.NewNopLogger(), - p2p.Endpoint{tcp.TestReserveAddr()}, - conn.DefaultMConnConfig(), - []*p2p.ChannelDescriptor{{ID: chID, Priority: 1}}, - p2p.TransportOptions{ - MaxAcceptedConnections: 2, - }, - ) - - err := utils.IgnoreCancel(scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { - s.SpawnBgNamed("transport", func() error { return transport.Run(ctx) }) - if err := transport.WaitForStart(ctx); err != nil { - return err - } - t.Logf("The first two connections should be accepted just fine.") - - a1, a2, err := connect(ctx, transport) - if err != nil { - return fmt.Errorf("1st connect(): %w", err) - } - defer a1.Close() - defer a2.Close() - - b1, b2, err := connect(ctx, transport) - if err != nil { - return fmt.Errorf("2nd connect(): %w", err) - } - defer b1.Close() - defer b2.Close() - - t.Logf("The third connection will be dialed successfully, but the accept should not go through.") - c1, err := transport.Dial(ctx, transport.Endpoint()) - if err != nil { - return fmt.Errorf("3rd Dial(): %w", err) - } - defer c1.Close() - if err := utils.WithTimeout(ctx, time.Second, func(ctx context.Context) error { - c2, err := transport.Accept(ctx) - if err == nil { - c2.Close() - } - return err - }); !errors.Is(err, context.DeadlineExceeded) { - return fmt.Errorf("Accept() over cap: %v, want %v", err, context.DeadlineExceeded) - } - - t.Logf("once either of the other connections are closed, the accept goes through.") - a1.Close() - a2.Close() // we close both a1 and a2 to make sure the connection count drops below the limit. - c2, err := transport.Accept(ctx) - if err != nil { - return fmt.Errorf("3rd Accept(): %w", err) - } - defer c2.Close() - return nil - })) - if err != nil { - t.Fatal(err) - } -} - -func TestTransport_Listen(t *testing.T) { - reservePort := func(ip netip.Addr) netip.AddrPort { - addr := tcp.TestReserveAddr() - return netip.AddrPortFrom(ip, addr.Port()) - } - - testcases := []struct { - endpoint p2p.Endpoint - ok bool - }{ - // Valid v4 and v6 addresses, with mconn and tcp protocols. - {p2p.Endpoint{reservePort(netip.IPv4Unspecified())}, true}, - {p2p.Endpoint{reservePort(tcp.IPv4Loopback())}, true}, - {p2p.Endpoint{reservePort(netip.IPv6Unspecified())}, true}, - {p2p.Endpoint{reservePort(netip.IPv6Loopback())}, true}, - - // Invalid endpoints. - {p2p.Endpoint{}, false}, - } - - aKey, aInfo := makeKeyAndInfo() - bKey, bInfo := makeKeyAndInfo() - for _, tc := range testcases { - t.Run(tc.endpoint.String(), func(t *testing.T) { - ctx := t.Context() - t.Cleanup(leaktest.Check(t)) - - transport := p2p.NewTransport( - log.NewNopLogger(), - tc.endpoint, - conn.DefaultMConnConfig(), - []*p2p.ChannelDescriptor{{ID: chID, Priority: 1}}, - p2p.TransportOptions{}, - ) - if got, want := transport.Endpoint(), tc.endpoint; got != want { - t.Fatalf("transport.Endpoint() = %v, want %v", got, want) - } - - err := utils.IgnoreCancel(scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { - s.SpawnBgNamed("transport", func() error { return transport.Run(ctx) }) - if err := transport.WaitForStart(ctx); err != nil { - return err - } - s.SpawnNamed("dial", func() error { - conn, err := transport.Dial(ctx, tc.endpoint) - if err != nil { - return fmt.Errorf("transport.Dial(): %w", err) - } - defer conn.Close() - if _, err := conn.Handshake(ctx, aInfo, aKey); err != nil { - return fmt.Errorf("conn.Handshake(): %w", err) - } - if err := conn.Close(); err != nil { - return fmt.Errorf("conn.Close(): %w", err) - } - if _, _, err := conn.ReceiveMessage(ctx); !errors.Is(err, io.EOF) { - return fmt.Errorf("conn.ReceiveMessage() = %v, want %v", err, io.EOF) - } - return nil - }) - s.SpawnNamed("accept", func() error { - conn, err := transport.Accept(ctx) - if err != nil { - return fmt.Errorf("transport.Accept(): %w", err) - } - defer conn.Close() - if _, err := conn.Handshake(ctx, bInfo, bKey); err != nil { - return fmt.Errorf("conn.Handshake(): %w", err) - } - if err := conn.Close(); err != nil { - return fmt.Errorf("conn.Close(): %w", err) - } - if _, _, err := conn.ReceiveMessage(ctx); !errors.Is(err, io.EOF) { - return fmt.Errorf("conn.ReceiveMessage() = %v, want %v", err, io.EOF) - } - return nil - }) - return nil - })) - if !tc.ok { - var want p2p.InvalidEndpointErr - if !errors.As(err, &want) { - t.Fatalf("error = %v, want %T", err, want) - } - } else if err != nil { - t.Fatal(err) - } - // Dialing the closed endpoint should error - _, err = transport.Dial(ctx, tc.endpoint) - require.Error(t, err) - }) - } -} diff --git a/internal/p2p/transport_test.go b/internal/p2p/transport_test.go index 1c5acee83..ccab7e169 100644 --- a/internal/p2p/transport_test.go +++ b/internal/p2p/transport_test.go @@ -15,8 +15,219 @@ import ( "github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/libs/utils/scope" "github.com/tendermint/tendermint/types" + "errors" + "fmt" + "io" + "github.com/tendermint/tendermint/libs/utils" + "github.com/tendermint/tendermint/libs/utils/tcp" + + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/internal/p2p/conn" + "github.com/tendermint/tendermint/libs/log" ) +func makeKeyAndInfo() (crypto.PrivKey, types.NodeInfo) { + peerKey := ed25519.GenPrivKey() + nodeID := types.NodeIDFromPubKey(peerKey.PubKey()) + peerInfo := types.NodeInfo{ + NodeID: nodeID, + ListenAddr: "0.0.0.0:0", + Network: "test", + Moniker: string(nodeID), + Channels: []byte{0x01, 0x02}, + } + return peerKey, peerInfo +} + +// Establishes a connection to the transport. +// Returns both ends of the connection. +func connect(ctx context.Context, tr *p2p.Transport) (c1 p2p.Connection, c2 p2p.Connection, err error) { + defer func() { + if err != nil { + if c1 != nil { + c1.Close() + } + if c2 != nil { + c2.Close() + } + } + }() + // Here we are utilizing the fact that Transport accepts connection proactively + // before Accept is called. + c1, err = tr.Dial(ctx, tr.Endpoint()) + if err != nil { + return nil, nil, fmt.Errorf("Dial(): %w", err) + } + c2, err = tr.Accept(ctx) + if err != nil { + return nil, nil, fmt.Errorf("Accept(): %w", err) + } + if got, want := c1.LocalEndpoint(), c2.RemoteEndpoint(); got != want { + return nil, nil, fmt.Errorf("c1.LocalEndpoint() = %v, want %v", got, want) + } + if got, want := c1.RemoteEndpoint(), c2.LocalEndpoint(); got != want { + return nil, nil, fmt.Errorf("c1.RemoteEndpoint() = %v, want %v", got, want) + } + return c1, c2, nil +} + +func TestTransport_AcceptMaxAcceptedConnections(t *testing.T) { + ctx := t.Context() + transport := p2p.NewTransport( + log.NewNopLogger(), + p2p.Endpoint{tcp.TestReserveAddr()}, + conn.DefaultMConnConfig(), + []*p2p.ChannelDescriptor{{ID: chID, Priority: 1}}, + p2p.TransportOptions{ + MaxAcceptedConnections: 2, + }, + ) + + err := utils.IgnoreCancel(scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { + s.SpawnBgNamed("transport", func() error { return transport.Run(ctx) }) + if err := transport.WaitForStart(ctx); err != nil { + return err + } + t.Logf("The first two connections should be accepted just fine.") + + a1, a2, err := connect(ctx, transport) + if err != nil { + return fmt.Errorf("1st connect(): %w", err) + } + defer a1.Close() + defer a2.Close() + + b1, b2, err := connect(ctx, transport) + if err != nil { + return fmt.Errorf("2nd connect(): %w", err) + } + defer b1.Close() + defer b2.Close() + + t.Logf("The third connection will be dialed successfully, but the accept should not go through.") + c1, err := transport.Dial(ctx, transport.Endpoint()) + if err != nil { + return fmt.Errorf("3rd Dial(): %w", err) + } + defer c1.Close() + if err := utils.WithTimeout(ctx, time.Second, func(ctx context.Context) error { + c2, err := transport.Accept(ctx) + if err == nil { + c2.Close() + } + return err + }); !errors.Is(err, context.DeadlineExceeded) { + return fmt.Errorf("Accept() over cap: %v, want %v", err, context.DeadlineExceeded) + } + + t.Logf("once either of the other connections are closed, the accept goes through.") + a1.Close() + a2.Close() // we close both a1 and a2 to make sure the connection count drops below the limit. + c2, err := transport.Accept(ctx) + if err != nil { + return fmt.Errorf("3rd Accept(): %w", err) + } + defer c2.Close() + return nil + })) + if err != nil { + t.Fatal(err) + } +} + +func TestTransport_Listen(t *testing.T) { + reservePort := func(ip netip.Addr) netip.AddrPort { + addr := tcp.TestReserveAddr() + return netip.AddrPortFrom(ip, addr.Port()) + } + + testcases := []struct { + endpoint p2p.Endpoint + ok bool + }{ + // Valid v4 and v6 addresses, with mconn and tcp protocols. + {p2p.Endpoint{reservePort(netip.IPv4Unspecified())}, true}, + {p2p.Endpoint{reservePort(tcp.IPv4Loopback())}, true}, + {p2p.Endpoint{reservePort(netip.IPv6Unspecified())}, true}, + {p2p.Endpoint{reservePort(netip.IPv6Loopback())}, true}, + + // Invalid endpoints. + {p2p.Endpoint{}, false}, + } + + aKey, aInfo := makeKeyAndInfo() + bKey, bInfo := makeKeyAndInfo() + for _, tc := range testcases { + t.Run(tc.endpoint.String(), func(t *testing.T) { + ctx := t.Context() + t.Cleanup(leaktest.Check(t)) + + transport := p2p.NewTransport( + log.NewNopLogger(), + tc.endpoint, + conn.DefaultMConnConfig(), + []*p2p.ChannelDescriptor{{ID: chID, Priority: 1}}, + p2p.TransportOptions{}, + ) + if got, want := transport.Endpoint(), tc.endpoint; got != want { + t.Fatalf("transport.Endpoint() = %v, want %v", got, want) + } + + err := utils.IgnoreCancel(scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { + s.SpawnBgNamed("transport", func() error { return transport.Run(ctx) }) + if err := transport.WaitForStart(ctx); err != nil { + return err + } + s.SpawnNamed("dial", func() error { + conn, err := transport.Dial(ctx, tc.endpoint) + if err != nil { + return fmt.Errorf("transport.Dial(): %w", err) + } + defer conn.Close() + if _, err := conn.Handshake(ctx, aInfo, aKey); err != nil { + return fmt.Errorf("conn.Handshake(): %w", err) + } + if err := conn.Close(); err != nil { + return fmt.Errorf("conn.Close(): %w", err) + } + if _, _, err := conn.ReceiveMessage(ctx); !errors.Is(err, io.EOF) { + return fmt.Errorf("conn.ReceiveMessage() = %v, want %v", err, io.EOF) + } + return nil + }) + s.SpawnNamed("accept", func() error { + conn, err := transport.Accept(ctx) + if err != nil { + return fmt.Errorf("transport.Accept(): %w", err) + } + defer conn.Close() + if _, err := conn.Handshake(ctx, bInfo, bKey); err != nil { + return fmt.Errorf("conn.Handshake(): %w", err) + } + if err := conn.Close(); err != nil { + return fmt.Errorf("conn.Close(): %w", err) + } + if _, _, err := conn.ReceiveMessage(ctx); !errors.Is(err, io.EOF) { + return fmt.Errorf("conn.ReceiveMessage() = %v, want %v", err, io.EOF) + } + return nil + }) + return nil + })) + if !tc.ok { + var want p2p.InvalidEndpointErr + if !errors.As(err, &want) { + t.Fatalf("error = %v, want %T", err, want) + } + } else if err != nil { + t.Fatal(err) + } + // Dialing the closed endpoint should error + _, err = transport.Dial(ctx, tc.endpoint) + require.Error(t, err) + }) + } +} // transportFactory is used to set up transports for tests. type transportFactory = func(ctx context.Context) *p2p.Transport @@ -27,12 +238,26 @@ var testTransports = map[string](func() transportFactory){} // registered in testTransports. func withTransports(t *testing.T, tester func(*testing.T, transportFactory)) { t.Helper() - for name, transportFactory := range testTransports { - t.Run(name, func(t *testing.T) { - t.Cleanup(leaktest.Check(t)) - tester(t, transportFactory()) - }) - } + t.Cleanup(leaktest.Check(t)) + tester(t, func(ctx context.Context) *p2p.Transport { + logger, _ := log.NewDefaultLogger("plain", "info") + transport := p2p.NewTransport( + logger, + p2p.Endpoint{tcp.TestReserveAddr()}, + conn.DefaultMConnConfig(), + []*p2p.ChannelDescriptor{{ID: chID, Priority: 1}}, + p2p.TransportOptions{}, + ) + go func() { + if err := transport.Run(ctx); err != nil { + panic(err) + } + }() + if err := transport.WaitForStart(ctx); err != nil { + panic(err) + } + return transport + }) } func TestTransport_DialEndpoints(t *testing.T) { From a4097c042527532d9079f01fea76b3e6a5eed2ce Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Wed, 1 Oct 2025 13:25:48 +0200 Subject: [PATCH 3/9] removed mocks from router tests --- internal/p2p/router_test.go | 808 ++++++++++++++++-------------------- internal/p2p/transport.go | 4 +- 2 files changed, 361 insertions(+), 451 deletions(-) diff --git a/internal/p2p/router_test.go b/internal/p2p/router_test.go index b61879a6b..648e9cf82 100644 --- a/internal/p2p/router_test.go +++ b/internal/p2p/router_test.go @@ -4,26 +4,23 @@ import ( "context" "errors" "fmt" - "io" - slog "log" - "runtime" "strings" - "sync" "testing" "time" "github.com/fortytw2/leaktest" "github.com/gogo/protobuf/proto" gogotypes "github.com/gogo/protobuf/types" - "github.com/stretchr/testify/mock" dbm "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/internal/p2p" - "github.com/tendermint/tendermint/internal/p2p/mocks" "github.com/tendermint/tendermint/internal/p2p/p2ptest" "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/libs/utils" "github.com/tendermint/tendermint/libs/utils/require" + "github.com/tendermint/tendermint/libs/utils/scope" "github.com/tendermint/tendermint/types" + "github.com/tendermint/tendermint/crypto" ) func echoReactor(ctx context.Context, channel *p2p.Channel) { @@ -31,7 +28,6 @@ func echoReactor(ctx context.Context, channel *p2p.Channel) { for iter.Next(ctx) { envelope := iter.Envelope() value := envelope.Message.(*p2ptest.Message).Value - slog.Printf("sending back %v", value) if err := channel.Send(ctx, p2p.Envelope{ To: envelope.From, Message: &p2ptest.Message{Value: value}, @@ -39,7 +35,6 @@ func echoReactor(ctx context.Context, channel *p2p.Channel) { return } } - slog.Printf("echoReactor done") } func TestRouter_Network(t *testing.T) { @@ -333,509 +328,424 @@ func TestRouter_Channel_Error(t *testing.T) { }) } +type RouterHandle struct { + router *p2p.Router + peerManager *p2p.PeerManager + transport *p2p.Transport +} + +var keyFiltered,infoFiltered = makeKeyAndInfo() + +func spawnRouter(t *testing.T, logger log.Logger) RouterHandle { + t.Helper() + ctx := t.Context() + // Set up and start the router. + opts := p2p.PeerManagerOptions{ + MinRetryTime: 100*time.Millisecond, + } + peerManager, err := p2p.NewPeerManager(logger, selfID, dbm.NewMemDB(), opts, p2p.NopMetrics()) + require.NoError(t, err) + // Router is responsible for running transport. + transport := p2p.TestTransport(logger.With("node","self"), selfID) + router, err := p2p.NewRouter( + logger, + p2p.NopMetrics(), + selfKey, + peerManager, + func() *types.NodeInfo { return &selfInfo }, + transport, + func(_ context.Context, id types.NodeID) error { + if id==infoFiltered.NodeID { + return errors.New("should filter") + } + return nil + }, + p2p.RouterOptions{ + DialSleep: func(context.Context) error { return nil }, + NumConcurrentDials: func() int { return 100 }, + }, + ) + require.NoError(t, err) + require.NoError(t, router.Start(ctx)) + t.Cleanup(router.Stop) + require.NoError(t, transport.WaitForStart(ctx)) + return RouterHandle{ + router: router, + peerManager: peerManager, + transport: transport, + } +} + func TestRouter_AcceptPeers(t *testing.T) { + key,info := makeKeyAndInfo() + info2 := info + info2.Network = "other-network" + info3 := info + info3.Channels = []byte{0x23} testcases := map[string]struct { - peerInfo types.NodeInfo + info types.NodeInfo + key crypto.PrivKey ok bool }{ - "valid handshake": {peerInfo, true}, - "empty handshake": {types.NodeInfo{}, false}, - "self handshake": {selfInfo, false}, - "incompatible peer": { - types.NodeInfo{ - NodeID: peerID, - ListenAddr: "0.0.0.0:0", - Network: "other-network", - Moniker: string(peerID), - }, - false, - }, + "valid handshake": {info, key, true}, + "empty handshake": {types.NodeInfo{}, key, false}, + "self handshake": {selfInfo, selfKey, false}, + "incompatible network": {info2, key, false}, + "incompatible channels": {info3, key, false}, + "filtered": {infoFiltered, keyFiltered, false}, } for name, tc := range testcases { t.Run(name, func(t *testing.T) { + logger, _ := log.NewDefaultLogger("plain", "debug") ctx := t.Context() - t.Cleanup(leaktest.Check(t)) - - // Set up and start the router. - peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) - require.NoError(t, err) - - sub := peerManager.Subscribe(ctx) - - router, err := p2p.NewRouter( - log.NewNopLogger(), - p2p.NopMetrics(), - selfKey, - peerManager, - func() *types.NodeInfo { return &selfInfo }, - mockTransport, - nil, - p2p.RouterOptions{}, - ) - require.NoError(t, err) - require.NoError(t, router.Start(ctx)) - - if tc.ok { - p2ptest.RequireUpdate(t, sub, p2p.PeerUpdate{ - NodeID: tc.peerInfo.NodeID, - Status: p2p.PeerStatusUp, - }) - // force a context switch so that the - // connection is handled. - time.Sleep(time.Millisecond) - } else { - select { - case <-connCtx.Done(): - case <-time.After(100 * time.Millisecond): - require.Fail(t, "connection not closed") + h := spawnRouter(t, logger) + sub := h.peerManager.Subscribe(ctx) + + if err := scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { + peerTransport := p2p.TestTransport(logger.With("node","peer"), peerID) + s.SpawnBgNamed("peerTransport.Run()",func() error { return peerTransport.Run(ctx) }) + conn,err := peerTransport.Dial(ctx, h.transport.Endpoint()) + if err!=nil { + return fmt.Errorf("peerTransport.Dial(): %w",err) } + defer conn.Close() + if tc.ok { + if _,err := conn.Handshake(ctx, tc.info, tc.key); err!=nil { + return fmt.Errorf("conn.Handshake(): %w",err) + } + p2ptest.RequireUpdate(t, sub, p2p.PeerUpdate{ + NodeID: tc.info.NodeID, + Status: p2p.PeerStatusUp, + }) + } else { + // Expect immediate or delayed failure. + // Peer should drop the connection during handshake. + if _,err := conn.Handshake(ctx, tc.info, tc.key); err!=nil { + return nil + } + if _,_,err := conn.ReceiveMessage(ctx); !errors.Is(err,context.Canceled) { + return fmt.Errorf("want Canceled, got %w",err) + } + } + return nil + }); err!=nil { + t.Fatal(err) } - - router.Stop() }) } } -func TestRouter_AcceptPeers_Errors(t *testing.T) { - - for _, err := range []error{io.EOF, context.Canceled, context.DeadlineExceeded} { - t.Run(err.Error(), func(t *testing.T) { - t.Cleanup(leaktest.Check(t)) - - ctx := t.Context() - - // Set up and start the router. - peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) - require.NoError(t, err) - - router, err := p2p.NewRouter( - log.NewNopLogger(), - p2p.NopMetrics(), - selfKey, - peerManager, - func() *types.NodeInfo { return &selfInfo }, - mockTransport, - nil, - p2p.RouterOptions{}, - ) - require.NoError(t, err) - - require.NoError(t, router.Start(ctx)) - time.Sleep(time.Second) - router.Stop() - }) +// Test checking that multiple peers connecting at once don't block each other. +func TestRouter_AcceptPeers_Parallel(t *testing.T) { + logger, _ := log.NewDefaultLogger("plain", "debug") + ctx := t.Context() + t.Cleanup(leaktest.Check(t)) + t.Logf("Set up and start the router.") + h := spawnRouter(t, logger) + sub := h.peerManager.Subscribe(ctx) + + if err := scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { + t.Logf("Dial raw connections.") + var conns []p2p.Connection + for range 10 { + peerTransport := p2p.TestTransport(logger.With("node","peer"), peerID) + s.SpawnBgNamed("peerTransport.Run()",func() error { return peerTransport.Run(ctx) }) + conn,err := peerTransport.Dial(ctx, h.transport.Endpoint()) + if err!=nil { + return fmt.Errorf("peerTransport.Dial(): %w",err) + } + defer conn.Close() + conns = append(conns, conn) + } + t.Logf("Handshake the connections in reverse order.") + for i:=len(conns)-1; i>=0; i-- { + conn := conns[i] + key,info := makeKeyAndInfo() + if _,err := conn.Handshake(ctx, info, key); err!=nil { + return fmt.Errorf("conn.Handshake(): %w",err) + } + p2ptest.RequireUpdate(t, sub, p2p.PeerUpdate{ + NodeID: info.NodeID, + Status: p2p.PeerStatusUp, + }) + } + return nil + }); err!=nil { + t.Fatal(err) } } -func TestRouter_AcceptPeers_HeadOfLineBlocking(t *testing.T) { - t.Cleanup(leaktest.Check(t)) - +func TestRouter_DialPeer_Retry(t *testing.T) { + logger, _ := log.NewDefaultLogger("plain", "debug") ctx := t.Context() + t.Cleanup(leaktest.Check(t)) - // Set up a mock transport that returns a connection that blocks during the - // handshake. It should be able to accept several of these in parallel, i.e. - // a single connection can't halt other connections being accepted. - acceptCh := make(chan bool, 3) - closeCh := make(chan time.Time) + t.Logf("Set up and start the router.") + h := spawnRouter(t, logger) + sub := h.peerManager.Subscribe(ctx) - // Set up and start the router. - peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) - require.NoError(t, err) + if err := scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { + peerTransport := p2p.TestTransport(logger.With("node","peer"), peerID) + s.SpawnBgNamed("peerTransport.Run()",func() error { return peerTransport.Run(ctx) }) + if err:=peerTransport.WaitForStart(ctx); err!=nil { return err } - router, err := p2p.NewRouter( - log.NewNopLogger(), - p2p.NopMetrics(), - selfKey, - peerManager, - func() *types.NodeInfo { return &selfInfo }, - mockTransport, - nil, - p2p.RouterOptions{}, - ) - require.NoError(t, err) - require.NoError(t, router.Start(ctx)) + t.Log("Populate peer manager.") + key,info := makeKeyAndInfo() + if ok,err := h.peerManager.Add(peerTransport.Endpoint().NodeAddress(info.NodeID)); !ok || err!=nil { + return fmt.Errorf("peerManager.Add() = %v,%w",ok,err) + } - require.Eventually(t, func() bool { - return len(acceptCh) == 3 - }, time.Second, 10*time.Millisecond, "num", len(acceptCh)) - close(closeCh) - time.Sleep(100 * time.Millisecond) + t.Log("Accept and drop.") + conn,err := peerTransport.Accept(ctx) + if err!=nil { + return fmt.Errorf("peerTransport.Dial(): %w",err) + } + conn.Close() - router.Stop() - mockTransport.AssertExpectations(t) - mockConnection.AssertExpectations(t) + t.Log("Accept and complete handshake.") + conn,err = peerTransport.Accept(ctx) + if err!=nil { + return fmt.Errorf("peerTransport.Dial(): %w",err) + } + defer conn.Close() + if _,err := conn.Handshake(ctx, info, key); err!=nil { + return fmt.Errorf("conn.Handshake(): %w",err) + } + p2ptest.RequireUpdate(t, sub, p2p.PeerUpdate{ + NodeID: info.NodeID, + Status: p2p.PeerStatusUp, + }) + return nil + }); err!=nil { + t.Fatal(err) + } } -func TestRouter_DialPeers(t *testing.T) { +func TestRouter_DialPeer_Reject(t *testing.T) { + key,info := makeKeyAndInfo() + _,info2 := makeKeyAndInfo() + info3 := info + info3.Network = "other-network" + info4 := info + info4.Channels = []byte{0x23} testcases := map[string]struct { - dialID types.NodeID - peerInfo types.NodeInfo - dialErr error - ok bool + dialID types.NodeID + info types.NodeInfo }{ - "valid dial": {peerInfo.NodeID, peerInfo, nil, true}, - "empty handshake": {peerInfo.NodeID, types.NodeInfo{}, nil, false}, - "unexpected node ID": {peerInfo.NodeID, selfInfo, nil, false}, - "dial error": {peerInfo.NodeID, peerInfo, errors.New("boom"), false}, - "incompatible peer": { - peerInfo.NodeID, - types.NodeInfo{ - NodeID: peerID, - ListenAddr: "0.0.0.0:0", - Network: "other-network", - Moniker: string(peerID), - }, - nil, - false, - }, + "empty handshake": {info.NodeID, types.NodeInfo{}}, + "unexpected node ID": {info2.NodeID, info}, + "incompatible network": {info.NodeID, info3}, + "incompatible channels": {info.NodeID, info4}, } - for name, tc := range testcases { t.Run(name, func(t *testing.T) { + logger, _ := log.NewDefaultLogger("plain", "debug") t.Cleanup(leaktest.Check(t)) ctx := t.Context() - - address := p2p.NodeAddress{NodeID: tc.dialID} - - // Set up and start the router. - peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) - require.NoError(t, err) - - added, err := peerManager.Add(address) - require.NoError(t, err) - require.True(t, added) - sub := peerManager.Subscribe(ctx) - - router, err := p2p.NewRouter( - log.NewNopLogger(), - p2p.NopMetrics(), - selfKey, - peerManager, - func() *types.NodeInfo { return &selfInfo }, - mockTransport, - nil, - p2p.RouterOptions{}, - ) - require.NoError(t, err) - require.NoError(t, router.Start(ctx)) - - if tc.ok { - p2ptest.RequireUpdate(t, sub, p2p.PeerUpdate{ - NodeID: tc.peerInfo.NodeID, - Status: p2p.PeerStatusUp, - }) - // force a context switch so that the - // connection is handled. - time.Sleep(time.Millisecond) - } else { - select { - case <-connCtx.Done(): - case <-time.After(100 * time.Millisecond): - require.Fail(t, "connection not closed") + h := spawnRouter(t, logger) + if err := scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { + peerTransport := p2p.TestTransport(logger.With("node","peer"), tc.dialID) + s.SpawnBgNamed("peerTransport.Run()",func() error { return peerTransport.Run(ctx) }) + if err:=peerTransport.WaitForStart(ctx); err!=nil { + return fmt.Errorf("peerTransport.WaitForStart(): %w",err) } + if ok,err := h.peerManager.Add(peerTransport.Endpoint().NodeAddress(tc.dialID)); !ok || err!=nil { + return fmt.Errorf("peerManager.Add() = %v,%w",ok,err) + } + conn,err:=peerTransport.Accept(ctx) + if err!=nil { + return fmt.Errorf("peerTransport.Accept(): %w",err) + } + defer conn.Close() + // Connections should be closed either during handshake, or immediately afterwards. + if _,err:=conn.Handshake(ctx, tc.info, key); err==nil { + return nil + } + if _,_,err:=conn.ReceiveMessage(ctx); !errors.Is(err,context.Canceled) { + return fmt.Errorf("want Canceled, got %w",err) + } + return nil + }); err!=nil { + t.Fatal(err) } - - router.Stop() }) } } func TestRouter_DialPeers_Parallel(t *testing.T) { - t.Cleanup(leaktest.Check(t)) - + logger, _ := log.NewDefaultLogger("plain", "debug") ctx := t.Context() + t.Cleanup(leaktest.Check(t)) - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} - - // Set up a mock transport that returns a connection that blocks during the - // handshake. It should dial all peers in parallel. - dialCh := make(chan bool, 3) - closeCh := make(chan time.Time) - - // Set up and start the router. - peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) - require.NoError(t, err) - - added, err := peerManager.Add(a) - require.NoError(t, err) - require.True(t, added) - - added, err = peerManager.Add(b) - require.NoError(t, err) - require.True(t, added) - - added, err = peerManager.Add(c) - require.NoError(t, err) - require.True(t, added) - - router, err := p2p.NewRouter( - log.NewNopLogger(), - p2p.NopMetrics(), - selfKey, - peerManager, - func() *types.NodeInfo { return &selfInfo }, - mockTransport, - nil, - p2p.RouterOptions{ - DialSleep: func(_ context.Context) error { return nil }, - NumConcurrentDials: func() int { - ncpu := runtime.NumCPU() - if ncpu <= 3 { - return 3 - } - return ncpu - }, - }, - ) - - require.NoError(t, err) - require.NoError(t, router.Start(ctx)) - - require.Eventually(t, - func() bool { - return len(dialCh) == 3 - }, - 5*time.Second, - 100*time.Millisecond, - "reached %d rather than 3", len(dialCh)) - - close(closeCh) - time.Sleep(500 * time.Millisecond) + var keys []crypto.PrivKey + var infos []types.NodeInfo + for range 10 { + key,info := makeKeyAndInfo() + keys = append(keys, key) + infos = append(infos, info) + } - router.Stop() + t.Logf("Set up and start the router.") + h := spawnRouter(t, logger) + sub := h.peerManager.Subscribe(ctx) + + if err := scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { + t.Logf("Accept raw connections.") + var conns []p2p.Connection + for i, info := range infos { + t.Logf("ACCEPT %v %v",i, info.NodeID) + peerTransport := p2p.TestTransport(logger.With("node","peer"), peerID) + s.SpawnBgNamed("peerTransport.Run()",func() error { return peerTransport.Run(ctx) }) + if err:=peerTransport.WaitForStart(ctx); err!=nil { + return fmt.Errorf("peerTransport.WaitForStart(): %w",err) + } + if ok,err := h.peerManager.Add(peerTransport.Endpoint().NodeAddress(info.NodeID)); !ok || err!=nil { + return fmt.Errorf("peerManager.Add() = %v,%w",ok,err) + } + conn,err := peerTransport.Accept(ctx) + if err!=nil { + return fmt.Errorf("peerTransport.Accept(): %w",err) + } + defer conn.Close() + conns = append(conns, conn) + } + t.Logf("Handshake the connections in reverse order.") + for i:=len(conns)-1; i>=0; i-- { + conn := conns[i] + info := infos[i] + if _,err := conn.Handshake(ctx, info, keys[i]); err!=nil { + return fmt.Errorf("conn.Handshake(): %w",err) + } + p2ptest.RequireUpdate(t, sub, p2p.PeerUpdate{ + NodeID: info.NodeID, + Status: p2p.PeerStatusUp, + }) + } + return nil + }); err!=nil { + t.Fatal(err) + } } func TestRouter_EvictPeers(t *testing.T) { - t.Cleanup(leaktest.Check(t)) logger, _ := log.NewDefaultLogger("plain", "debug") - ctx := t.Context() - - // Set up a mock transport that we can evict. - closeCh := make(chan time.Time) - closeOnce := sync.Once{} - - // Set up and start the router. - peerManager, err := p2p.NewPeerManager(logger, selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) - require.NoError(t, err) - - sub := peerManager.Subscribe(ctx) - - router, err := p2p.NewRouter( - logger, - p2p.NopMetrics(), - selfKey, - peerManager, - func() *types.NodeInfo { return &selfInfo }, - nil, - p2p.RouterOptions{}, - ) - require.NoError(t, err) - require.NoError(t, router.Start(ctx)) - - // Wait for the mock peer to connect, then evict it by reporting an error. - p2ptest.RequireUpdate(t, sub, p2p.PeerUpdate{ - NodeID: peerInfo.NodeID, - Status: p2p.PeerStatusUp, - }) - t.Logf("node is up") - peerManager.Errored(peerInfo.NodeID, errors.New("boom")) - - p2ptest.RequireUpdate(t, sub, p2p.PeerUpdate{ - NodeID: peerInfo.NodeID, - Status: p2p.PeerStatusDown, - }) - - router.Stop() -} - -func TestRouter_ChannelCompatability(t *testing.T) { t.Cleanup(leaktest.Check(t)) - ctx := t.Context() + h := spawnRouter(t, logger) + sub := h.peerManager.Subscribe(ctx) + + if err := scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { + key,info := makeKeyAndInfo() + peerTransport := p2p.TestTransport(logger.With("node","peer"), info.NodeID) + s.SpawnBgNamed("peerTransport.Run()",func() error { return peerTransport.Run(ctx) }) + conn,err := peerTransport.Dial(ctx, h.transport.Endpoint()) + if err!=nil { + return fmt.Errorf("peerTransport.Dial(): %w",err) + } + defer conn.Close() + if _,err := conn.Handshake(ctx, info, key); err!=nil { + return fmt.Errorf("conn.Handshake(): %w",err) + } + p2ptest.RequireUpdate(t, sub, p2p.PeerUpdate{ + NodeID: info.NodeID, + Status: p2p.PeerStatusUp, + }) - incompatiblePeer := types.NodeInfo{ - NodeID: peerID, - ListenAddr: "0.0.0.0:0", - Network: "test", - Moniker: string(peerID), - Channels: []byte{0x03}, + t.Log("Report the peer as bad.") + h.peerManager.Errored(info.NodeID, errors.New("boom")) + p2ptest.RequireUpdate(t, sub, p2p.PeerUpdate{ + NodeID: info.NodeID, + Status: p2p.PeerStatusDown, + }) + if _,_,err:=conn.ReceiveMessage(ctx); !errors.Is(err,context.Canceled) { + return fmt.Errorf("want Canceled, got %w",err) + } + return nil + }); err!=nil { + t.Fatal(err) } - - mockConnection := &mocks.Connection{} - mockConnection.On("String").Maybe().Return("mock") - mockConnection.On("Handshake", mock.Anything, selfInfo, selfKey). - Return(incompatiblePeer, nil) - mockConnection.On("RemoteEndpoint").Return(p2p.Endpoint{}) - mockConnection.On("Close").Return(nil) - - mockTransport := &mocks.Transport{} - mockTransport.On("Run", mock.Anything).Return(nil) - mockTransport.On("Accept", mock.Anything).Once().Return(mockConnection, nil) - mockTransport.On("Accept", mock.Anything).Once().Return(nil, context.Canceled) - - // Set up and start the router. - peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) - require.NoError(t, err) - - router, err := p2p.NewRouter( - log.NewNopLogger(), - p2p.NopMetrics(), - selfKey, - peerManager, - func() *types.NodeInfo { return &selfInfo }, - mockTransport, - nil, - p2p.RouterOptions{}, - ) - require.NoError(t, err) - require.NoError(t, router.Start(ctx)) - time.Sleep(1 * time.Second) - router.Stop() - require.Empty(t, peerManager.Peers()) - - mockConnection.AssertExpectations(t) - mockTransport.AssertExpectations(t) } -func TestRouter_DontSendOnInvalidChannel(t *testing.T) { - t.Cleanup(leaktest.Check(t)) - ctx := t.Context() - - peer := types.NodeInfo{ - NodeID: peerID, - ListenAddr: "0.0.0.0:0", - Network: "test", - Moniker: string(peerID), - Channels: []byte{0x02}, +func makeChDesc(id p2p.ChannelID) *p2p.ChannelDescriptor { + return &p2p.ChannelDescriptor{ + ID: id, + MessageType: &p2ptest.Message{}, + Priority: 5, + SendQueueCapacity: 10, + RecvBufferCapacity: 10, + RecvMessageCapacity: 10, } - - mockConnection := &mocks.Connection{} - mockConnection.On("String").Maybe().Return("mock") - mockConnection.On("Handshake", mock.Anything, selfInfo, selfKey). - Return(peer, nil) - mockConnection.On("RemoteEndpoint").Return(p2p.Endpoint{}) - mockConnection.On("Close").Return(nil) - mockConnection.On("ReceiveMessage", mock.Anything).Return(chID, nil, io.EOF) - - mockTransport := &mocks.Transport{} - mockTransport.On("AddChannelDescriptors", mock.Anything).Return() - mockTransport.On("Accept", mock.Anything).Once().Return(mockConnection, nil) - mockTransport.On("Accept", mock.Anything).Maybe().Return(nil, context.Canceled) - mockTransport.On("Run", mock.Anything).Return(nil) - - // Set up and start the router. - peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) - require.NoError(t, err) - - sub := peerManager.Subscribe(ctx) - - router, err := p2p.NewRouter( - log.NewNopLogger(), - p2p.NopMetrics(), - selfKey, - peerManager, - func() *types.NodeInfo { return &selfInfo }, - mockTransport, - nil, - p2p.RouterOptions{}, - ) - require.NoError(t, err) - require.NoError(t, router.Start(ctx)) - - p2ptest.RequireUpdate(t, sub, p2p.PeerUpdate{ - NodeID: peerInfo.NodeID, - Status: p2p.PeerStatusUp, - }) - - channel, err := router.OpenChannel(chDesc) - require.NoError(t, err) - - require.NoError(t, channel.Send(ctx, p2p.Envelope{ - To: peer.NodeID, - Message: &p2ptest.Message{Value: "Hi"}, - })) - - router.Stop() - mockTransport.AssertExpectations(t) } -func TestRouter_Channel_FilterByID(t *testing.T) { +func TestRouter_DontSendOnInvalidChannel(t *testing.T) { + logger, _ := log.NewDefaultLogger("plain", "debug") + ctx := t.Context() t.Cleanup(leaktest.Check(t)) + h := spawnRouter(t, logger) + sub := h.peerManager.Subscribe(ctx) - // Set up a mock transport that handshakes. - connCtx, connCancel := context.WithCancel(t.Context()) - defer connCancel() - peer := types.NodeInfo{ - NodeID: peerID, - ListenAddr: "0.0.0.0:0", - Network: "test", - Moniker: string(peerID), - Channels: []byte{0x02}, - } - - mockConnection := &mocks.Connection{} - mockConnection.On("String").Maybe().Return("mock") - mockConnection.On("Handshake", mock.Anything, selfInfo, selfKey). - Return(peer, nil) - mockConnection.On("RemoteEndpoint").Return(p2p.Endpoint{}) - mockConnection.On("Close").Return(nil) - mockConnection.On("ReceiveMessage", mock.Anything).Return(chID, nil, io.EOF) - - mockTransport := &mocks.Transport{} - mockTransport.On("AddChannelDescriptors", mock.Anything).Return() - mockTransport.On("String").Maybe().Return("mock") - mockTransport.On("Accept", mock.Anything).Once().Return(mockConnection, nil) - mockTransport.On("Accept", mock.Anything).Maybe().Return(nil, context.Canceled) - mockTransport.On("Run", mock.Anything).Return(nil) - - peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) + desc1 := makeChDesc(1) + ch1, err := h.router.OpenChannel(desc1) require.NoError(t, err) - // no filter - router, err := p2p.NewRouter( - log.NewNopLogger(), - p2p.NopMetrics(), - selfKey, - peerManager, - func() *types.NodeInfo { return &selfInfo }, - mockTransport, - nil, - p2p.RouterOptions{}, - ) + desc2 := makeChDesc(2) + ch2, err := h.router.OpenChannel(desc2) require.NoError(t, err) - require.NoError(t, router.Start(connCtx)) - t.Cleanup(router.Wait) - - time.Sleep(1 * time.Second) - - require.Equal(t, 1, len(peerManager.Peers())) - - peerManager, err = p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) - require.NoError(t, err) - - // with filter - router, err = p2p.NewRouter( - log.NewNopLogger(), - p2p.NopMetrics(), - selfKey, - peerManager, - func() *types.NodeInfo { return &selfInfo }, - mockTransport, - func(_ context.Context, _ types.NodeID) error { return errors.New("should filter") }, - p2p.RouterOptions{}, - ) - require.NoError(t, err) - - require.NoError(t, router.Start(connCtx)) - t.Cleanup(router.Wait) - - time.Sleep(1 * time.Second) - - require.Equal(t, 0, len(peerManager.Peers())) + if err := scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { + key,info := makeKeyAndInfo() + info.Channels = []byte{byte(desc1.ID)} + peerTransport := p2p.TestTransport(logger.With("node","peer"), info.NodeID, desc1) + s.SpawnBgNamed("peerTransport.Run()",func() error { return peerTransport.Run(ctx) }) + conn,err := peerTransport.Dial(ctx, h.transport.Endpoint()) + if err!=nil { + return fmt.Errorf("peerTransport.Dial(): %w",err) + } + defer conn.Close() + if _,err := conn.Handshake(ctx, info, key); err!=nil { + return fmt.Errorf("conn.Handshake(): %w",err) + } + p2ptest.RequireUpdate(t, sub, p2p.PeerUpdate{ + NodeID: info.NodeID, + Status: p2p.PeerStatusUp, + }) + n := 1 + msg1 := &p2ptest.Message{Value: "Hello"} + msg2 := &p2ptest.Message{Value: "Hello2"} + t.Log("Broadcast messages of both channels.") + s.Spawn(func() error { + for range n { + if err:=ch1.Send(ctx, p2p.Envelope{Broadcast:true, Message: msg1}); err!=nil { + return fmt.Errorf("ch1.Send(): %w",err) + } + if err:=ch2.Send(ctx, p2p.Envelope{Broadcast:true, Message: msg2}); err!=nil { + return fmt.Errorf("ch2.Send(): %w",err) + } + } + return nil + }) + t.Log("Expect messages of 1 channel only.") + for range n { + gotChID,gotMsg,err:=conn.ReceiveMessage(ctx) + if err!=nil { + return fmt.Errorf("ReceiveMessage(): %w",err) + } + if gotChID!=desc1.ID { + return fmt.Errorf("gotChID = %v, want %v", gotChID, desc1.ID) + } + got := proto.Clone(desc1.MessageType) + if err := proto.Unmarshal(gotMsg, got); err != nil { + return fmt.Errorf("Unmarshal: %w", err) + } + if err := utils.TestDiff[proto.Message](got,msg1); err!=nil { + return fmt.Errorf("gotMsg: %v", err) + } + } + return nil + }); err!=nil { + t.Fatal(err) + } } diff --git a/internal/p2p/transport.go b/internal/p2p/transport.go index cc6866916..fce29a164 100644 --- a/internal/p2p/transport.go +++ b/internal/p2p/transport.go @@ -38,12 +38,12 @@ type TransportOptions struct { } // TestTransport creates a new Transport for tests. -func TestTransport(logger log.Logger, nodeID types.NodeID) *Transport { +func TestTransport(logger log.Logger, nodeID types.NodeID, descs ...*ChannelDescriptor) *Transport { return NewTransport( logger.With("local", nodeID), Endpoint{tcp.TestReserveAddr()}, conn.DefaultMConnConfig(), - []*ChannelDescriptor{}, + descs, TransportOptions{}, ) } From fedfcf3e25c50f89c41f07ead86ec578cf873e80 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Wed, 1 Oct 2025 14:10:45 +0200 Subject: [PATCH 4/9] wtf --- internal/p2p/peermanager_test.go | 169 +++++++++++++++++-------------- internal/p2p/pex/reactor_test.go | 5 +- internal/p2p/transport_test.go | 5 - 3 files changed, 96 insertions(+), 83 deletions(-) diff --git a/internal/p2p/peermanager_test.go b/internal/p2p/peermanager_test.go index bfe04c00f..e8fd2080a 100644 --- a/internal/p2p/peermanager_test.go +++ b/internal/p2p/peermanager_test.go @@ -1,6 +1,7 @@ package p2p_test import ( + "fmt" "context" "errors" "math" @@ -19,6 +20,16 @@ import ( "github.com/tendermint/tendermint/types" ) +func testAddr(x string) p2p.NodeAddress { + if len(x)!=1 { panic("x must be a single character") } + return p2p.NodeAddress{ + NodeID: types.NodeID(strings.Repeat(x, 40)), + Hostname: fmt.Sprintf("%v.com",x), + Port: 26657, + } +} + + // FIXME: We should probably have some randomized property-based tests for the // PeerManager too, which runs a bunch of random operations with random peers // and ensures certain invariants always hold. The logic can be complex, with @@ -258,8 +269,8 @@ func TestNewPeerManager_Unconditional(t *testing.T) { } func TestNewPeerManager_SelfIDChange(t *testing.T) { - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + a := testAddr("a") + b := testAddr("b") db := dbm.NewMemDB() peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, db, p2p.PeerManagerOptions{}, p2p.NopMetrics()) @@ -332,7 +343,7 @@ func TestPeerManager_Add(t *testing.T) { func TestPeerManager_DialNext(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + a := testAddr("a") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -357,7 +368,7 @@ func TestPeerManager_DialNext(t *testing.T) { func TestPeerManager_DialNext_Retry(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + a := testAddr("a") options := p2p.PeerManagerOptions{ MinRetryTime: 100 * time.Millisecond, @@ -391,7 +402,7 @@ func TestPeerManager_DialNext_Retry(t *testing.T) { func TestPeerManagerDeleteOnMaxRetries(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + a := testAddr("a") options := p2p.PeerManagerOptions{ MinRetryTime: 100 * time.Millisecond, @@ -437,8 +448,8 @@ func TestPeerManager_DialNext_WakeOnDialFailed(t *testing.T) { }, p2p.NopMetrics()) require.NoError(t, err) - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + a := testAddr("a") + b := testAddr("b") // Add and dial a. added, err := peerManager.Add(a) @@ -480,7 +491,7 @@ func TestPeerManager_DialNext_WakeOnDialFailedRetry(t *testing.T) { peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), options, p2p.NopMetrics()) require.NoError(t, err) - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + a := testAddr("a") // Add a, dial it, and mark it a failure. This will start a retry timer. added, err := peerManager.Add(a) @@ -505,7 +516,7 @@ func TestPeerManager_DialNext_WakeOnDialFailedRetry(t *testing.T) { func TestPeerManager_DialNext_WakeOnDisconnected(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + a := testAddr("a") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -535,9 +546,9 @@ func TestPeerManager_DialNext_WakeOnDisconnected(t *testing.T) { } func TestPeerManager_TryDialNext_MaxConnected(t *testing.T) { - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} + a := testAddr("a") + b := testAddr("b") + c := testAddr("c") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 2, @@ -573,11 +584,11 @@ func TestPeerManager_TryDialNext_MaxConnected(t *testing.T) { func TestPeerManager_TryDialNext_MaxConnectedUpgrade(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} - d := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("d", 40))} - e := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("e", 40))} + a := testAddr("a") + b := testAddr("b") + c := testAddr("c") + d := testAddr("d") + e := testAddr("e") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ PeerScores: map[types.NodeID]p2p.PeerScore{ @@ -657,9 +668,9 @@ func TestPeerManager_TryDialNext_MaxConnectedUpgrade(t *testing.T) { } func TestPeerManager_TryDialNext_UpgradeReservesPeer(t *testing.T) { - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} + a := testAddr("a") + b := testAddr("b") + c := testAddr("c") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ PeerScores: map[types.NodeID]p2p.PeerScore{b.NodeID: p2p.DefaultMutableScore + 1, c.NodeID: p2p.DefaultMutableScore + 1}, @@ -820,9 +831,9 @@ func TestPeerManager_DialFailed(t *testing.T) { func TestPeerManager_DialFailed_UnreservePeer(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} + a := testAddr("a") + b := testAddr("b") + c := testAddr("c") logger, _ := log.NewDefaultLogger("plain", "debug") peerManager, err := p2p.NewPeerManager(logger, selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ @@ -867,8 +878,8 @@ func TestPeerManager_DialFailed_UnreservePeer(t *testing.T) { } func TestPeerManager_Dialed_Connected(t *testing.T) { - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + a := testAddr("a") + b := testAddr("b") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -901,14 +912,14 @@ func TestPeerManager_Dialed_Self(t *testing.T) { require.NoError(t, err) // Dialing self should error. - added, err := peerManager.Add(p2p.NodeAddress{NodeID: selfID}) + added, err := peerManager.Add(p2p.NodeAddress{NodeID: selfID, Hostname: "a.com", Port: 1234}) require.Nil(t, err) require.False(t, added) } func TestPeerManager_Dialed_MaxConnected(t *testing.T) { - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + a := testAddr("a") + b := testAddr("b") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 1, @@ -935,10 +946,10 @@ func TestPeerManager_Dialed_MaxConnected(t *testing.T) { } func TestPeerManager_Dialed_MaxConnectedUpgrade(t *testing.T) { - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} - d := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("d", 40))} + a := testAddr("a") + b := testAddr("b") + c := testAddr("c") + d := testAddr("d") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 2, @@ -987,7 +998,7 @@ func TestPeerManager_Dialed_MaxConnectedUpgrade(t *testing.T) { } func TestPeerManager_Dialed_Unknown(t *testing.T) { - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + a := testAddr("a") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -997,9 +1008,9 @@ func TestPeerManager_Dialed_Unknown(t *testing.T) { } func TestPeerManager_Dialed_Upgrade(t *testing.T) { - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} + a := testAddr("a") + b := testAddr("b") + c := testAddr("c") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 1, @@ -1043,10 +1054,10 @@ func TestPeerManager_Dialed_Upgrade(t *testing.T) { func TestPeerManager_Dialed_UpgradeEvenLower(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} - d := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("d", 40))} + a := testAddr("a") + b := testAddr("b") + c := testAddr("c") + d := testAddr("d") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 2, @@ -1101,9 +1112,9 @@ func TestPeerManager_Dialed_UpgradeEvenLower(t *testing.T) { func TestPeerManager_Dialed_UpgradeNoEvict(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} + a := testAddr("a") + b := testAddr("b") + c := testAddr("c") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 2, @@ -1148,10 +1159,10 @@ func TestPeerManager_Dialed_UpgradeNoEvict(t *testing.T) { } func TestPeerManager_Accepted(t *testing.T) { - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} - d := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("d", 40))} + a := testAddr("a") + b := testAddr("b") + c := testAddr("c") + d := testAddr("d") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1195,9 +1206,9 @@ func TestPeerManager_Accepted(t *testing.T) { } func TestPeerManager_Accepted_MaxConnected(t *testing.T) { - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} + a := testAddr("a") + b := testAddr("b") + c := testAddr("c") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 2, @@ -1223,10 +1234,10 @@ func TestPeerManager_Accepted_MaxConnected(t *testing.T) { } func TestPeerManager_Accepted_MaxConnectedUpgrade(t *testing.T) { - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} - d := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("d", 40))} + a := testAddr("a") + b := testAddr("b") + c := testAddr("c") + d := testAddr("d") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ PeerScores: map[types.NodeID]p2p.PeerScore{ @@ -1271,9 +1282,9 @@ func TestPeerManager_Accepted_MaxConnectedUpgrade(t *testing.T) { func TestPeerManager_Accepted_Upgrade(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} + a := testAddr("a") + b := testAddr("b") + c := testAddr("c") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ PeerScores: map[types.NodeID]p2p.PeerScore{ @@ -1316,9 +1327,9 @@ func TestPeerManager_Accepted_Upgrade(t *testing.T) { } func TestPeerManager_Accepted_UpgradeDialing(t *testing.T) { - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} - c := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("c", 40))} + a := testAddr("a") + b := testAddr("b") + c := testAddr("c") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ PeerScores: map[types.NodeID]p2p.PeerScore{ @@ -1365,8 +1376,8 @@ func TestPeerManager_Accepted_UpgradeDialing(t *testing.T) { } func TestPeerManager_Ready(t *testing.T) { - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + a := testAddr("a") + b := testAddr("b") ctx := t.Context() @@ -1408,7 +1419,7 @@ func TestPeerManager_Ready_Channels(t *testing.T) { sub := pm.Subscribe(ctx) - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + a := testAddr("a") added, err := pm.Add(a) require.NoError(t, err) require.True(t, added) @@ -1426,7 +1437,7 @@ func TestPeerManager_Ready_Channels(t *testing.T) { func TestPeerManager_EvictNext(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + a := testAddr("a") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1461,7 +1472,7 @@ func TestPeerManager_EvictNext(t *testing.T) { func TestPeerManager_EvictNext_WakeOnError(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + a := testAddr("a") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1489,8 +1500,8 @@ func TestPeerManager_EvictNext_WakeOnError(t *testing.T) { func TestPeerManager_EvictNext_WakeOnUpgradeDialed(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + a := testAddr("a") + b := testAddr("b") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 1, @@ -1529,8 +1540,8 @@ func TestPeerManager_EvictNext_WakeOnUpgradeDialed(t *testing.T) { func TestPeerManager_EvictNext_WakeOnUpgradeAccepted(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} - b := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("b", 40))} + a := testAddr("a") + b := testAddr("b") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 1, @@ -1562,7 +1573,11 @@ func TestPeerManager_EvictNext_WakeOnUpgradeAccepted(t *testing.T) { func TestPeerManager_TryEvictNext(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + a := p2p.NodeAddress{ + NodeID: types.NodeID(strings.Repeat("a", 40)), + Hostname: "a.com", + Port: 26657, + } peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1600,7 +1615,7 @@ func TestPeerManager_TryEvictNext(t *testing.T) { } func TestPeerManager_Disconnected(t *testing.T) { - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + a := testAddr("a") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1657,7 +1672,7 @@ func TestPeerManager_Disconnected(t *testing.T) { func TestPeerManager_Errored(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + a := testAddr("a") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1697,7 +1712,7 @@ func TestPeerManager_Errored(t *testing.T) { func TestPeerManager_Subscribe(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + a := testAddr("a") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1761,7 +1776,7 @@ func TestPeerManager_Subscribe(t *testing.T) { func TestPeerManager_Subscribe_Close(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + a := testAddr("a") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1790,7 +1805,7 @@ func TestPeerManager_Subscribe_Broadcast(t *testing.T) { t.Cleanup(leaktest.Check(t)) - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + a := testAddr("a") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -1837,7 +1852,7 @@ func TestPeerManager_Close(t *testing.T) { ctx := t.Context() - a := p2p.NodeAddress{NodeID: types.NodeID(strings.Repeat("a", 40))} + a := testAddr("a") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MinRetryTime: 10 * time.Second, @@ -1903,7 +1918,7 @@ func TestPeerManager_Advertise(t *testing.T) { // Asking for 2 addresses should get the highest-rated ones, i.e. a. require.ElementsMatch(t, []p2p.NodeAddress{ aTCP, - }, peerManager.Advertise(dID, 2)) + }, peerManager.Advertise(dID, 1)) } func TestPeerManager_Advertise_Self(t *testing.T) { diff --git a/internal/p2p/pex/reactor_test.go b/internal/p2p/pex/reactor_test.go index 85fb0050c..751f24d0a 100644 --- a/internal/p2p/pex/reactor_test.go +++ b/internal/p2p/pex/reactor_test.go @@ -7,6 +7,7 @@ import ( "strings" "testing" "time" + "net/netip" "github.com/stretchr/testify/require" dbm "github.com/tendermint/tm-db" @@ -146,7 +147,9 @@ func TestReactorErrorsOnReceivingTooManyPeers(t *testing.T) { ctx := t.Context() r := setupSingle(ctx, t) - peer := p2p.NodeAddress{NodeID: randomNodeID()} + peer := p2p.Endpoint{ + AddrPort: netip.AddrPortFrom(netip.IPv6Loopback(),1234), + }.NodeAddress(randomNodeID()) added, err := r.manager.Add(peer) require.NoError(t, err) require.True(t, added) diff --git a/internal/p2p/transport_test.go b/internal/p2p/transport_test.go index ccab7e169..d1c41cc8d 100644 --- a/internal/p2p/transport_test.go +++ b/internal/p2p/transport_test.go @@ -525,10 +525,6 @@ func TestEndpoint_NodeAddress(t *testing.T) { p2p.Endpoint{netip.AddrPortFrom(ip6, 8080)}, p2p.NodeAddress{Hostname: "b10c::1", Port: 8080}, }, - - // Partial (invalid) endpoints. - {p2p.Endpoint{}, p2p.NodeAddress{}}, - {p2p.Endpoint{netip.AddrPortFrom(ip4, 0)}, p2p.NodeAddress{Hostname: "1.2.3.4"}}, } for _, tc := range testcases { t.Run(tc.endpoint.String(), func(t *testing.T) { @@ -558,7 +554,6 @@ func TestEndpoint_Validate(t *testing.T) { // Invalid endpoints. {p2p.Endpoint{}, false}, - {p2p.Endpoint{netip.AddrPortFrom(ip4, 0)}, false}, } for _, tc := range testcases { t.Run(tc.endpoint.String(), func(t *testing.T) { From 580b3bcd4637cc6a497a05541d6db0abdc01074f Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Wed, 1 Oct 2025 15:57:42 +0200 Subject: [PATCH 5/9] fmt --- internal/blocksync/pool_test.go | 2 +- internal/p2p/address_test.go | 16 +- internal/p2p/peermanager_scoring_test.go | 63 +++--- internal/p2p/peermanager_test.go | 49 +++-- internal/p2p/pex/reactor_test.go | 4 +- internal/p2p/router.go | 2 +- internal/p2p/router_test.go | 235 ++++++++++++----------- internal/p2p/transport.go | 7 +- internal/p2p/transport_test.go | 9 +- node/setup.go | 4 +- 10 files changed, 194 insertions(+), 197 deletions(-) diff --git a/internal/blocksync/pool_test.go b/internal/blocksync/pool_test.go index 45e16c83b..816b1e319 100644 --- a/internal/blocksync/pool_test.go +++ b/internal/blocksync/pool_test.go @@ -95,7 +95,7 @@ func makePeerManager(peers map[types.NodeID]testPeer) *p2p.PeerManager { MaxConnectedUpgrade: 2, }, p2p.NopMetrics()) for nodeId := range peers { - _, err := peerManager.Add(p2p.NodeAddress{Protocol: "memory", NodeID: nodeId}) + _, err := peerManager.Add(p2p.NodeAddress{NodeID: nodeId, Hostname: "a.com", Port: 1234}) peerManager.MarkReadyConnected(nodeId) if err != nil { panic(err) diff --git a/internal/p2p/address_test.go b/internal/p2p/address_test.go index 5a58b76b9..fee0dcfa1 100644 --- a/internal/p2p/address_test.go +++ b/internal/p2p/address_test.go @@ -216,7 +216,6 @@ func TestNodeAddress_Resolve(t *testing.T) { // Invalid addresses. {p2p.NodeAddress{}, p2p.Endpoint{}, false}, - {p2p.NodeAddress{Hostname: "127.0.0.1"}, p2p.Endpoint{}, false}, {p2p.NodeAddress{Hostname: "127.0.0.1:80"}, p2p.Endpoint{}, false}, {p2p.NodeAddress{Hostname: "💥"}, p2p.Endpoint{}, false}, } @@ -268,23 +267,12 @@ func TestNodeAddress_String(t *testing.T) { p2p.NodeAddress{NodeID: id, Hostname: "host.domain"}, "mconn://" + user + "@host.domain", }, - { - p2p.NodeAddress{NodeID: id, Hostname: "host", Port: 80}, - "mconn://" + user + "@host:80/foo/bar", - }, // Addresses with weird contents, which are technically fine (not harmful). { p2p.NodeAddress{NodeID: "👨", Hostname: "💻", Port: 80}, - "%F0%9F%91%A8@%F0%9F%92%BB:80", + "mconn://%F0%9F%91%A8@%F0%9F%92%BB:80", }, - - // Partial (invalid) addresses. - {p2p.NodeAddress{}, ""}, - {p2p.NodeAddress{NodeID: id}, user + "@"}, - {p2p.NodeAddress{Hostname: "host"}, "host"}, - {p2p.NodeAddress{Port: 80}, ""}, - {p2p.NodeAddress{NodeID: id, Port: 80}, user + "@"}, } for _, tc := range testcases { t.Run(tc.address.String(), func(t *testing.T) { @@ -302,12 +290,10 @@ func TestNodeAddress_Validate(t *testing.T) { // Valid addresses. {p2p.NodeAddress{NodeID: id, Hostname: "host", Port: 80}, true}, {p2p.NodeAddress{NodeID: id, Hostname: "host"}, true}, - {p2p.NodeAddress{NodeID: id}, true}, // Invalid addresses. {p2p.NodeAddress{}, false}, {p2p.NodeAddress{NodeID: "foo", Hostname: "host"}, false}, - {p2p.NodeAddress{NodeID: id}, true}, {p2p.NodeAddress{NodeID: id, Port: 80}, false}, } for _, tc := range testcases { diff --git a/internal/p2p/peermanager_scoring_test.go b/internal/p2p/peermanager_scoring_test.go index fb007bafe..48eca42f9 100644 --- a/internal/p2p/peermanager_scoring_test.go +++ b/internal/p2p/peermanager_scoring_test.go @@ -1,6 +1,7 @@ package p2p import ( + "fmt" "strings" "testing" "time" @@ -14,6 +15,17 @@ import ( "github.com/tendermint/tendermint/types" ) +func testAddr(x string) NodeAddress { + if len(x) != 1 { + panic("x must be a single character") + } + return NodeAddress{ + NodeID: types.NodeID(strings.Repeat(x, 40)), + Hostname: fmt.Sprintf("%v.com", x), + Port: 26657, + } +} + func TestPeerScoring(t *testing.T) { // coppied from p2p_test shared variables selfKey := ed25519.GenPrivKeyFromSecret([]byte{0xf9, 0x1b, 0x08, 0xaa, 0x38, 0xee, 0x34, 0xdd}) @@ -25,8 +37,8 @@ func TestPeerScoring(t *testing.T) { require.NoError(t, err) // create a fake node - id := types.NodeID(strings.Repeat("a1", 20)) - added, err := peerManager.Add(NodeAddress{NodeID: id}) + a := testAddr("a") + added, err := peerManager.Add(a) require.NoError(t, err) require.True(t, added) @@ -35,77 +47,76 @@ func TestPeerScoring(t *testing.T) { t.Run("Synchronous", func(t *testing.T) { // update the manager and make sure it's correct defaultScore := DefaultMutableScore - require.EqualValues(t, defaultScore, peerManager.Scores()[id]) + require.EqualValues(t, defaultScore, peerManager.Scores()[a.NodeID]) // add a bunch of good status updates and watch things increase. for i := 1; i < 10; i++ { peerManager.processPeerEvent(ctx, PeerUpdate{ - NodeID: id, + NodeID: a.NodeID, Status: PeerStatusGood, }) - require.EqualValues(t, defaultScore+PeerScore(i), peerManager.Scores()[id]) + require.EqualValues(t, defaultScore+PeerScore(i), peerManager.Scores()[a.NodeID]) } // watch the corresponding decreases respond to update for i := 1; i < 10; i++ { peerManager.processPeerEvent(ctx, PeerUpdate{ - NodeID: id, + NodeID: a.NodeID, Status: PeerStatusBad, }) - require.EqualValues(t, DefaultMutableScore+PeerScore(9)-PeerScore(i), peerManager.Scores()[id]) + require.EqualValues(t, DefaultMutableScore+PeerScore(9)-PeerScore(i), peerManager.Scores()[a.NodeID]) } // Dial failure should decrease score - addr := NodeAddress{NodeID: id} - _ = peerManager.DialFailed(ctx, addr) - _ = peerManager.DialFailed(ctx, addr) - _ = peerManager.DialFailed(ctx, addr) - require.EqualValues(t, DefaultMutableScore-2, peerManager.Scores()[id]) + _ = peerManager.DialFailed(ctx, a) + _ = peerManager.DialFailed(ctx, a) + _ = peerManager.DialFailed(ctx, a) + require.EqualValues(t, DefaultMutableScore-2, peerManager.Scores()[a.NodeID]) // Disconnect every 3 times should also decrease score for i := 1; i < 7; i++ { - peerManager.Disconnected(ctx, id) + peerManager.Disconnected(ctx, a.NodeID) } - require.EqualValues(t, DefaultMutableScore-2, peerManager.Scores()[id]) + require.EqualValues(t, DefaultMutableScore-2, peerManager.Scores()[a.NodeID]) }) t.Run("AsynchronousIncrement", func(t *testing.T) { - start := peerManager.Scores()[id] + start := peerManager.Scores()[a.NodeID] pu := peerManager.Subscribe(ctx) pu.SendUpdate(ctx, PeerUpdate{ - NodeID: id, + NodeID: a.NodeID, Status: PeerStatusGood, }) require.Eventually(t, - func() bool { return start+1 == peerManager.Scores()[id] }, + func() bool { return start+1 == peerManager.Scores()[a.NodeID] }, time.Second, time.Millisecond, - "startAt=%d score=%d", start, peerManager.Scores()[id]) + "startAt=%d score=%d", start, peerManager.Scores()[a.NodeID]) }) t.Run("AsynchronousDecrement", func(t *testing.T) { - start := peerManager.Scores()[id] + start := peerManager.Scores()[a.NodeID] pu := peerManager.Subscribe(ctx) pu.SendUpdate(ctx, PeerUpdate{ - NodeID: id, + NodeID: a.NodeID, Status: PeerStatusBad, }) require.Eventually(t, - func() bool { return start-1 == peerManager.Scores()[id] }, + func() bool { return start-1 == peerManager.Scores()[a.NodeID] }, time.Second, time.Millisecond, - "startAt=%d score=%d", start, peerManager.Scores()[id]) + "startAt=%d score=%d", start, peerManager.Scores()[a.NodeID]) }) t.Run("TestNonPersistantPeerUpperBound", func(t *testing.T) { // Reset peer state to remove any previous penalties - peerManager.store.peers[id] = &peerInfo{ - ID: id, + peerManager.store.peers[a.NodeID] = &peerInfo{ + ID: a.NodeID, MutableScore: DefaultMutableScore, } // Add successful blocks to increase score for i := 0; i < 100; i++ { - peerManager.IncrementBlockSyncs(id) + peerManager.IncrementBlockSyncs(a.NodeID) } // Score should be capped at MaxPeerScoreNotPersistent - require.EqualValues(t, MaxPeerScoreNotPersistent, peerManager.Scores()[id]) + require.EqualValues(t, MaxPeerScoreNotPersistent, peerManager.Scores()[a.NodeID]) }) } diff --git a/internal/p2p/peermanager_test.go b/internal/p2p/peermanager_test.go index e8fd2080a..e993cc60d 100644 --- a/internal/p2p/peermanager_test.go +++ b/internal/p2p/peermanager_test.go @@ -1,9 +1,9 @@ package p2p_test import ( - "fmt" "context" "errors" + "fmt" "math" "strings" "testing" @@ -21,15 +21,16 @@ import ( ) func testAddr(x string) p2p.NodeAddress { - if len(x)!=1 { panic("x must be a single character") } + if len(x) != 1 { + panic("x must be a single character") + } return p2p.NodeAddress{ - NodeID: types.NodeID(strings.Repeat(x, 40)), - Hostname: fmt.Sprintf("%v.com",x), - Port: 26657, + NodeID: types.NodeID(strings.Repeat(x, 40)), + Hostname: fmt.Sprintf("%v.com", x), + Port: 26657, } } - // FIXME: We should probably have some randomized property-based tests for the // PeerManager too, which runs a bunch of random operations with random peers // and ensures certain invariants always hold. The logic can be complex, with @@ -707,12 +708,10 @@ func TestPeerManager_TryDialNext_UpgradeReservesPeer(t *testing.T) { } func TestPeerManager_TryDialNext_DialingConnected(t *testing.T) { - aID := types.NodeID(strings.Repeat("a", 40)) - a := p2p.NodeAddress{NodeID: aID} - aTCP := p2p.NodeAddress{NodeID: aID, Hostname: "localhost"} - - bID := types.NodeID(strings.Repeat("b", 40)) - b := p2p.NodeAddress{NodeID: bID} + a := testAddr("a") + a2 := a + a2.Hostname = "else.com" + b := testAddr("b") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ MaxConnected: 2, @@ -728,7 +727,7 @@ func TestPeerManager_TryDialNext_DialingConnected(t *testing.T) { require.Equal(t, a, dial) // Adding a's TCP address will not dispense a, since it's already dialing. - added, err = peerManager.Add(aTCP) + added, err = peerManager.Add(a2) require.NoError(t, err) require.True(t, added) dial, err = peerManager.TryDialNext() @@ -745,7 +744,7 @@ func TestPeerManager_TryDialNext_DialingConnected(t *testing.T) { added, err = peerManager.Add(b) require.NoError(t, err) require.True(t, added) - require.NoError(t, peerManager.Accepted(bID)) + require.NoError(t, peerManager.Accepted(b.NodeID)) dial, err = peerManager.TryDialNext() require.NoError(t, err) require.Zero(t, dial) @@ -757,8 +756,6 @@ func TestPeerManager_TryDialNext_Multiple(t *testing.T) { aID := types.NodeID(strings.Repeat("a", 40)) bID := types.NodeID(strings.Repeat("b", 40)) addresses := []p2p.NodeAddress{ - {NodeID: aID}, - {NodeID: bID}, {NodeID: aID, Hostname: "127.0.0.1"}, {NodeID: bID, Hostname: "::1"}, } @@ -791,10 +788,8 @@ func TestPeerManager_TryDialNext_Multiple(t *testing.T) { func TestPeerManager_DialFailed(t *testing.T) { // DialFailed is tested through other tests, we'll just check a few basic // things here, e.g. reporting unknown addresses. - aID := types.NodeID(strings.Repeat("a", 40)) - a := p2p.NodeAddress{NodeID: aID} - bID := types.NodeID(strings.Repeat("b", 40)) - b := p2p.NodeAddress{NodeID: bID} + a := testAddr("a") + b := testAddr("b") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{}, p2p.NopMetrics()) require.NoError(t, err) @@ -812,8 +807,8 @@ func TestPeerManager_DialFailed(t *testing.T) { require.NoError(t, err) require.Equal(t, a, dial) require.NoError(t, peerManager.DialFailed(ctx, p2p.NodeAddress{ - NodeID: aID, Hostname: "localhost"})) - require.Equal(t, []p2p.NodeAddress{a}, peerManager.Addresses(aID)) + NodeID: a.NodeID, Hostname: "localhost"})) + require.Equal(t, []p2p.NodeAddress{a}, peerManager.Addresses(a.NodeID)) dial, err = peerManager.TryDialNext() require.NoError(t, err) @@ -825,7 +820,7 @@ func TestPeerManager_DialFailed(t *testing.T) { // DialFailed on an unknown peer shouldn't error or add it. require.NoError(t, peerManager.DialFailed(ctx, b)) - require.Equal(t, []types.NodeID{aID}, peerManager.Peers()) + require.Equal(t, []types.NodeID{a.NodeID}, peerManager.Peers()) } func TestPeerManager_DialFailed_UnreservePeer(t *testing.T) { @@ -946,7 +941,7 @@ func TestPeerManager_Dialed_MaxConnected(t *testing.T) { } func TestPeerManager_Dialed_MaxConnectedUpgrade(t *testing.T) { - a := testAddr("a") + a := testAddr("a") b := testAddr("b") c := testAddr("c") d := testAddr("d") @@ -1008,7 +1003,7 @@ func TestPeerManager_Dialed_Unknown(t *testing.T) { } func TestPeerManager_Dialed_Upgrade(t *testing.T) { - a := testAddr("a") + a := testAddr("a") b := testAddr("b") c := testAddr("c") @@ -1113,7 +1108,7 @@ func TestPeerManager_Dialed_UpgradeNoEvict(t *testing.T) { ctx := t.Context() a := testAddr("a") - b := testAddr("b") + b := testAddr("b") c := testAddr("c") peerManager, err := p2p.NewPeerManager(log.NewNopLogger(), selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{ @@ -1574,7 +1569,7 @@ func TestPeerManager_TryEvictNext(t *testing.T) { ctx := t.Context() a := p2p.NodeAddress{ - NodeID: types.NodeID(strings.Repeat("a", 40)), + NodeID: types.NodeID(strings.Repeat("a", 40)), Hostname: "a.com", Port: 26657, } diff --git a/internal/p2p/pex/reactor_test.go b/internal/p2p/pex/reactor_test.go index 751f24d0a..82ea01480 100644 --- a/internal/p2p/pex/reactor_test.go +++ b/internal/p2p/pex/reactor_test.go @@ -4,10 +4,10 @@ package pex_test import ( "context" "errors" + "net/netip" "strings" "testing" "time" - "net/netip" "github.com/stretchr/testify/require" dbm "github.com/tendermint/tm-db" @@ -148,7 +148,7 @@ func TestReactorErrorsOnReceivingTooManyPeers(t *testing.T) { r := setupSingle(ctx, t) peer := p2p.Endpoint{ - AddrPort: netip.AddrPortFrom(netip.IPv6Loopback(),1234), + AddrPort: netip.AddrPortFrom(netip.IPv6Loopback(), 1234), }.NodeAddress(randomNodeID()) added, err := r.manager.Add(peer) require.NoError(t, err) diff --git a/internal/p2p/router.go b/internal/p2p/router.go index fbe61ea31..1ec7696b6 100644 --- a/internal/p2p/router.go +++ b/internal/p2p/router.go @@ -652,7 +652,6 @@ func (r *Router) handshakePeer( // they are closed elsewhere it will cause this method to shut down and return. func (r *Router) routePeer(ctx context.Context, peerID types.NodeID, conn Connection, channels ChannelIDSet) error { r.metrics.Peers.Add(1) - r.peerManager.Ready(ctx, peerID, channels) peerCtx, cancel := context.WithCancel(ctx) state := &peerState{ cancel: cancel, @@ -665,6 +664,7 @@ func (r *Router) routePeer(ctx context.Context, peerID types.NodeID, conn Connec } states[peerID] = state } + r.peerManager.Ready(ctx, peerID, channels) r.logger.Debug("peer connected", "peer", peerID, "endpoint", conn) err := scope.Run(peerCtx, func(ctx context.Context, s scope.Scope) error { s.Spawn(func() error { return r.receivePeer(ctx, peerID, conn) }) diff --git a/internal/p2p/router_test.go b/internal/p2p/router_test.go index 648e9cf82..049210015 100644 --- a/internal/p2p/router_test.go +++ b/internal/p2p/router_test.go @@ -13,6 +13,7 @@ import ( gogotypes "github.com/gogo/protobuf/types" dbm "github.com/tendermint/tm-db" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/internal/p2p" "github.com/tendermint/tendermint/internal/p2p/p2ptest" "github.com/tendermint/tendermint/libs/log" @@ -20,7 +21,6 @@ import ( "github.com/tendermint/tendermint/libs/utils/require" "github.com/tendermint/tendermint/libs/utils/scope" "github.com/tendermint/tendermint/types" - "github.com/tendermint/tendermint/crypto" ) func echoReactor(ctx context.Context, channel *p2p.Channel) { @@ -329,24 +329,24 @@ func TestRouter_Channel_Error(t *testing.T) { } type RouterHandle struct { - router *p2p.Router + router *p2p.Router peerManager *p2p.PeerManager - transport *p2p.Transport + transport *p2p.Transport } -var keyFiltered,infoFiltered = makeKeyAndInfo() +var keyFiltered, infoFiltered = makeKeyAndInfo() func spawnRouter(t *testing.T, logger log.Logger) RouterHandle { t.Helper() ctx := t.Context() // Set up and start the router. opts := p2p.PeerManagerOptions{ - MinRetryTime: 100*time.Millisecond, + MinRetryTime: 100 * time.Millisecond, } peerManager, err := p2p.NewPeerManager(logger, selfID, dbm.NewMemDB(), opts, p2p.NopMetrics()) require.NoError(t, err) // Router is responsible for running transport. - transport := p2p.TestTransport(logger.With("node","self"), selfID) + transport := p2p.TestTransport(logger.With("node", "self"), selfID) router, err := p2p.NewRouter( logger, p2p.NopMetrics(), @@ -355,13 +355,13 @@ func spawnRouter(t *testing.T, logger log.Logger) RouterHandle { func() *types.NodeInfo { return &selfInfo }, transport, func(_ context.Context, id types.NodeID) error { - if id==infoFiltered.NodeID { + if id == infoFiltered.NodeID { return errors.New("should filter") } return nil }, p2p.RouterOptions{ - DialSleep: func(context.Context) error { return nil }, + DialSleep: func(context.Context) error { return nil }, NumConcurrentDials: func() int { return 100 }, }, ) @@ -370,29 +370,29 @@ func spawnRouter(t *testing.T, logger log.Logger) RouterHandle { t.Cleanup(router.Stop) require.NoError(t, transport.WaitForStart(ctx)) return RouterHandle{ - router: router, + router: router, peerManager: peerManager, - transport: transport, + transport: transport, } } func TestRouter_AcceptPeers(t *testing.T) { - key,info := makeKeyAndInfo() + key, info := makeKeyAndInfo() info2 := info info2.Network = "other-network" info3 := info info3.Channels = []byte{0x23} testcases := map[string]struct { info types.NodeInfo - key crypto.PrivKey - ok bool + key crypto.PrivKey + ok bool }{ - "valid handshake": {info, key, true}, - "empty handshake": {types.NodeInfo{}, key, false}, - "self handshake": {selfInfo, selfKey, false}, - "incompatible network": {info2, key, false}, + "valid handshake": {info, key, true}, + "empty handshake": {types.NodeInfo{}, key, false}, + "self handshake": {selfInfo, selfKey, false}, + "incompatible network": {info2, key, false}, "incompatible channels": {info3, key, false}, - "filtered": {infoFiltered, keyFiltered, false}, + "filtered": {infoFiltered, keyFiltered, false}, } for name, tc := range testcases { @@ -404,16 +404,16 @@ func TestRouter_AcceptPeers(t *testing.T) { sub := h.peerManager.Subscribe(ctx) if err := scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { - peerTransport := p2p.TestTransport(logger.With("node","peer"), peerID) - s.SpawnBgNamed("peerTransport.Run()",func() error { return peerTransport.Run(ctx) }) - conn,err := peerTransport.Dial(ctx, h.transport.Endpoint()) - if err!=nil { - return fmt.Errorf("peerTransport.Dial(): %w",err) + peerTransport := p2p.TestTransport(logger.With("node", "peer"), peerID) + s.SpawnBgNamed("peerTransport.Run()", func() error { return peerTransport.Run(ctx) }) + conn, err := peerTransport.Dial(ctx, h.transport.Endpoint()) + if err != nil { + return fmt.Errorf("peerTransport.Dial(): %w", err) } defer conn.Close() if tc.ok { - if _,err := conn.Handshake(ctx, tc.info, tc.key); err!=nil { - return fmt.Errorf("conn.Handshake(): %w",err) + if _, err := conn.Handshake(ctx, tc.info, tc.key); err != nil { + return fmt.Errorf("conn.Handshake(): %w", err) } p2ptest.RequireUpdate(t, sub, p2p.PeerUpdate{ NodeID: tc.info.NodeID, @@ -422,15 +422,15 @@ func TestRouter_AcceptPeers(t *testing.T) { } else { // Expect immediate or delayed failure. // Peer should drop the connection during handshake. - if _,err := conn.Handshake(ctx, tc.info, tc.key); err!=nil { + if _, err := conn.Handshake(ctx, tc.info, tc.key); err != nil { return nil } - if _,_,err := conn.ReceiveMessage(ctx); !errors.Is(err,context.Canceled) { - return fmt.Errorf("want Canceled, got %w",err) + if _, _, err := conn.ReceiveMessage(ctx); !errors.Is(err, context.Canceled) { + return fmt.Errorf("want Canceled, got %w", err) } } return nil - }); err!=nil { + }); err != nil { t.Fatal(err) } }) @@ -451,21 +451,21 @@ func TestRouter_AcceptPeers_Parallel(t *testing.T) { t.Logf("Dial raw connections.") var conns []p2p.Connection for range 10 { - peerTransport := p2p.TestTransport(logger.With("node","peer"), peerID) - s.SpawnBgNamed("peerTransport.Run()",func() error { return peerTransport.Run(ctx) }) - conn,err := peerTransport.Dial(ctx, h.transport.Endpoint()) - if err!=nil { - return fmt.Errorf("peerTransport.Dial(): %w",err) + peerTransport := p2p.TestTransport(logger.With("node", "peer"), peerID) + s.SpawnBgNamed("peerTransport.Run()", func() error { return peerTransport.Run(ctx) }) + conn, err := peerTransport.Dial(ctx, h.transport.Endpoint()) + if err != nil { + return fmt.Errorf("peerTransport.Dial(): %w", err) } defer conn.Close() conns = append(conns, conn) } t.Logf("Handshake the connections in reverse order.") - for i:=len(conns)-1; i>=0; i-- { + for i := len(conns) - 1; i >= 0; i-- { conn := conns[i] - key,info := makeKeyAndInfo() - if _,err := conn.Handshake(ctx, info, key); err!=nil { - return fmt.Errorf("conn.Handshake(): %w",err) + key, info := makeKeyAndInfo() + if _, err := conn.Handshake(ctx, info, key); err != nil { + return fmt.Errorf("conn.Handshake(): %w", err) } p2ptest.RequireUpdate(t, sub, p2p.PeerUpdate{ NodeID: info.NodeID, @@ -473,7 +473,7 @@ func TestRouter_AcceptPeers_Parallel(t *testing.T) { }) } return nil - }); err!=nil { + }); err != nil { t.Fatal(err) } } @@ -488,55 +488,57 @@ func TestRouter_DialPeer_Retry(t *testing.T) { sub := h.peerManager.Subscribe(ctx) if err := scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { - peerTransport := p2p.TestTransport(logger.With("node","peer"), peerID) - s.SpawnBgNamed("peerTransport.Run()",func() error { return peerTransport.Run(ctx) }) - if err:=peerTransport.WaitForStart(ctx); err!=nil { return err } + peerTransport := p2p.TestTransport(logger.With("node", "peer"), peerID) + s.SpawnBgNamed("peerTransport.Run()", func() error { return peerTransport.Run(ctx) }) + if err := peerTransport.WaitForStart(ctx); err != nil { + return err + } t.Log("Populate peer manager.") - key,info := makeKeyAndInfo() - if ok,err := h.peerManager.Add(peerTransport.Endpoint().NodeAddress(info.NodeID)); !ok || err!=nil { - return fmt.Errorf("peerManager.Add() = %v,%w",ok,err) + key, info := makeKeyAndInfo() + if ok, err := h.peerManager.Add(peerTransport.Endpoint().NodeAddress(info.NodeID)); !ok || err != nil { + return fmt.Errorf("peerManager.Add() = %v,%w", ok, err) } t.Log("Accept and drop.") - conn,err := peerTransport.Accept(ctx) - if err!=nil { - return fmt.Errorf("peerTransport.Dial(): %w",err) + conn, err := peerTransport.Accept(ctx) + if err != nil { + return fmt.Errorf("peerTransport.Dial(): %w", err) } conn.Close() t.Log("Accept and complete handshake.") - conn,err = peerTransport.Accept(ctx) - if err!=nil { - return fmt.Errorf("peerTransport.Dial(): %w",err) + conn, err = peerTransport.Accept(ctx) + if err != nil { + return fmt.Errorf("peerTransport.Dial(): %w", err) } defer conn.Close() - if _,err := conn.Handshake(ctx, info, key); err!=nil { - return fmt.Errorf("conn.Handshake(): %w",err) + if _, err := conn.Handshake(ctx, info, key); err != nil { + return fmt.Errorf("conn.Handshake(): %w", err) } p2ptest.RequireUpdate(t, sub, p2p.PeerUpdate{ NodeID: info.NodeID, Status: p2p.PeerStatusUp, }) return nil - }); err!=nil { + }); err != nil { t.Fatal(err) } } func TestRouter_DialPeer_Reject(t *testing.T) { - key,info := makeKeyAndInfo() - _,info2 := makeKeyAndInfo() + key, info := makeKeyAndInfo() + _, info2 := makeKeyAndInfo() info3 := info info3.Network = "other-network" info4 := info info4.Channels = []byte{0x23} testcases := map[string]struct { dialID types.NodeID - info types.NodeInfo + info types.NodeInfo }{ - "empty handshake": {info.NodeID, types.NodeInfo{}}, - "unexpected node ID": {info2.NodeID, info}, + "empty handshake": {info.NodeID, types.NodeInfo{}}, + "unexpected node ID": {info2.NodeID, info}, "incompatible network": {info.NodeID, info3}, "incompatible channels": {info.NodeID, info4}, } @@ -547,28 +549,28 @@ func TestRouter_DialPeer_Reject(t *testing.T) { ctx := t.Context() h := spawnRouter(t, logger) if err := scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { - peerTransport := p2p.TestTransport(logger.With("node","peer"), tc.dialID) - s.SpawnBgNamed("peerTransport.Run()",func() error { return peerTransport.Run(ctx) }) - if err:=peerTransport.WaitForStart(ctx); err!=nil { - return fmt.Errorf("peerTransport.WaitForStart(): %w",err) + peerTransport := p2p.TestTransport(logger.With("node", "peer"), tc.dialID) + s.SpawnBgNamed("peerTransport.Run()", func() error { return peerTransport.Run(ctx) }) + if err := peerTransport.WaitForStart(ctx); err != nil { + return fmt.Errorf("peerTransport.WaitForStart(): %w", err) } - if ok,err := h.peerManager.Add(peerTransport.Endpoint().NodeAddress(tc.dialID)); !ok || err!=nil { - return fmt.Errorf("peerManager.Add() = %v,%w",ok,err) + if ok, err := h.peerManager.Add(peerTransport.Endpoint().NodeAddress(tc.dialID)); !ok || err != nil { + return fmt.Errorf("peerManager.Add() = %v,%w", ok, err) } - conn,err:=peerTransport.Accept(ctx) - if err!=nil { - return fmt.Errorf("peerTransport.Accept(): %w",err) + conn, err := peerTransport.Accept(ctx) + if err != nil { + return fmt.Errorf("peerTransport.Accept(): %w", err) } defer conn.Close() // Connections should be closed either during handshake, or immediately afterwards. - if _,err:=conn.Handshake(ctx, tc.info, key); err==nil { + if _, err := conn.Handshake(ctx, tc.info, key); err == nil { return nil } - if _,_,err:=conn.ReceiveMessage(ctx); !errors.Is(err,context.Canceled) { - return fmt.Errorf("want Canceled, got %w",err) + if _, _, err := conn.ReceiveMessage(ctx); !errors.Is(err, context.Canceled) { + return fmt.Errorf("want Canceled, got %w", err) } return nil - }); err!=nil { + }); err != nil { t.Fatal(err) } }) @@ -583,7 +585,7 @@ func TestRouter_DialPeers_Parallel(t *testing.T) { var keys []crypto.PrivKey var infos []types.NodeInfo for range 10 { - key,info := makeKeyAndInfo() + key, info := makeKeyAndInfo() keys = append(keys, key) infos = append(infos, info) } @@ -596,28 +598,28 @@ func TestRouter_DialPeers_Parallel(t *testing.T) { t.Logf("Accept raw connections.") var conns []p2p.Connection for i, info := range infos { - t.Logf("ACCEPT %v %v",i, info.NodeID) - peerTransport := p2p.TestTransport(logger.With("node","peer"), peerID) - s.SpawnBgNamed("peerTransport.Run()",func() error { return peerTransport.Run(ctx) }) - if err:=peerTransport.WaitForStart(ctx); err!=nil { - return fmt.Errorf("peerTransport.WaitForStart(): %w",err) + t.Logf("ACCEPT %v %v", i, info.NodeID) + peerTransport := p2p.TestTransport(logger.With("node", "peer"), peerID) + s.SpawnBgNamed("peerTransport.Run()", func() error { return peerTransport.Run(ctx) }) + if err := peerTransport.WaitForStart(ctx); err != nil { + return fmt.Errorf("peerTransport.WaitForStart(): %w", err) } - if ok,err := h.peerManager.Add(peerTransport.Endpoint().NodeAddress(info.NodeID)); !ok || err!=nil { - return fmt.Errorf("peerManager.Add() = %v,%w",ok,err) + if ok, err := h.peerManager.Add(peerTransport.Endpoint().NodeAddress(info.NodeID)); !ok || err != nil { + return fmt.Errorf("peerManager.Add() = %v,%w", ok, err) } - conn,err := peerTransport.Accept(ctx) - if err!=nil { - return fmt.Errorf("peerTransport.Accept(): %w",err) + conn, err := peerTransport.Accept(ctx) + if err != nil { + return fmt.Errorf("peerTransport.Accept(): %w", err) } defer conn.Close() conns = append(conns, conn) } t.Logf("Handshake the connections in reverse order.") - for i:=len(conns)-1; i>=0; i-- { + for i := len(conns) - 1; i >= 0; i-- { conn := conns[i] info := infos[i] - if _,err := conn.Handshake(ctx, info, keys[i]); err!=nil { - return fmt.Errorf("conn.Handshake(): %w",err) + if _, err := conn.Handshake(ctx, info, keys[i]); err != nil { + return fmt.Errorf("conn.Handshake(): %w", err) } p2ptest.RequireUpdate(t, sub, p2p.PeerUpdate{ NodeID: info.NodeID, @@ -625,7 +627,7 @@ func TestRouter_DialPeers_Parallel(t *testing.T) { }) } return nil - }); err!=nil { + }); err != nil { t.Fatal(err) } } @@ -638,16 +640,16 @@ func TestRouter_EvictPeers(t *testing.T) { sub := h.peerManager.Subscribe(ctx) if err := scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { - key,info := makeKeyAndInfo() - peerTransport := p2p.TestTransport(logger.With("node","peer"), info.NodeID) - s.SpawnBgNamed("peerTransport.Run()",func() error { return peerTransport.Run(ctx) }) - conn,err := peerTransport.Dial(ctx, h.transport.Endpoint()) - if err!=nil { - return fmt.Errorf("peerTransport.Dial(): %w",err) + key, info := makeKeyAndInfo() + peerTransport := p2p.TestTransport(logger.With("node", "peer"), info.NodeID) + s.SpawnBgNamed("peerTransport.Run()", func() error { return peerTransport.Run(ctx) }) + conn, err := peerTransport.Dial(ctx, h.transport.Endpoint()) + if err != nil { + return fmt.Errorf("peerTransport.Dial(): %w", err) } defer conn.Close() - if _,err := conn.Handshake(ctx, info, key); err!=nil { - return fmt.Errorf("conn.Handshake(): %w",err) + if _, err := conn.Handshake(ctx, info, key); err != nil { + return fmt.Errorf("conn.Handshake(): %w", err) } p2ptest.RequireUpdate(t, sub, p2p.PeerUpdate{ NodeID: info.NodeID, @@ -660,11 +662,12 @@ func TestRouter_EvictPeers(t *testing.T) { NodeID: info.NodeID, Status: p2p.PeerStatusDown, }) - if _,_,err:=conn.ReceiveMessage(ctx); !errors.Is(err,context.Canceled) { - return fmt.Errorf("want Canceled, got %w",err) + t.Log("Wait for conn down") + if _, _, err := conn.ReceiveMessage(ctx); !errors.Is(err, context.Canceled) { + return fmt.Errorf("want Canceled, got %w", err) } return nil - }); err!=nil { + }); err != nil { t.Fatal(err) } } @@ -696,17 +699,17 @@ func TestRouter_DontSendOnInvalidChannel(t *testing.T) { require.NoError(t, err) if err := scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { - key,info := makeKeyAndInfo() + key, info := makeKeyAndInfo() info.Channels = []byte{byte(desc1.ID)} - peerTransport := p2p.TestTransport(logger.With("node","peer"), info.NodeID, desc1) - s.SpawnBgNamed("peerTransport.Run()",func() error { return peerTransport.Run(ctx) }) - conn,err := peerTransport.Dial(ctx, h.transport.Endpoint()) - if err!=nil { - return fmt.Errorf("peerTransport.Dial(): %w",err) + peerTransport := p2p.TestTransport(logger.With("node", "peer"), info.NodeID, desc1) + s.SpawnBgNamed("peerTransport.Run()", func() error { return peerTransport.Run(ctx) }) + conn, err := peerTransport.Dial(ctx, h.transport.Endpoint()) + if err != nil { + return fmt.Errorf("peerTransport.Dial(): %w", err) } defer conn.Close() - if _,err := conn.Handshake(ctx, info, key); err!=nil { - return fmt.Errorf("conn.Handshake(): %w",err) + if _, err := conn.Handshake(ctx, info, key); err != nil { + return fmt.Errorf("conn.Handshake(): %w", err) } p2ptest.RequireUpdate(t, sub, p2p.PeerUpdate{ NodeID: info.NodeID, @@ -718,34 +721,34 @@ func TestRouter_DontSendOnInvalidChannel(t *testing.T) { t.Log("Broadcast messages of both channels.") s.Spawn(func() error { for range n { - if err:=ch1.Send(ctx, p2p.Envelope{Broadcast:true, Message: msg1}); err!=nil { - return fmt.Errorf("ch1.Send(): %w",err) + if err := ch1.Send(ctx, p2p.Envelope{Broadcast: true, Message: msg1}); err != nil { + return fmt.Errorf("ch1.Send(): %w", err) } - if err:=ch2.Send(ctx, p2p.Envelope{Broadcast:true, Message: msg2}); err!=nil { - return fmt.Errorf("ch2.Send(): %w",err) + if err := ch2.Send(ctx, p2p.Envelope{Broadcast: true, Message: msg2}); err != nil { + return fmt.Errorf("ch2.Send(): %w", err) } } return nil }) t.Log("Expect messages of 1 channel only.") for range n { - gotChID,gotMsg,err:=conn.ReceiveMessage(ctx) - if err!=nil { - return fmt.Errorf("ReceiveMessage(): %w",err) + gotChID, gotMsg, err := conn.ReceiveMessage(ctx) + if err != nil { + return fmt.Errorf("ReceiveMessage(): %w", err) } - if gotChID!=desc1.ID { + if gotChID != desc1.ID { return fmt.Errorf("gotChID = %v, want %v", gotChID, desc1.ID) } got := proto.Clone(desc1.MessageType) if err := proto.Unmarshal(gotMsg, got); err != nil { return fmt.Errorf("Unmarshal: %w", err) } - if err := utils.TestDiff[proto.Message](got,msg1); err!=nil { + if err := utils.TestDiff[proto.Message](got, msg1); err != nil { return fmt.Errorf("gotMsg: %v", err) } } return nil - }); err!=nil { + }); err != nil { t.Fatal(err) } } diff --git a/internal/p2p/transport.go b/internal/p2p/transport.go index fce29a164..9a3ca19d9 100644 --- a/internal/p2p/transport.go +++ b/internal/p2p/transport.go @@ -300,6 +300,7 @@ func (c *mConnConnection) Close() error { } return c.mconn.Close() } + // Connection represents an established connection between two endpoints. // // FIXME: This is a temporary interface for backwards-compatibility with the @@ -350,11 +351,11 @@ type Connection interface { // Endpoint represents a transport connection endpoint, either local or remote. // It is a TCP endpoint address. -type Endpoint struct { netip.AddrPort } +type Endpoint struct{ netip.AddrPort } // NewEndpoint constructs an Endpoint from a types.NetAddress structure. func NewEndpoint(addr string) (Endpoint, error) { - e,err := types.ParseAddressString(addr) + e, err := types.ParseAddressString(addr) return Endpoint{e}, err } @@ -363,7 +364,7 @@ func (e Endpoint) NodeAddress(nodeID types.NodeID) NodeAddress { return NodeAddress{ NodeID: nodeID, Hostname: e.Addr().String(), - Port: e.Port(), + Port: e.Port(), } } diff --git a/internal/p2p/transport_test.go b/internal/p2p/transport_test.go index d1c41cc8d..3c7acaeb4 100644 --- a/internal/p2p/transport_test.go +++ b/internal/p2p/transport_test.go @@ -10,16 +10,16 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "errors" + "fmt" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/internal/p2p" "github.com/tendermint/tendermint/libs/bytes" + "github.com/tendermint/tendermint/libs/utils" "github.com/tendermint/tendermint/libs/utils/scope" + "github.com/tendermint/tendermint/libs/utils/tcp" "github.com/tendermint/tendermint/types" - "errors" - "fmt" "io" - "github.com/tendermint/tendermint/libs/utils" - "github.com/tendermint/tendermint/libs/utils/tcp" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/internal/p2p/conn" @@ -228,6 +228,7 @@ func TestTransport_Listen(t *testing.T) { }) } } + // transportFactory is used to set up transports for tests. type transportFactory = func(ctx context.Context) *p2p.Transport diff --git a/node/setup.go b/node/setup.go index 205c205c9..0dfd10e26 100644 --- a/node/setup.go +++ b/node/setup.go @@ -309,9 +309,9 @@ func createRouter( transportConf.SendRate = cfg.P2P.SendRate transportConf.RecvRate = cfg.P2P.RecvRate transportConf.MaxPacketMsgPayloadSize = cfg.P2P.MaxPacketMsgPayloadSize - transport := p2p.NewMConnTransport( + transport := p2p.NewTransport( p2pLogger, ep, transportConf, []*p2p.ChannelDescriptor{}, - p2p.MConnTransportOptions{ + p2p.TransportOptions{ MaxAcceptedConnections: uint32(cfg.P2P.MaxConnections), }, ) From c13c5469fdf0907cda469a6b62ca532bfd5e08dd Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Wed, 1 Oct 2025 16:37:03 +0200 Subject: [PATCH 6/9] reverted address validation --- internal/p2p/address.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/p2p/address.go b/internal/p2p/address.go index a6efe686a..d69a224ee 100644 --- a/internal/p2p/address.go +++ b/internal/p2p/address.go @@ -115,8 +115,8 @@ func (a NodeAddress) Validate() error { } else if err := a.NodeID.Validate(); err != nil { return fmt.Errorf("invalid peer ID: %w", err) } - if a.Hostname == "" { - return errors.New("missing hostname") + if a.Port > 0 && a.Hostname == "" { + return errors.New("cannot specify port without hostname") } return nil } From 88cafd2baf02acd6fb26e0938badf4a866b29dbe Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Wed, 1 Oct 2025 16:44:22 +0200 Subject: [PATCH 7/9] race condition fix --- internal/p2p/transport.go | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/internal/p2p/transport.go b/internal/p2p/transport.go index 9a3ca19d9..dfc76ef69 100644 --- a/internal/p2p/transport.go +++ b/internal/p2p/transport.go @@ -55,7 +55,7 @@ type Transport struct { endpoint Endpoint options TransportOptions mConnConfig conn.MConnConfig - channelDescs []*ChannelDescriptor + channelDescs utils.Mutex[*[]*ChannelDescriptor] started chan struct{} listener chan *mConnConnection } @@ -75,7 +75,7 @@ func NewTransport( endpoint: endpoint, options: options, mConnConfig: mConnConfig, - channelDescs: channelDescs, + channelDescs: utils.NewMutex(&channelDescs), // This is rendezvous channel, so that no unclosed connections get stuck inside // when transport is closing. started: make(chan struct{}), @@ -124,7 +124,8 @@ func (m *Transport) Run(ctx context.Context) error { } return err } - mconn := newMConnConnection(m.logger, conn, m.mConnConfig, m.channelDescs) + descs := m.getChannelDescs() + mconn := newMConnConnection(m.logger, conn, m.mConnConfig, descs) if err := utils.Send(ctx, m.listener, mconn); err != nil { mconn.Close() return err @@ -156,7 +157,8 @@ func (m *Transport) Dial(ctx context.Context, endpoint Endpoint) (Connection, er if err != nil { return nil, fmt.Errorf("dialer.DialContext(%v): %w", endpoint, err) } - return newMConnConnection(m.logger, tcpConn, m.mConnConfig, m.channelDescs), nil + descs := m.getChannelDescs() + return newMConnConnection(m.logger, tcpConn, m.mConnConfig, descs), nil } // SetChannels sets the channel descriptors to be used when @@ -167,7 +169,18 @@ func (m *Transport) Dial(ctx context.Context, endpoint Endpoint) (Connection, er // connections should be agnostic to everything but the channel ID's which are // initialized in the handshake. func (m *Transport) AddChannelDescriptors(channelDesc []*ChannelDescriptor) { - m.channelDescs = append(m.channelDescs, channelDesc...) + for descs := range m.channelDescs.Lock() { + *descs = append(*descs, channelDesc...) + } +} + +func (m *Transport) getChannelDescs() []*ChannelDescriptor { + var descs []*ChannelDescriptor + for d := range m.channelDescs.Lock() { + descs = make([]*ChannelDescriptor, len(*d)) + copy(descs, *d) + } + return descs } type InvalidEndpointErr struct{ error } From 16c0e38ad1212ede9cf78e5565df4bdab03b00e6 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Wed, 1 Oct 2025 16:46:24 +0200 Subject: [PATCH 8/9] removed connection mock --- internal/p2p/mocks/connection.go | 191 ------------------------------- internal/p2p/transport.go | 2 - 2 files changed, 193 deletions(-) delete mode 100644 internal/p2p/mocks/connection.go diff --git a/internal/p2p/mocks/connection.go b/internal/p2p/mocks/connection.go deleted file mode 100644 index af36f4ce1..000000000 --- a/internal/p2p/mocks/connection.go +++ /dev/null @@ -1,191 +0,0 @@ -// Code generated by mockery. DO NOT EDIT. - -package mocks - -import ( - context "context" - - conn "github.com/tendermint/tendermint/internal/p2p/conn" - - crypto "github.com/tendermint/tendermint/crypto" - - mock "github.com/stretchr/testify/mock" - - p2p "github.com/tendermint/tendermint/internal/p2p" - - types "github.com/tendermint/tendermint/types" -) - -// Connection is an autogenerated mock type for the Connection type -type Connection struct { - mock.Mock -} - -// Close provides a mock function with no fields -func (_m *Connection) Close() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Close") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Handshake provides a mock function with given fields: _a0, _a1, _a2 -func (_m *Connection) Handshake(_a0 context.Context, _a1 types.NodeInfo, _a2 crypto.PrivKey) (types.NodeInfo, error) { - ret := _m.Called(_a0, _a1, _a2) - - if len(ret) == 0 { - panic("no return value specified for Handshake") - } - - var r0 types.NodeInfo - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, types.NodeInfo, crypto.PrivKey) (types.NodeInfo, error)); ok { - return rf(_a0, _a1, _a2) - } - if rf, ok := ret.Get(0).(func(context.Context, types.NodeInfo, crypto.PrivKey) types.NodeInfo); ok { - r0 = rf(_a0, _a1, _a2) - } else { - r0 = ret.Get(0).(types.NodeInfo) - } - - if rf, ok := ret.Get(1).(func(context.Context, types.NodeInfo, crypto.PrivKey) error); ok { - r1 = rf(_a0, _a1, _a2) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// LocalEndpoint provides a mock function with no fields -func (_m *Connection) LocalEndpoint() p2p.Endpoint { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for LocalEndpoint") - } - - var r0 p2p.Endpoint - if rf, ok := ret.Get(0).(func() p2p.Endpoint); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(p2p.Endpoint) - } - - return r0 -} - -// ReceiveMessage provides a mock function with given fields: _a0 -func (_m *Connection) ReceiveMessage(_a0 context.Context) (conn.ChannelID, []byte, error) { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for ReceiveMessage") - } - - var r0 conn.ChannelID - var r1 []byte - var r2 error - if rf, ok := ret.Get(0).(func(context.Context) (conn.ChannelID, []byte, error)); ok { - return rf(_a0) - } - if rf, ok := ret.Get(0).(func(context.Context) conn.ChannelID); ok { - r0 = rf(_a0) - } else { - r0 = ret.Get(0).(conn.ChannelID) - } - - if rf, ok := ret.Get(1).(func(context.Context) []byte); ok { - r1 = rf(_a0) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).([]byte) - } - } - - if rf, ok := ret.Get(2).(func(context.Context) error); ok { - r2 = rf(_a0) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// RemoteEndpoint provides a mock function with no fields -func (_m *Connection) RemoteEndpoint() p2p.Endpoint { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for RemoteEndpoint") - } - - var r0 p2p.Endpoint - if rf, ok := ret.Get(0).(func() p2p.Endpoint); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(p2p.Endpoint) - } - - return r0 -} - -// SendMessage provides a mock function with given fields: _a0, _a1, _a2 -func (_m *Connection) SendMessage(_a0 context.Context, _a1 conn.ChannelID, _a2 []byte) error { - ret := _m.Called(_a0, _a1, _a2) - - if len(ret) == 0 { - panic("no return value specified for SendMessage") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, conn.ChannelID, []byte) error); ok { - r0 = rf(_a0, _a1, _a2) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// String provides a mock function with no fields -func (_m *Connection) String() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for String") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// NewConnection creates a new instance of Connection. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewConnection(t interface { - mock.TestingT - Cleanup(func()) -}) *Connection { - mock := &Connection{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/p2p/transport.go b/internal/p2p/transport.go index dfc76ef69..f5b939b00 100644 --- a/internal/p2p/transport.go +++ b/internal/p2p/transport.go @@ -23,8 +23,6 @@ import ( "github.com/tendermint/tendermint/types" ) -//go:generate ../../scripts/mockery_generate.sh Connection - // TransportOptions sets options for Transport. type TransportOptions struct { // MaxAcceptedConnections is the maximum number of simultaneous accepted From acba1c9b20b7e0146fca10faba39cf29cbe84ce1 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Wed, 1 Oct 2025 16:50:10 +0200 Subject: [PATCH 9/9] fixed test --- internal/p2p/router_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/p2p/router_test.go b/internal/p2p/router_test.go index 049210015..818d3fa2d 100644 --- a/internal/p2p/router_test.go +++ b/internal/p2p/router_test.go @@ -563,7 +563,7 @@ func TestRouter_DialPeer_Reject(t *testing.T) { } defer conn.Close() // Connections should be closed either during handshake, or immediately afterwards. - if _, err := conn.Handshake(ctx, tc.info, key); err == nil { + if _, err := conn.Handshake(ctx, tc.info, key); err != nil { return nil } if _, _, err := conn.ReceiveMessage(ctx); !errors.Is(err, context.Canceled) {