Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stratum v2 connman #50

Open
wants to merge 22 commits into
base: sv2-transport
Choose a base branch
from
Open

Stratum v2 connman #50

wants to merge 22 commits into from

Conversation

Sjors
Copy link
Owner

@Sjors Sjors commented Jul 19, 2024

Based on:

Followed by #49. Parent PR #68

This PR introduces Sv2Connman, a subclass of SockMan (bitcoin#30988). It uses the Sv2Transport introduced in bitcoin#30315 to enable incoming connections from other Stratum v2 roles.

It's main target user is the Template Provider role introduced in #49.

There may be other Stratum v2 roles we want to support in the future.

Earlier version(s): bitcoin#30332


Note to self, to keep this rebased until bitcoin#30205 and bitcoin#30988 land (2 merge commits and 3 normal commits):

git rebase --rebase-merges HEAD~4 --onto sv2-transport

This won't include changes in the original branches. To achieve that:

git fetch vasild
git reset --hard sv2-transport
git merge vasild/sockman
git cherry-pick sjors/2024/06/sv2_connection~2^..sjors/2024/06/sv2_connection

Note that this code will most likely not be upstreamed to Bitcoin Core, but instead used to create a standalone c++ application that connects to a running node via an IPC interface. See bitcoin#31098.

@Sjors
Copy link
Owner Author

Sjors commented Jul 19, 2024

@pinheadmz wrote in the original PR:

I am working on a libevent-replacing HTTP server using netbase primitives and yeah, I have some functions that look a lot like the sv2connamn in this branch.

I think it would be cool if possible to abstract the mechanisms in ThreadSocketHandler to work on "abstract clients" and I'd be happy to review and collaborate on that.

I plan to take a look at this.

@Sjors
Copy link
Owner Author

Sjors commented Aug 29, 2024

CMake rebase.

@Sjors Sjors force-pushed the 2024/06/sv2_connection branch from 6b5ee20 to 4f957ee Compare August 29, 2024 11:37
@Sjors Sjors force-pushed the 2024/06/sv2_transport branch from 922fd8a to 50b6eb8 Compare August 29, 2024 11:47
@Sjors Sjors force-pushed the 2024/06/sv2_connection branch from 4f957ee to b69544c Compare August 29, 2024 11:49
@Sjors Sjors force-pushed the 2024/06/sv2_transport branch from 50b6eb8 to 6a02367 Compare September 5, 2024 12:48
@Sjors Sjors force-pushed the 2024/06/sv2_connection branch from b69544c to 1ca68d2 Compare September 5, 2024 12:57
@Sjors Sjors force-pushed the 2024/06/sv2_transport branch from 6a02367 to ce4269c Compare September 10, 2024 14:50
@Sjors Sjors force-pushed the 2024/06/sv2_connection branch from 1ca68d2 to b578e0a Compare September 10, 2024 14:54
@Sjors Sjors force-pushed the 2024/06/sv2_transport branch 3 times, most recently from 8220337 to 5fca2c5 Compare September 19, 2024 14:40
@Sjors Sjors force-pushed the 2024/06/sv2_connection branch from b578e0a to 489c9fb Compare September 19, 2024 15:02
@Sjors Sjors force-pushed the 2024/06/sv2_transport branch from 5fca2c5 to dfb9a6d Compare September 20, 2024 09:13
@Sjors Sjors force-pushed the 2024/06/sv2_connection branch from 489c9fb to a55fa83 Compare September 20, 2024 09:17
@Sjors
Copy link
Owner Author

Sjors commented Sep 20, 2024

Rebased to move everything into a bitcoin_sv2 library (use -DWITH_SV2=ON to compile).

Comment on lines +114 to +115
using Clients = std::unordered_map<NodeId, std::shared_ptr<Sv2Client>>;
Clients m_sv2_clients GUARDED_BY(m_clients_mutex);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need a lock? I'm curious because in HTTP I think all client-related operations take place exclusively in the Sockman I/O thread.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think because of DisconnectFlagged(), GetClientById() or ForEachClient() - or for the test-only (Fully)ConnectedClients(). But it could be a left-over of earlier versions.

Copy link
Owner Author

@Sjors Sjors Feb 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In HTTP you probably don't have a need to "proactively" talk to a specific client.

Are http connections ephemeral?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HTTP connections can persist ("keep-alive") and send more requests over time. My equivalents to DisconnectFlagged and GetClientById still take place inside the single sockman IO thread though so even though I started copying your code, I think I can get away with removing the lock

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TSan will tell you

@Sjors Sjors force-pushed the 2024/06/sv2_connection branch from 7fb1a19 to 2503578 Compare February 10, 2025 09:14
@Sjors
Copy link
Owner Author

Sjors commented Feb 10, 2025

Rebased on the latest bitcoin#30988.

* `CConnman::CalculateKeyedNetGroup()` needs `CNetAddr`, not `CAddress`,
  thus change its argument.

* Both callers of `CConnman::CreateNodeFromAcceptedSocket()` create a
  dummy `CAddress` from `CService`, so use `CService` instead.

* `GetBindAddress()` only needs to return `CAddress`.

* `CNode::addrBind` does not need to be `CAddress`.
They were coupled in `struct ListenSocket`, but the socket belongs to
the lower level transport protocol, whereas the permissions are specific
to the higher Bitcoin P2P protocol.
Now that `CConnman::ListenSocket` is a `struct` that contains only one
member variable of type `std::shared_ptr<Sock>`, drop `ListenSocket` and
use `shared_ptr` directly.

Replace the vector of `ListenSocket` with a vector of `shared_ptr`.
Introduce a new low-level socket managing class `SockMan`
and move the `CConnman::BindListenPort()` method to it.
It was copied verbatim from `CConnman::BindListenPort()` in the previous
commit. Modernize its variables and style and log the error messages
from the caller. Also categorize the informative messages to the "net"
category because they are quite specific to the networking layer.
Move the `CConnman::AcceptConnection()` method to `SockMan` and split
parts of it:
* the flip-to-CJDNS part: to just after the `AcceptConnection()` call
* the permissions part: at the start of `CreateNodeFromAcceptedSocket()`
Move `CConnman::GetNewNodeId()` to `SockMan::GetNewId()`. Avoid using
the word "node" because that is too specific for `CConnman`.
CConnman-specific or in other words, Bitcoin P2P specific. Now
the `ThreadI2PAcceptIncoming()` method is protocol agnostic and
can be moved to `SockMan`.
Change `CConnman::m_nodes` from `std::vector<CNode*>` to
`std::unordered_map<NodeId, CNode*>` because interaction
between `CConnman` and `SockMan` is going to be based on
`NodeId` and finding a node by its id would better be fast.

Change `PeerManagerImpl::EvictExtraOutboundPeers()` to account for nodes
no longer always being in order of id. The old code would have failed to
update `next_youngest_peer` correctly if `CConnman::m_nodes` hadn't
always had nodes in ascending order of id.

During fuzzing make sure that we don't generate duplicate `CNode` ids.
The easiest way to do that is to use sequential ids.

As a nice side effect the existent search-by-id operations in
`CConnman::AttemptToEvictConnection()`,
`CConnman::DisconnectNode()` and
`CConnman::ForNode()` now become `O(1)` (were `O(number of nodes)`),
as well as the erase in `CConnman::DisconnectNodes()`.
Move the parts of `CConnman::GenerateWaitSockets()` that are specific to
the Bitcoin-P2P protocol to dedicated methods:
`ShouldTryToSend()` and `ShouldTryToRecv()`.

This brings us one step closer to moving `GenerateWaitSockets()` to the
protocol agnostic `SockMan` (which would call `ShouldTry...()` from
`CConnman`).
…cketHandler()

Move some parts of `CConnman::SocketHandlerConnected()` and
`CConnman::ThreadSocketHandler()` that are specific to the Bitcoin-P2P
protocol to dedicated methods:
`EventIOLoopCompletedForOne(id)` and
`EventIOLoopCompletedForAll()`.

This brings us one step closer to moving `SocketHandlerConnected()` and
`ThreadSocketHandler()` to the protocol agnostic `SockMan` (which would
call `EventIOLoopCompleted...()` from `CConnman`).
Introduce 4 new methods for the interaction between `CConnman` and
`SockMan`:

* `EventReadyToSend()`:
  called when there is readiness to send and do the actual sending of data.

* `EventGotData()`, `EventGotEOF()`, `EventGotPermanentReadError()`:
  called when the corresponing recv events occur.

These methods contain logic that is specific to the Bitcoin-P2P protocol
and move it away from `CConnman::SocketHandlerConnected()` which will
become a protocol agnostic method of `SockMan`.

Also, move the counting of sent bytes to `CConnman::SocketSendData()` -
both callers of that method called `RecordBytesSent()` just after the
call, so move it from the callers to inside
`CConnman::SocketSendData()`.
Move the protocol agnostic parts of `CConnman::ConnectNode()` into
`SockMan::ConnectAndMakeId()` and leave the Bitcoin-P2P specific
stuff in `CConnman::ConnectNode()`.

Move the protocol agnostic `CConnman::m_unused_i2p_sessions`, its mutex
and `MAX_UNUSED_I2P_SESSIONS_SIZE` to `SockMan`.

Move `GetBindAddress()` from `net.cpp` to `sockman.cpp`.
Move `MaybeFlipIPv6toCJDNS()`, which is Bitcoin P2P specific from the
callers of `CConnman::EventNewConnectionAccepted()` to inside that
method.

Move the IsSelectable check, the `TCP_NODELAY` option set and the
generation of new connection id out of
`CConnman::EventNewConnectionAccepted()` because those are protocol
agnostic. Move those to a new method `SockMan::NewSockAccepted()` which
is called instead of `CConnman::EventNewConnectionAccepted()`.
Move `CNode::m_sock` and `CNode::m_i2p_sam_session` to `SockMan::m_connected`.
Also move all the code that handles sockets to `SockMan`.

`CNode::CloseSocketDisconnect()` becomes
`CConnman::MarkAsDisconnectAndCloseConnection()`.

`CConnman::SocketSendData()` is renamed to
`CConnman::SendMessagesAsBytes()` and its sockets-touching bits are moved to
`SockMan::SendBytes()`.

`CConnman::GenerateWaitSockets()` goes to
`SockMan::GenerateWaitSockets()`.

`CConnman::ThreadSocketHandler()` and
`CConnman::SocketHandler()` are combined into
`SockMan::ThreadSocketHandler()`.

`CConnman::SocketHandlerConnected()` goes to
`SockMan::SocketHandlerConnected()`.

`CConnman::SocketHandlerListening()` goes to
`SockMan::SocketHandlerListening()`.
`SockMan` members

`AcceptConnection()`
`NewSockAccepted()`
`GetNewId()`
`m_i2p_sam_session`
`m_listen`

are now used only by `SockMan`, thus make them private.
@Sjors Sjors force-pushed the 2024/06/sv2_connection branch from 2503578 to 360aedd Compare February 10, 2025 14:31
@Sjors
Copy link
Owner Author

Sjors commented Feb 10, 2025

Rebased after bitcoin#30205 landed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants