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

885 lines
24 KiB

p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: file descriptor leaks (#3150) * close peer's connection to avoid fd leak Fixes #2967 * rename peer#Addr to RemoteAddr * fix test * fixes after Ethan's review * bring back the check * changelog entry * write a test for switch#acceptRoutine * increase timeouts? :( * remove extra assertNPeersWithTimeout * simplify test * assert number of peers (just to be safe) * Cleanup in OnStop * run tests with verbose flag on CircleCI * spawn a reading routine to prevent connection from closing * get port from the listener random port is faster, but often results in ``` panic: listen tcp 127.0.0.1:44068: bind: address already in use [recovered] panic: listen tcp 127.0.0.1:44068: bind: address already in use goroutine 79 [running]: testing.tRunner.func1(0xc0001bd600) /usr/local/go/src/testing/testing.go:792 +0x387 panic(0x974d20, 0xc0001b0500) /usr/local/go/src/runtime/panic.go:513 +0x1b9 github.com/tendermint/tendermint/p2p.MakeSwitch(0xc0000f42a0, 0x0, 0x9fb9cc, 0x9, 0x9fc346, 0xb, 0xb42128, 0x0, 0x0, 0x0, ...) /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/test_util.go:182 +0xa28 github.com/tendermint/tendermint/p2p.MakeConnectedSwitches(0xc0000f42a0, 0x2, 0xb42128, 0xb41eb8, 0x4f1205, 0xc0001bed80, 0x4f16ed) /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/test_util.go:75 +0xf9 github.com/tendermint/tendermint/p2p.MakeSwitchPair(0xbb8d20, 0xc0001bd600, 0xb42128, 0x2f7, 0x4f16c0) /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/switch_test.go:94 +0x4c github.com/tendermint/tendermint/p2p.TestSwitches(0xc0001bd600) /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/switch_test.go:117 +0x58 testing.tRunner(0xc0001bd600, 0xb42038) /usr/local/go/src/testing/testing.go:827 +0xbf created by testing.(*T).Run /usr/local/go/src/testing/testing.go:878 +0x353 exit status 2 FAIL github.com/tendermint/tendermint/p2p 0.350s ```
6 years ago
p2p: file descriptor leaks (#3150) * close peer's connection to avoid fd leak Fixes #2967 * rename peer#Addr to RemoteAddr * fix test * fixes after Ethan's review * bring back the check * changelog entry * write a test for switch#acceptRoutine * increase timeouts? :( * remove extra assertNPeersWithTimeout * simplify test * assert number of peers (just to be safe) * Cleanup in OnStop * run tests with verbose flag on CircleCI * spawn a reading routine to prevent connection from closing * get port from the listener random port is faster, but often results in ``` panic: listen tcp 127.0.0.1:44068: bind: address already in use [recovered] panic: listen tcp 127.0.0.1:44068: bind: address already in use goroutine 79 [running]: testing.tRunner.func1(0xc0001bd600) /usr/local/go/src/testing/testing.go:792 +0x387 panic(0x974d20, 0xc0001b0500) /usr/local/go/src/runtime/panic.go:513 +0x1b9 github.com/tendermint/tendermint/p2p.MakeSwitch(0xc0000f42a0, 0x0, 0x9fb9cc, 0x9, 0x9fc346, 0xb, 0xb42128, 0x0, 0x0, 0x0, ...) /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/test_util.go:182 +0xa28 github.com/tendermint/tendermint/p2p.MakeConnectedSwitches(0xc0000f42a0, 0x2, 0xb42128, 0xb41eb8, 0x4f1205, 0xc0001bed80, 0x4f16ed) /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/test_util.go:75 +0xf9 github.com/tendermint/tendermint/p2p.MakeSwitchPair(0xbb8d20, 0xc0001bd600, 0xb42128, 0x2f7, 0x4f16c0) /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/switch_test.go:94 +0x4c github.com/tendermint/tendermint/p2p.TestSwitches(0xc0001bd600) /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/switch_test.go:117 +0x58 testing.tRunner(0xc0001bd600, 0xb42038) /usr/local/go/src/testing/testing.go:827 +0xbf created by testing.(*T).Run /usr/local/go/src/testing/testing.go:878 +0x353 exit status 2 FAIL github.com/tendermint/tendermint/p2p 0.350s ```
6 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: file descriptor leaks (#3150) * close peer's connection to avoid fd leak Fixes #2967 * rename peer#Addr to RemoteAddr * fix test * fixes after Ethan's review * bring back the check * changelog entry * write a test for switch#acceptRoutine * increase timeouts? :( * remove extra assertNPeersWithTimeout * simplify test * assert number of peers (just to be safe) * Cleanup in OnStop * run tests with verbose flag on CircleCI * spawn a reading routine to prevent connection from closing * get port from the listener random port is faster, but often results in ``` panic: listen tcp 127.0.0.1:44068: bind: address already in use [recovered] panic: listen tcp 127.0.0.1:44068: bind: address already in use goroutine 79 [running]: testing.tRunner.func1(0xc0001bd600) /usr/local/go/src/testing/testing.go:792 +0x387 panic(0x974d20, 0xc0001b0500) /usr/local/go/src/runtime/panic.go:513 +0x1b9 github.com/tendermint/tendermint/p2p.MakeSwitch(0xc0000f42a0, 0x0, 0x9fb9cc, 0x9, 0x9fc346, 0xb, 0xb42128, 0x0, 0x0, 0x0, ...) /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/test_util.go:182 +0xa28 github.com/tendermint/tendermint/p2p.MakeConnectedSwitches(0xc0000f42a0, 0x2, 0xb42128, 0xb41eb8, 0x4f1205, 0xc0001bed80, 0x4f16ed) /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/test_util.go:75 +0xf9 github.com/tendermint/tendermint/p2p.MakeSwitchPair(0xbb8d20, 0xc0001bd600, 0xb42128, 0x2f7, 0x4f16c0) /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/switch_test.go:94 +0x4c github.com/tendermint/tendermint/p2p.TestSwitches(0xc0001bd600) /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/switch_test.go:117 +0x58 testing.tRunner(0xc0001bd600, 0xb42038) /usr/local/go/src/testing/testing.go:827 +0xbf created by testing.(*T).Run /usr/local/go/src/testing/testing.go:878 +0x353 exit status 2 FAIL github.com/tendermint/tendermint/p2p 0.350s ```
6 years ago
p2p: file descriptor leaks (#3150) * close peer's connection to avoid fd leak Fixes #2967 * rename peer#Addr to RemoteAddr * fix test * fixes after Ethan's review * bring back the check * changelog entry * write a test for switch#acceptRoutine * increase timeouts? :( * remove extra assertNPeersWithTimeout * simplify test * assert number of peers (just to be safe) * Cleanup in OnStop * run tests with verbose flag on CircleCI * spawn a reading routine to prevent connection from closing * get port from the listener random port is faster, but often results in ``` panic: listen tcp 127.0.0.1:44068: bind: address already in use [recovered] panic: listen tcp 127.0.0.1:44068: bind: address already in use goroutine 79 [running]: testing.tRunner.func1(0xc0001bd600) /usr/local/go/src/testing/testing.go:792 +0x387 panic(0x974d20, 0xc0001b0500) /usr/local/go/src/runtime/panic.go:513 +0x1b9 github.com/tendermint/tendermint/p2p.MakeSwitch(0xc0000f42a0, 0x0, 0x9fb9cc, 0x9, 0x9fc346, 0xb, 0xb42128, 0x0, 0x0, 0x0, ...) /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/test_util.go:182 +0xa28 github.com/tendermint/tendermint/p2p.MakeConnectedSwitches(0xc0000f42a0, 0x2, 0xb42128, 0xb41eb8, 0x4f1205, 0xc0001bed80, 0x4f16ed) /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/test_util.go:75 +0xf9 github.com/tendermint/tendermint/p2p.MakeSwitchPair(0xbb8d20, 0xc0001bd600, 0xb42128, 0x2f7, 0x4f16c0) /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/switch_test.go:94 +0x4c github.com/tendermint/tendermint/p2p.TestSwitches(0xc0001bd600) /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/switch_test.go:117 +0x58 testing.tRunner(0xc0001bd600, 0xb42038) /usr/local/go/src/testing/testing.go:827 +0xbf created by testing.(*T).Run /usr/local/go/src/testing/testing.go:878 +0x353 exit status 2 FAIL github.com/tendermint/tendermint/p2p 0.350s ```
6 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
  1. package p2p
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "net"
  8. "net/url"
  9. "runtime/debug"
  10. "strconv"
  11. "sync"
  12. "time"
  13. "github.com/tendermint/tendermint/libs/cmap"
  14. "github.com/tendermint/tendermint/libs/log"
  15. "github.com/tendermint/tendermint/libs/service"
  16. tmconn "github.com/tendermint/tendermint/p2p/conn"
  17. )
  18. // PeerAddress is a peer address URL.
  19. type PeerAddress struct {
  20. *url.URL
  21. }
  22. // ParsePeerAddress parses a peer address URL into a PeerAddress.
  23. func ParsePeerAddress(address string) (PeerAddress, error) {
  24. u, err := url.Parse(address)
  25. if err != nil || u == nil {
  26. return PeerAddress{}, fmt.Errorf("unable to parse peer address %q: %w", address, err)
  27. }
  28. if u.Scheme == "" {
  29. u.Scheme = string(defaultProtocol)
  30. }
  31. pa := PeerAddress{URL: u}
  32. if err = pa.Validate(); err != nil {
  33. return PeerAddress{}, err
  34. }
  35. return pa, nil
  36. }
  37. // NodeID returns the address node ID.
  38. func (a PeerAddress) NodeID() NodeID {
  39. return NodeID(a.User.Username())
  40. }
  41. // Resolve resolves a PeerAddress into a set of Endpoints, by expanding
  42. // out a DNS name in Host to its IP addresses. Field mapping:
  43. //
  44. // Scheme → Endpoint.Protocol
  45. // Host → Endpoint.IP
  46. // User → Endpoint.PeerID
  47. // Port → Endpoint.Port
  48. // Path+Query+Fragment,Opaque → Endpoint.Path
  49. //
  50. func (a PeerAddress) Resolve(ctx context.Context) ([]Endpoint, error) {
  51. ips, err := net.DefaultResolver.LookupIP(ctx, "ip", a.Host)
  52. if err != nil {
  53. return nil, err
  54. }
  55. port, err := a.parsePort()
  56. if err != nil {
  57. return nil, err
  58. }
  59. path := a.Path
  60. if a.RawPath != "" {
  61. path = a.RawPath
  62. }
  63. if a.Opaque != "" { // used for e.g. "about:blank" style URLs
  64. path = a.Opaque
  65. }
  66. if a.RawQuery != "" {
  67. path += "?" + a.RawQuery
  68. }
  69. if a.RawFragment != "" {
  70. path += "#" + a.RawFragment
  71. }
  72. endpoints := make([]Endpoint, len(ips))
  73. for i, ip := range ips {
  74. endpoints[i] = Endpoint{
  75. PeerID: a.NodeID(),
  76. Protocol: Protocol(a.Scheme),
  77. IP: ip,
  78. Port: port,
  79. Path: path,
  80. }
  81. }
  82. return endpoints, nil
  83. }
  84. // Validates validates a PeerAddress.
  85. func (a PeerAddress) Validate() error {
  86. if a.Scheme == "" {
  87. return errors.New("no protocol")
  88. }
  89. if id := a.User.Username(); id == "" {
  90. return errors.New("no peer ID")
  91. } else if err := NodeID(id).Validate(); err != nil {
  92. return fmt.Errorf("invalid peer ID: %w", err)
  93. }
  94. if a.Hostname() == "" && len(a.Query()) == 0 && a.Opaque == "" {
  95. return errors.New("no host or path given")
  96. }
  97. if port, err := a.parsePort(); err != nil {
  98. return err
  99. } else if port > 0 && a.Hostname() == "" {
  100. return errors.New("cannot specify port without host")
  101. }
  102. return nil
  103. }
  104. // parsePort returns the port number as a uint16.
  105. func (a PeerAddress) parsePort() (uint16, error) {
  106. if portString := a.Port(); portString != "" {
  107. port64, err := strconv.ParseUint(portString, 10, 16)
  108. if err != nil {
  109. return 0, fmt.Errorf("invalid port %q: %w", portString, err)
  110. }
  111. return uint16(port64), nil
  112. }
  113. return 0, nil
  114. }
  115. // PeerStatus specifies peer statuses.
  116. type PeerStatus string
  117. const (
  118. PeerStatusNew = PeerStatus("new") // New peer which we haven't tried to contact yet.
  119. PeerStatusUp = PeerStatus("up") // Peer which we have an active connection to.
  120. PeerStatusDown = PeerStatus("down") // Peer which we're temporarily disconnected from.
  121. PeerStatusRemoved = PeerStatus("removed") // Peer which has been removed.
  122. PeerStatusBanned = PeerStatus("banned") // Peer which is banned for misbehavior.
  123. )
  124. // PeerPriority specifies peer priorities.
  125. type PeerPriority int
  126. const (
  127. PeerPriorityNormal PeerPriority = iota + 1
  128. PeerPriorityValidator
  129. PeerPriorityPersistent
  130. )
  131. // PeerError is a peer error reported by a reactor via the Error channel. The
  132. // severity may cause the peer to be disconnected or banned depending on policy.
  133. type PeerError struct {
  134. PeerID NodeID
  135. Err error
  136. Severity PeerErrorSeverity
  137. }
  138. // PeerErrorSeverity determines the severity of a peer error.
  139. type PeerErrorSeverity string
  140. const (
  141. PeerErrorSeverityLow PeerErrorSeverity = "low" // Mostly ignored.
  142. PeerErrorSeverityHigh PeerErrorSeverity = "high" // May disconnect.
  143. PeerErrorSeverityCritical PeerErrorSeverity = "critical" // Ban.
  144. )
  145. // PeerUpdatesCh defines a wrapper around a PeerUpdate go channel that allows
  146. // a reactor to listen for peer updates and safely close it when stopping.
  147. type PeerUpdatesCh struct {
  148. closeOnce sync.Once
  149. // updatesCh defines the go channel in which the router sends peer updates to
  150. // reactors. Each reactor will have its own PeerUpdatesCh to listen for updates
  151. // from.
  152. updatesCh chan PeerUpdate
  153. // doneCh is used to signal that a PeerUpdatesCh is closed. It is the
  154. // reactor's responsibility to invoke Close.
  155. doneCh chan struct{}
  156. }
  157. // NewPeerUpdates returns a reference to a new PeerUpdatesCh.
  158. func NewPeerUpdates(updatesCh chan PeerUpdate) *PeerUpdatesCh {
  159. return &PeerUpdatesCh{
  160. updatesCh: updatesCh,
  161. doneCh: make(chan struct{}),
  162. }
  163. }
  164. // Updates returns a read-only go channel where a consuming reactor can listen
  165. // for peer updates sent from the router.
  166. func (puc *PeerUpdatesCh) Updates() <-chan PeerUpdate {
  167. return puc.updatesCh
  168. }
  169. // Close closes the PeerUpdatesCh channel. It should only be closed by the respective
  170. // reactor when stopping and ensure nothing is listening for updates.
  171. //
  172. // NOTE: After a PeerUpdatesCh is closed, the router may safely assume it can no
  173. // longer send on the internal updatesCh, however it should NEVER explicitly close
  174. // it as that could result in panics by sending on a closed channel.
  175. func (puc *PeerUpdatesCh) Close() {
  176. puc.closeOnce.Do(func() {
  177. close(puc.doneCh)
  178. })
  179. }
  180. // Done returns a read-only version of the PeerUpdatesCh's internal doneCh go
  181. // channel that should be used by a router to signal when it is safe to explicitly
  182. // not send any peer updates.
  183. func (puc *PeerUpdatesCh) Done() <-chan struct{} {
  184. return puc.doneCh
  185. }
  186. // PeerUpdate is a peer status update for reactors.
  187. type PeerUpdate struct {
  188. PeerID NodeID
  189. Status PeerStatus
  190. }
  191. // peerManager manages peer information, using a peerStore for underlying
  192. // storage. Its primary purpose is to determine which peers to connect to next,
  193. // make sure a peer only has a single active connection (either inbound or outbound),
  194. // and to avoid dialing the same peer in parallel goroutines.
  195. //
  196. // For an outbound connection, the flow is as follows:
  197. // - DialNext: returns a peer address to dial, marking the peer as dialing.
  198. // - DialFailed: reports a dial failure, unmarking the peer as dialing.
  199. // - Dialed: successfully dialed, unmarking as dialing and marking as connected
  200. // (or erroring if already connected).
  201. // - Ready: routing is up, broadcasts a PeerStatusUp peer update to subscribers.
  202. // - Disconnected: peer disconnects, unmarking as connected and broadcasts a
  203. // PeerStatusDown peer update.
  204. //
  205. // For an inbound connection, the flow is as follows:
  206. // - Accepted: successfully accepted connection, marking as connected (or erroring
  207. // if already connected).
  208. // - Ready: routing is up, broadcasts a PeerStatusUp peer update to subscribers.
  209. // - Disconnected: peer disconnects, unmarking as connected and broadcasts a
  210. // PeerStatusDown peer update.
  211. //
  212. // We track dialing and connected states independently. This allows us to accept
  213. // an inbound connection from a peer while the router is also dialing an
  214. // outbound connection to that same peer, which will cause the dialer to
  215. // eventually error (when attempting to mark the peer as connected). This also
  216. // avoids race conditions where multiple goroutines may end up dialing a peer if
  217. // an incoming connection was briefly accepted and disconnected while we were
  218. // also dialing.
  219. type peerManager struct {
  220. mtx sync.Mutex
  221. store *peerStore
  222. dialing map[NodeID]bool
  223. connected map[NodeID]bool
  224. subscriptions map[*PeerUpdatesCh]*PeerUpdatesCh // keyed by struct identity (address)
  225. }
  226. // newPeerManager creates a new peer manager.
  227. func newPeerManager(store *peerStore) *peerManager {
  228. return &peerManager{
  229. store: store,
  230. dialing: map[NodeID]bool{},
  231. connected: map[NodeID]bool{},
  232. subscriptions: map[*PeerUpdatesCh]*PeerUpdatesCh{},
  233. }
  234. }
  235. // Add adds a peer to the manager, given as an address. If the peer already
  236. // exists, the address is added to it.
  237. func (m *peerManager) Add(address PeerAddress) error {
  238. if err := address.Validate(); err != nil {
  239. return err
  240. }
  241. peerID := address.NodeID()
  242. m.mtx.Lock()
  243. defer m.mtx.Unlock()
  244. peer, err := m.store.Get(peerID)
  245. if err != nil {
  246. return err
  247. }
  248. if peer == nil {
  249. peer = newPeerInfo(peerID)
  250. }
  251. if peer.AddAddress(address) {
  252. return m.store.Set(peer)
  253. }
  254. return nil
  255. }
  256. // Subscribe subscribes to peer updates. The caller must consume the peer
  257. // updates in a timely fashion and close the subscription when done, since
  258. // delivery is guaranteed and will block peer connection/disconnection
  259. // otherwise.
  260. func (m *peerManager) Subscribe() *PeerUpdatesCh {
  261. // FIXME: We may want to use a size 1 buffer here. When the router
  262. // broadcasts a peer update it has to loop over all of the
  263. // subscriptions, and we want to avoid blocking and waiting for a
  264. // context switch before continuing to the next subscription. This also
  265. // prevents tail latencies from compounding across updates. We also want
  266. // to make sure the subscribers are reasonably in sync, so it should be
  267. // kept at 1. However, this should be benchmarked first.
  268. peerUpdates := NewPeerUpdates(make(chan PeerUpdate))
  269. m.mtx.Lock()
  270. m.subscriptions[peerUpdates] = peerUpdates
  271. m.mtx.Unlock()
  272. go func() {
  273. <-peerUpdates.Done()
  274. m.mtx.Lock()
  275. delete(m.subscriptions, peerUpdates)
  276. m.mtx.Unlock()
  277. }()
  278. return peerUpdates
  279. }
  280. // broadcast broadcasts a peer update to all subscriptions. The caller must
  281. // already hold the mutex lock. This means the mutex is held for the duration
  282. // of the broadcast, which we want to make sure all subscriptions receive all
  283. // updates in the same order.
  284. //
  285. // FIXME: Consider using more fine-grained mutexes here, and/or a channel to
  286. // enforce ordering of updates.
  287. func (m *peerManager) broadcast(peerUpdate PeerUpdate) {
  288. for _, sub := range m.subscriptions {
  289. select {
  290. case sub.updatesCh <- peerUpdate:
  291. case <-sub.doneCh:
  292. }
  293. }
  294. }
  295. // DialNext finds an appropriate peer address to dial, and marks it as dialing.
  296. // The peer will not be returned again until Dialed() or DialFailed() is called
  297. // for the peer and it is no longer connected.
  298. //
  299. // Returns an empty ID if no appropriate peers are available.
  300. func (m *peerManager) DialNext() (NodeID, PeerAddress, error) {
  301. m.mtx.Lock()
  302. defer m.mtx.Unlock()
  303. peers, err := m.store.List()
  304. if err != nil {
  305. return "", PeerAddress{}, err
  306. }
  307. for _, peer := range peers {
  308. switch {
  309. case len(peer.Addresses) == 0:
  310. case m.dialing[peer.ID]:
  311. case m.connected[peer.ID]:
  312. default:
  313. // FIXME: We currently only dial the first address, but we should
  314. // track connection statistics for each address and return the most
  315. // appropriate one.
  316. m.dialing[peer.ID] = true
  317. return peer.ID, peer.Addresses[0], nil
  318. }
  319. }
  320. return "", PeerAddress{}, nil
  321. }
  322. // DialFailed reports a failed dial attempt. This will make the peer available
  323. // for dialing again when appropriate.
  324. func (m *peerManager) DialFailed(peerID NodeID, address PeerAddress) error {
  325. m.mtx.Lock()
  326. defer m.mtx.Unlock()
  327. delete(m.dialing, peerID)
  328. // FIXME: We need to track address quality statistics and exponential backoff.
  329. return nil
  330. }
  331. // Dialed marks a peer as successfully dialed. Any further incoming connections
  332. // will be rejected, and once disconnected the peer may be dialed again.
  333. func (m *peerManager) Dialed(peerID NodeID, address PeerAddress) error {
  334. m.mtx.Lock()
  335. defer m.mtx.Unlock()
  336. peer, err := m.store.Get(peerID)
  337. if err != nil {
  338. return err
  339. } else if peer == nil {
  340. return fmt.Errorf("unknown peer %q", peerID)
  341. }
  342. if m.connected[peerID] {
  343. return fmt.Errorf("peer %v is already connected", peerID)
  344. }
  345. delete(m.dialing, peerID)
  346. m.connected[peerID] = true
  347. return nil
  348. }
  349. // Accepted marks an incoming peer connection successfully accepted. If the peer
  350. // is already connected this will return an error.
  351. //
  352. // NOTE: We can't take an address here, since e.g. TCP uses a different port
  353. // number for outbound traffic than inbound traffic, so the peer's endpoint
  354. // wouldn't necessarily be an appropriate address to dial.
  355. func (m *peerManager) Accepted(peerID NodeID) error {
  356. m.mtx.Lock()
  357. defer m.mtx.Unlock()
  358. peer, err := m.store.Get(peerID)
  359. if err != nil {
  360. return err
  361. } else if peer == nil {
  362. peer = newPeerInfo(peerID)
  363. if err = m.store.Set(peer); err != nil {
  364. return err
  365. }
  366. }
  367. if m.connected[peerID] {
  368. return fmt.Errorf("peer %q is already connected", peerID)
  369. }
  370. m.connected[peerID] = true
  371. return nil
  372. }
  373. // Ready marks a peer as ready, broadcasting status updates to subscribers. The
  374. // peer must already be marked as connected. This is separate from Dialed() and
  375. // Accepted() to allow the router to set up its internal queues before reactors
  376. // start sending messages (holding the Router.peerMtx mutex while calling
  377. // Accepted or Dialed will halt all message routing while peers are set up, which
  378. // is too expensive and also causes difficulties in tests where we may want to
  379. // consume peer updates and send messages sequentially).
  380. //
  381. // FIXME: This possibly indicates an architectural problem. Should the peerManager
  382. // handle actual network connections to/from peers as well? Or should all of this
  383. // be done by the router?
  384. func (m *peerManager) Ready(peerID NodeID) {
  385. m.mtx.Lock()
  386. defer m.mtx.Unlock()
  387. connected := m.connected[peerID]
  388. if connected {
  389. m.broadcast(PeerUpdate{
  390. PeerID: peerID,
  391. Status: PeerStatusUp,
  392. })
  393. }
  394. }
  395. // Disconnected unmarks a peer as connected, allowing new connections to be
  396. // established.
  397. func (m *peerManager) Disconnected(peerID NodeID) error {
  398. m.mtx.Lock()
  399. defer m.mtx.Unlock()
  400. delete(m.connected, peerID)
  401. m.broadcast(PeerUpdate{
  402. PeerID: peerID,
  403. Status: PeerStatusDown,
  404. })
  405. return nil
  406. }
  407. // peerStore stores information about peers. It is currently a bare-bones
  408. // in-memory store, and will be fleshed out later.
  409. //
  410. // peerStore is not thread-safe, since it assumes it is only used by peerManager
  411. // which handles concurrency control. This allows multiple operations to be
  412. // executed atomically, since the peerManager will hold a mutex while executing.
  413. type peerStore struct {
  414. peers map[NodeID]peerInfo
  415. }
  416. // newPeerStore creates a new peer store.
  417. func newPeerStore() *peerStore {
  418. return &peerStore{
  419. peers: map[NodeID]peerInfo{},
  420. }
  421. }
  422. // Get fetches a peer, returning nil if not found.
  423. func (s *peerStore) Get(id NodeID) (*peerInfo, error) {
  424. peer, ok := s.peers[id]
  425. if !ok {
  426. return nil, nil
  427. }
  428. return &peer, nil
  429. }
  430. // Set stores peer data.
  431. func (s *peerStore) Set(peer *peerInfo) error {
  432. if peer == nil {
  433. return errors.New("peer cannot be nil")
  434. }
  435. s.peers[peer.ID] = *peer
  436. return nil
  437. }
  438. // List retrieves all peers.
  439. func (s *peerStore) List() ([]*peerInfo, error) {
  440. peers := []*peerInfo{}
  441. for _, peer := range s.peers {
  442. peer := peer
  443. peers = append(peers, &peer)
  444. }
  445. return peers, nil
  446. }
  447. // peerInfo contains peer information stored in a peerStore.
  448. //
  449. // FIXME: This should be renamed peer or something else once the old peer is
  450. // removed.
  451. type peerInfo struct {
  452. ID NodeID
  453. Addresses []PeerAddress
  454. }
  455. // newPeerInfo creates a new peerInfo.
  456. func newPeerInfo(id NodeID) *peerInfo {
  457. return &peerInfo{
  458. ID: id,
  459. Addresses: []PeerAddress{},
  460. }
  461. }
  462. // AddAddress adds an address to a peer, unless it already exists. It does not
  463. // validate the address. Returns true if the address was new.
  464. func (p *peerInfo) AddAddress(address PeerAddress) bool {
  465. // We just do a linear search for now.
  466. addressString := address.String()
  467. for _, a := range p.Addresses {
  468. if a.String() == addressString {
  469. return false
  470. }
  471. }
  472. p.Addresses = append(p.Addresses, address)
  473. return true
  474. }
  475. // ============================================================================
  476. // Types and business logic below may be deprecated.
  477. //
  478. // TODO: Rename once legacy p2p types are removed.
  479. // ref: https://github.com/tendermint/tendermint/issues/5670
  480. // ============================================================================
  481. //go:generate mockery --case underscore --name Peer
  482. const metricsTickerDuration = 10 * time.Second
  483. // Peer is an interface representing a peer connected on a reactor.
  484. type Peer interface {
  485. service.Service
  486. FlushStop()
  487. ID() NodeID // peer's cryptographic ID
  488. RemoteIP() net.IP // remote IP of the connection
  489. RemoteAddr() net.Addr // remote address of the connection
  490. IsOutbound() bool // did we dial the peer
  491. IsPersistent() bool // do we redial this peer when we disconnect
  492. CloseConn() error // close original connection
  493. NodeInfo() NodeInfo // peer's info
  494. Status() tmconn.ConnectionStatus
  495. SocketAddr() *NetAddress // actual address of the socket
  496. Send(byte, []byte) bool
  497. TrySend(byte, []byte) bool
  498. Set(string, interface{})
  499. Get(string) interface{}
  500. }
  501. //----------------------------------------------------------
  502. // peerConn contains the raw connection and its config.
  503. type peerConn struct {
  504. outbound bool
  505. persistent bool
  506. conn Connection
  507. ip net.IP // cached RemoteIP()
  508. }
  509. func newPeerConn(outbound, persistent bool, conn Connection) peerConn {
  510. return peerConn{
  511. outbound: outbound,
  512. persistent: persistent,
  513. conn: conn,
  514. }
  515. }
  516. // ID only exists for SecretConnection.
  517. func (pc peerConn) ID() NodeID {
  518. return NodeIDFromPubKey(pc.conn.PubKey())
  519. }
  520. // Return the IP from the connection RemoteAddr
  521. func (pc peerConn) RemoteIP() net.IP {
  522. if pc.ip == nil {
  523. pc.ip = pc.conn.RemoteEndpoint().IP
  524. }
  525. return pc.ip
  526. }
  527. // peer implements Peer.
  528. //
  529. // Before using a peer, you will need to perform a handshake on connection.
  530. type peer struct {
  531. service.BaseService
  532. // raw peerConn and the multiplex connection
  533. peerConn
  534. // peer's node info and the channel it knows about
  535. // channels = nodeInfo.Channels
  536. // cached to avoid copying nodeInfo in hasChannel
  537. nodeInfo NodeInfo
  538. channels []byte
  539. reactors map[byte]Reactor
  540. onPeerError func(Peer, interface{})
  541. // User data
  542. Data *cmap.CMap
  543. metrics *Metrics
  544. metricsTicker *time.Ticker
  545. }
  546. type PeerOption func(*peer)
  547. func newPeer(
  548. pc peerConn,
  549. reactorsByCh map[byte]Reactor,
  550. onPeerError func(Peer, interface{}),
  551. options ...PeerOption,
  552. ) *peer {
  553. nodeInfo := pc.conn.NodeInfo()
  554. p := &peer{
  555. peerConn: pc,
  556. nodeInfo: nodeInfo,
  557. channels: nodeInfo.Channels, // TODO
  558. reactors: reactorsByCh,
  559. onPeerError: onPeerError,
  560. Data: cmap.NewCMap(),
  561. metricsTicker: time.NewTicker(metricsTickerDuration),
  562. metrics: NopMetrics(),
  563. }
  564. p.BaseService = *service.NewBaseService(nil, "Peer", p)
  565. for _, option := range options {
  566. option(p)
  567. }
  568. return p
  569. }
  570. // onError calls the peer error callback.
  571. func (p *peer) onError(err interface{}) {
  572. p.onPeerError(p, err)
  573. }
  574. // String representation.
  575. func (p *peer) String() string {
  576. if p.outbound {
  577. return fmt.Sprintf("Peer{%v %v out}", p.conn, p.ID())
  578. }
  579. return fmt.Sprintf("Peer{%v %v in}", p.conn, p.ID())
  580. }
  581. //---------------------------------------------------
  582. // Implements service.Service
  583. // SetLogger implements BaseService.
  584. func (p *peer) SetLogger(l log.Logger) {
  585. p.Logger = l
  586. }
  587. // OnStart implements BaseService.
  588. func (p *peer) OnStart() error {
  589. if err := p.BaseService.OnStart(); err != nil {
  590. return err
  591. }
  592. go p.processMessages()
  593. go p.metricsReporter()
  594. return nil
  595. }
  596. // processMessages processes messages received from the connection.
  597. func (p *peer) processMessages() {
  598. defer func() {
  599. if r := recover(); r != nil {
  600. p.Logger.Error("peer message processing panic", "err", r, "stack", string(debug.Stack()))
  601. p.onError(fmt.Errorf("panic during peer message processing: %v", r))
  602. }
  603. }()
  604. for {
  605. chID, msg, err := p.conn.ReceiveMessage()
  606. if err != nil {
  607. p.onError(err)
  608. return
  609. }
  610. reactor, ok := p.reactors[chID]
  611. if !ok {
  612. p.onError(fmt.Errorf("unknown channel %v", chID))
  613. return
  614. }
  615. reactor.Receive(chID, p, msg)
  616. }
  617. }
  618. // FlushStop mimics OnStop but additionally ensures that all successful
  619. // .Send() calls will get flushed before closing the connection.
  620. // NOTE: it is not safe to call this method more than once.
  621. func (p *peer) FlushStop() {
  622. p.metricsTicker.Stop()
  623. p.BaseService.OnStop()
  624. if err := p.conn.FlushClose(); err != nil {
  625. p.Logger.Debug("error while stopping peer", "err", err)
  626. }
  627. }
  628. // OnStop implements BaseService.
  629. func (p *peer) OnStop() {
  630. p.metricsTicker.Stop()
  631. p.BaseService.OnStop()
  632. if err := p.conn.Close(); err != nil {
  633. p.Logger.Debug("error while stopping peer", "err", err)
  634. }
  635. }
  636. //---------------------------------------------------
  637. // Implements Peer
  638. // ID returns the peer's ID - the hex encoded hash of its pubkey.
  639. func (p *peer) ID() NodeID {
  640. return p.nodeInfo.ID()
  641. }
  642. // IsOutbound returns true if the connection is outbound, false otherwise.
  643. func (p *peer) IsOutbound() bool {
  644. return p.peerConn.outbound
  645. }
  646. // IsPersistent returns true if the peer is persitent, false otherwise.
  647. func (p *peer) IsPersistent() bool {
  648. return p.peerConn.persistent
  649. }
  650. // NodeInfo returns a copy of the peer's NodeInfo.
  651. func (p *peer) NodeInfo() NodeInfo {
  652. return p.nodeInfo
  653. }
  654. // SocketAddr returns the address of the socket.
  655. // For outbound peers, it's the address dialed (after DNS resolution).
  656. // For inbound peers, it's the address returned by the underlying connection
  657. // (not what's reported in the peer's NodeInfo).
  658. func (p *peer) SocketAddr() *NetAddress {
  659. return p.peerConn.conn.RemoteEndpoint().NetAddress()
  660. }
  661. // Status returns the peer's ConnectionStatus.
  662. func (p *peer) Status() tmconn.ConnectionStatus {
  663. return p.conn.Status()
  664. }
  665. // Send msg bytes to the channel identified by chID byte. Returns false if the
  666. // send queue is full after timeout, specified by MConnection.
  667. func (p *peer) Send(chID byte, msgBytes []byte) bool {
  668. if !p.IsRunning() {
  669. // see Switch#Broadcast, where we fetch the list of peers and loop over
  670. // them - while we're looping, one peer may be removed and stopped.
  671. return false
  672. } else if !p.hasChannel(chID) {
  673. return false
  674. }
  675. res, err := p.conn.SendMessage(chID, msgBytes)
  676. if err == io.EOF {
  677. return false
  678. } else if err != nil {
  679. p.onError(err)
  680. return false
  681. }
  682. if res {
  683. labels := []string{
  684. "peer_id", string(p.ID()),
  685. "chID", fmt.Sprintf("%#x", chID),
  686. }
  687. p.metrics.PeerSendBytesTotal.With(labels...).Add(float64(len(msgBytes)))
  688. }
  689. return res
  690. }
  691. // TrySend msg bytes to the channel identified by chID byte. Immediately returns
  692. // false if the send queue is full.
  693. func (p *peer) TrySend(chID byte, msgBytes []byte) bool {
  694. if !p.IsRunning() {
  695. return false
  696. } else if !p.hasChannel(chID) {
  697. return false
  698. }
  699. res, err := p.conn.TrySendMessage(chID, msgBytes)
  700. if err == io.EOF {
  701. return false
  702. } else if err != nil {
  703. p.onError(err)
  704. return false
  705. }
  706. if res {
  707. labels := []string{
  708. "peer_id", string(p.ID()),
  709. "chID", fmt.Sprintf("%#x", chID),
  710. }
  711. p.metrics.PeerSendBytesTotal.With(labels...).Add(float64(len(msgBytes)))
  712. }
  713. return res
  714. }
  715. // Get the data for a given key.
  716. func (p *peer) Get(key string) interface{} {
  717. return p.Data.Get(key)
  718. }
  719. // Set sets the data for the given key.
  720. func (p *peer) Set(key string, data interface{}) {
  721. p.Data.Set(key, data)
  722. }
  723. // hasChannel returns true if the peer reported
  724. // knowing about the given chID.
  725. func (p *peer) hasChannel(chID byte) bool {
  726. for _, ch := range p.channels {
  727. if ch == chID {
  728. return true
  729. }
  730. }
  731. // NOTE: probably will want to remove this
  732. // but could be helpful while the feature is new
  733. p.Logger.Debug(
  734. "Unknown channel for peer",
  735. "channel",
  736. chID,
  737. "channels",
  738. p.channels,
  739. )
  740. return false
  741. }
  742. // CloseConn closes original connection. Used for cleaning up in cases where the peer had not been started at all.
  743. func (p *peer) CloseConn() error {
  744. return p.peerConn.conn.Close()
  745. }
  746. //---------------------------------------------------
  747. // methods only used for testing
  748. // TODO: can we remove these?
  749. // CloseConn closes the underlying connection
  750. func (pc *peerConn) CloseConn() {
  751. pc.conn.Close()
  752. }
  753. // RemoteAddr returns peer's remote network address.
  754. func (p *peer) RemoteAddr() net.Addr {
  755. endpoint := p.conn.RemoteEndpoint()
  756. return &net.TCPAddr{
  757. IP: endpoint.IP,
  758. Port: int(endpoint.Port),
  759. }
  760. }
  761. //---------------------------------------------------
  762. func PeerMetrics(metrics *Metrics) PeerOption {
  763. return func(p *peer) {
  764. p.metrics = metrics
  765. }
  766. }
  767. func (p *peer) metricsReporter() {
  768. for {
  769. select {
  770. case <-p.metricsTicker.C:
  771. status := p.conn.Status()
  772. var sendQueueSize float64
  773. for _, chStatus := range status.Channels {
  774. sendQueueSize += float64(chStatus.SendQueueSize)
  775. }
  776. p.metrics.PeerPendingSendBytes.With("peer_id", string(p.ID())).Set(sendQueueSize)
  777. case <-p.Quit():
  778. return
  779. }
  780. }
  781. }