diff --git a/src/common/transport.h b/src/common/transport.h new file mode 100644 index 00000000000000..3a7987b102416c --- /dev/null +++ b/src/common/transport.h @@ -0,0 +1,189 @@ +// Copyright (c) 2024 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_COMMON_TRANSPORT_H +#define BITCOIN_COMMON_TRANSPORT_H + +#include +#include +#include +#include +#include +#include + +/** Transport layer version */ +enum class TransportProtocolType : uint8_t { + DETECTING, //!< Peer could be v1 or v2 + V1, //!< Unencrypted, plaintext protocol + V2, //!< BIP324 protocol +}; + +/** Convert TransportProtocolType enum to a string value */ +std::string TransportTypeAsString(TransportProtocolType transport_type); + +/** Transport protocol agnostic message container. + * Ideally it should only contain receive time, payload, + * type and size. + */ +class CNetMessage +{ +public: + DataStream m_recv; //!< received message data + std::chrono::microseconds m_time{0}; //!< time of message receipt + uint32_t m_message_size{0}; //!< size of the payload + uint32_t m_raw_message_size{0}; //!< used wire size of the message (including header/checksum) + std::string m_type; + + explicit CNetMessage(DataStream&& recv_in) : m_recv(std::move(recv_in)) {} + // Only one CNetMessage object will exist for the same message on either + // the receive or processing queue. For performance reasons we therefore + // delete the copy constructor and assignment operator to avoid the + // possibility of copying CNetMessage objects. + CNetMessage(CNetMessage&&) = default; + CNetMessage(const CNetMessage&) = delete; + CNetMessage& operator=(CNetMessage&&) = default; + CNetMessage& operator=(const CNetMessage&) = delete; + + /** Compute total memory usage of this object (own memory + any dynamic memory). */ + size_t GetMemoryUsage() const noexcept; +}; + +struct CSerializedNetMsg { + CSerializedNetMsg() = default; + CSerializedNetMsg(CSerializedNetMsg&&) = default; + CSerializedNetMsg& operator=(CSerializedNetMsg&&) = default; + // No implicit copying, only moves. + CSerializedNetMsg(const CSerializedNetMsg& msg) = delete; + CSerializedNetMsg& operator=(const CSerializedNetMsg&) = delete; + + CSerializedNetMsg Copy() const + { + CSerializedNetMsg copy; + copy.data = data; + copy.m_type = m_type; + return copy; + } + + std::vector data; + std::string m_type; + + /** Compute total memory usage of this object (own memory + any dynamic memory). */ + size_t GetMemoryUsage() const noexcept; +}; + +/** The Transport converts one connection's sent messages to wire bytes, and received bytes back. */ +class Transport { +public: + virtual ~Transport() = default; + + struct Info + { + TransportProtocolType transport_type; + std::optional session_id; + }; + + /** Retrieve information about this transport. */ + virtual Info GetInfo() const noexcept = 0; + + // 1. Receiver side functions, for decoding bytes received on the wire into transport protocol + // agnostic CNetMessage (message type & payload) objects. + + /** Returns true if the current message is complete (so GetReceivedMessage can be called). */ + virtual bool ReceivedMessageComplete() const = 0; + + /** Feed wire bytes to the transport. + * + * @return false if some bytes were invalid, in which case the transport can't be used anymore. + * + * Consumed bytes are chopped off the front of msg_bytes. + */ + virtual bool ReceivedBytes(Span& msg_bytes) = 0; + + /** Retrieve a completed message from transport. + * + * This can only be called when ReceivedMessageComplete() is true. + * + * If reject_message=true is returned the message itself is invalid, but (other than false + * returned by ReceivedBytes) the transport is not in an inconsistent state. + */ + virtual CNetMessage GetReceivedMessage(std::chrono::microseconds time, bool& reject_message) = 0; + + // 2. Sending side functions, for converting messages into bytes to be sent over the wire. + + /** Set the next message to send. + * + * If no message can currently be set (perhaps because the previous one is not yet done being + * sent), returns false, and msg will be unmodified. Otherwise msg is enqueued (and + * possibly moved-from) and true is returned. + */ + virtual bool SetMessageToSend(CSerializedNetMsg& msg) noexcept = 0; + + /** Return type for GetBytesToSend, consisting of: + * - Span to_send: span of bytes to be sent over the wire (possibly empty). + * - bool more: whether there will be more bytes to be sent after the ones in to_send are + * all sent (as signaled by MarkBytesSent()). + * - const std::string& m_type: message type on behalf of which this is being sent + * ("" for bytes that are not on behalf of any message). + */ + using BytesToSend = std::tuple< + Span /*to_send*/, + bool /*more*/, + const std::string& /*m_type*/ + >; + + /** Get bytes to send on the wire, if any, along with other information about it. + * + * As a const function, it does not modify the transport's observable state, and is thus safe + * to be called multiple times. + * + * @param[in] have_next_message If true, the "more" return value reports whether more will + * be sendable after a SetMessageToSend call. It is set by the caller when they know + * they have another message ready to send, and only care about what happens + * after that. The have_next_message argument only affects this "more" return value + * and nothing else. + * + * Effectively, there are three possible outcomes about whether there are more bytes + * to send: + * - Yes: the transport itself has more bytes to send later. For example, for + * V1Transport this happens during the sending of the header of a + * message, when there is a non-empty payload that follows. + * - No: the transport itself has no more bytes to send, but will have bytes to + * send if handed a message through SetMessageToSend. In V1Transport this + * happens when sending the payload of a message. + * - Blocked: the transport itself has no more bytes to send, and is also incapable + * of sending anything more at all now, if it were handed another + * message to send. This occurs in V2Transport before the handshake is + * complete, as the encryption ciphers are not set up for sending + * messages before that point. + * + * The boolean 'more' is true for Yes, false for Blocked, and have_next_message + * controls what is returned for No. + * + * @return a BytesToSend object. The to_send member returned acts as a stream which is only + * ever appended to. This means that with the exception of MarkBytesSent (which pops + * bytes off the front of later to_sends), operations on the transport can only append + * to what is being returned. Also note that m_type and to_send refer to data that is + * internal to the transport, and calling any non-const function on this object may + * invalidate them. + */ + virtual BytesToSend GetBytesToSend(bool have_next_message) const noexcept = 0; + + /** Report how many bytes returned by the last GetBytesToSend() have been sent. + * + * bytes_sent cannot exceed to_send.size() of the last GetBytesToSend() result. + * + * If bytes_sent=0, this call has no effect. + */ + virtual void MarkBytesSent(size_t bytes_sent) noexcept = 0; + + /** Return the memory usage of this transport attributable to buffered data to send. */ + virtual size_t GetSendMemoryUsage() const noexcept = 0; + + // 3. Miscellaneous functions. + + /** Whether upon disconnections, a reconnect with V1 is warranted. */ + virtual bool ShouldReconnectV1() const noexcept = 0; +}; + +#endif // BITCOIN_COMMON_TRANSPORT_H diff --git a/src/net.h b/src/net.h index fc096ff7b8680d..a009bb683ecf5c 100644 --- a/src/net.h +++ b/src/net.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -111,29 +112,6 @@ struct AddedNodeInfo { class CNodeStats; class CClientUIInterface; -struct CSerializedNetMsg { - CSerializedNetMsg() = default; - CSerializedNetMsg(CSerializedNetMsg&&) = default; - CSerializedNetMsg& operator=(CSerializedNetMsg&&) = default; - // No implicit copying, only moves. - CSerializedNetMsg(const CSerializedNetMsg& msg) = delete; - CSerializedNetMsg& operator=(const CSerializedNetMsg&) = delete; - - CSerializedNetMsg Copy() const - { - CSerializedNetMsg copy; - copy.data = data; - copy.m_type = m_type; - return copy; - } - - std::vector data; - std::string m_type; - - /** Compute total memory usage of this object (own memory + any dynamic memory). */ - size_t GetMemoryUsage() const noexcept; -}; - /** * Look up IP addresses from all interfaces on the machine and add them to the * list of local addresses to self-advertise. @@ -222,148 +200,6 @@ class CNodeStats std::string m_session_id; }; - -/** Transport protocol agnostic message container. - * Ideally it should only contain receive time, payload, - * type and size. - */ -class CNetMessage -{ -public: - DataStream m_recv; //!< received message data - std::chrono::microseconds m_time{0}; //!< time of message receipt - uint32_t m_message_size{0}; //!< size of the payload - uint32_t m_raw_message_size{0}; //!< used wire size of the message (including header/checksum) - std::string m_type; - - explicit CNetMessage(DataStream&& recv_in) : m_recv(std::move(recv_in)) {} - // Only one CNetMessage object will exist for the same message on either - // the receive or processing queue. For performance reasons we therefore - // delete the copy constructor and assignment operator to avoid the - // possibility of copying CNetMessage objects. - CNetMessage(CNetMessage&&) = default; - CNetMessage(const CNetMessage&) = delete; - CNetMessage& operator=(CNetMessage&&) = default; - CNetMessage& operator=(const CNetMessage&) = delete; - - /** Compute total memory usage of this object (own memory + any dynamic memory). */ - size_t GetMemoryUsage() const noexcept; -}; - -/** The Transport converts one connection's sent messages to wire bytes, and received bytes back. */ -class Transport { -public: - virtual ~Transport() = default; - - struct Info - { - TransportProtocolType transport_type; - std::optional session_id; - }; - - /** Retrieve information about this transport. */ - virtual Info GetInfo() const noexcept = 0; - - // 1. Receiver side functions, for decoding bytes received on the wire into transport protocol - // agnostic CNetMessage (message type & payload) objects. - - /** Returns true if the current message is complete (so GetReceivedMessage can be called). */ - virtual bool ReceivedMessageComplete() const = 0; - - /** Feed wire bytes to the transport. - * - * @return false if some bytes were invalid, in which case the transport can't be used anymore. - * - * Consumed bytes are chopped off the front of msg_bytes. - */ - virtual bool ReceivedBytes(Span& msg_bytes) = 0; - - /** Retrieve a completed message from transport. - * - * This can only be called when ReceivedMessageComplete() is true. - * - * If reject_message=true is returned the message itself is invalid, but (other than false - * returned by ReceivedBytes) the transport is not in an inconsistent state. - */ - virtual CNetMessage GetReceivedMessage(std::chrono::microseconds time, bool& reject_message) = 0; - - // 2. Sending side functions, for converting messages into bytes to be sent over the wire. - - /** Set the next message to send. - * - * If no message can currently be set (perhaps because the previous one is not yet done being - * sent), returns false, and msg will be unmodified. Otherwise msg is enqueued (and - * possibly moved-from) and true is returned. - */ - virtual bool SetMessageToSend(CSerializedNetMsg& msg) noexcept = 0; - - /** Return type for GetBytesToSend, consisting of: - * - Span to_send: span of bytes to be sent over the wire (possibly empty). - * - bool more: whether there will be more bytes to be sent after the ones in to_send are - * all sent (as signaled by MarkBytesSent()). - * - const std::string& m_type: message type on behalf of which this is being sent - * ("" for bytes that are not on behalf of any message). - */ - using BytesToSend = std::tuple< - Span /*to_send*/, - bool /*more*/, - const std::string& /*m_type*/ - >; - - /** Get bytes to send on the wire, if any, along with other information about it. - * - * As a const function, it does not modify the transport's observable state, and is thus safe - * to be called multiple times. - * - * @param[in] have_next_message If true, the "more" return value reports whether more will - * be sendable after a SetMessageToSend call. It is set by the caller when they know - * they have another message ready to send, and only care about what happens - * after that. The have_next_message argument only affects this "more" return value - * and nothing else. - * - * Effectively, there are three possible outcomes about whether there are more bytes - * to send: - * - Yes: the transport itself has more bytes to send later. For example, for - * V1Transport this happens during the sending of the header of a - * message, when there is a non-empty payload that follows. - * - No: the transport itself has no more bytes to send, but will have bytes to - * send if handed a message through SetMessageToSend. In V1Transport this - * happens when sending the payload of a message. - * - Blocked: the transport itself has no more bytes to send, and is also incapable - * of sending anything more at all now, if it were handed another - * message to send. This occurs in V2Transport before the handshake is - * complete, as the encryption ciphers are not set up for sending - * messages before that point. - * - * The boolean 'more' is true for Yes, false for Blocked, and have_next_message - * controls what is returned for No. - * - * @return a BytesToSend object. The to_send member returned acts as a stream which is only - * ever appended to. This means that with the exception of MarkBytesSent (which pops - * bytes off the front of later to_sends), operations on the transport can only append - * to what is being returned. Also note that m_type and to_send refer to data that is - * internal to the transport, and calling any non-const function on this object may - * invalidate them. - */ - virtual BytesToSend GetBytesToSend(bool have_next_message) const noexcept = 0; - - /** Report how many bytes returned by the last GetBytesToSend() have been sent. - * - * bytes_sent cannot exceed to_send.size() of the last GetBytesToSend() result. - * - * If bytes_sent=0, this call has no effect. - */ - virtual void MarkBytesSent(size_t bytes_sent) noexcept = 0; - - /** Return the memory usage of this transport attributable to buffered data to send. */ - virtual size_t GetSendMemoryUsage() const noexcept = 0; - - // 3. Miscellaneous functions. - - /** Whether upon disconnections, a reconnect with V1 is warranted. */ - virtual bool ShouldReconnectV1() const noexcept = 0; -}; - class V1Transport final : public Transport { private: diff --git a/src/node/connection_types.cpp b/src/node/connection_types.cpp index 5e4dc5bf2ef94e..2d8dbec2f131cb 100644 --- a/src/node/connection_types.cpp +++ b/src/node/connection_types.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include #include #include diff --git a/src/node/connection_types.h b/src/node/connection_types.h index a911b95f7e917a..5e1abcace67d11 100644 --- a/src/node/connection_types.h +++ b/src/node/connection_types.h @@ -6,7 +6,6 @@ #define BITCOIN_NODE_CONNECTION_TYPES_H #include -#include /** Different types of connections to a peer. This enum encapsulates the * information we have available at the time of opening or accepting the @@ -80,14 +79,4 @@ enum class ConnectionType { /** Convert ConnectionType enum to a string value */ std::string ConnectionTypeAsString(ConnectionType conn_type); -/** Transport layer version */ -enum class TransportProtocolType : uint8_t { - DETECTING, //!< Peer could be v1 or v2 - V1, //!< Unencrypted, plaintext protocol - V2, //!< BIP324 protocol -}; - -/** Convert TransportProtocolType enum to a string value */ -std::string TransportTypeAsString(TransportProtocolType transport_type); - #endif // BITCOIN_NODE_CONNECTION_TYPES_H