Skip to content

Commit 30874a7

Browse files
committed
Merge bitcoin/bitcoin#26837: I2P network optimizations
3c1de03 i2p: use consistent number of tunnels with i2pd and Java I2P (Vasil Dimov) 801b405 i2p: lower the number of tunnels for transient sessions (Vasil Dimov) b906b64 i2p: reuse created I2P sessions if not used (Vasil Dimov) Pull request description: * Reuse an I2P transient session instead of discarding it if we failed to connect to the desired peer. This means we never used the generated address (destination), whose creation is not cheap. This does not mean that we will use the same address for more than one peer. * Lower the number of tunnels for transient sessions. * Explicitly specify the number of tunnels for persistent sessions instead of relying on the defaults which differ between I2P routers. This way we get consistent behavior with all routers. Alleviates: bitcoin/bitcoin#26754 (I have not tested this with i2pd, yet) ACKs for top commit: jonatack: ACK 3c1de03 mzumsande: Light ACK 3c1de03 Tree-SHA512: 477b4b9a5755e6a9a46bc0f7b268fa419dff4414e25445c750ae913f7552d9e2313f2aca4e3b70067b8390c2d0c2d68ec459f331765e939fc84139e454031cd4
2 parents c6e65a1 + 3c1de03 commit 30874a7

File tree

3 files changed

+54
-9
lines changed

3 files changed

+54
-9
lines changed

src/i2p.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,9 @@ void Session::CreateIfNotCreatedAlready()
380380
// in the reply in DESTINATION=.
381381
const Reply& reply = SendRequestAndGetReply(
382382
*sock,
383-
strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=TRANSIENT SIGNATURE_TYPE=7", session_id));
383+
strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=TRANSIENT SIGNATURE_TYPE=7 "
384+
"inbound.quantity=1 outbound.quantity=1",
385+
session_id));
384386

385387
m_private_key = DecodeI2PBase64(reply.Get("DESTINATION"));
386388
} else {
@@ -396,7 +398,8 @@ void Session::CreateIfNotCreatedAlready()
396398
const std::string& private_key_b64 = SwapBase64(EncodeBase64(m_private_key));
397399

398400
SendRequestAndGetReply(*sock,
399-
strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=%s",
401+
strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=%s "
402+
"inbound.quantity=3 outbound.quantity=3",
400403
session_id,
401404
private_key_b64));
402405
}

src/net.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ static CAddress GetBindAddress(const Sock& sock)
436436

437437
CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type)
438438
{
439+
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
439440
assert(conn_type != ConnectionType::INBOUND);
440441

441442
if (pszDest == nullptr) {
@@ -496,8 +497,23 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
496497
if (m_i2p_sam_session) {
497498
connected = m_i2p_sam_session->Connect(addrConnect, conn, proxyConnectionFailed);
498499
} else {
499-
i2p_transient_session = std::make_unique<i2p::sam::Session>(proxy.proxy, &interruptNet);
500+
{
501+
LOCK(m_unused_i2p_sessions_mutex);
502+
if (m_unused_i2p_sessions.empty()) {
503+
i2p_transient_session =
504+
std::make_unique<i2p::sam::Session>(proxy.proxy, &interruptNet);
505+
} else {
506+
i2p_transient_session.swap(m_unused_i2p_sessions.front());
507+
m_unused_i2p_sessions.pop();
508+
}
509+
}
500510
connected = i2p_transient_session->Connect(addrConnect, conn, proxyConnectionFailed);
511+
if (!connected) {
512+
LOCK(m_unused_i2p_sessions_mutex);
513+
if (m_unused_i2p_sessions.size() < MAX_UNUSED_I2P_SESSIONS_SIZE) {
514+
m_unused_i2p_sessions.emplace(i2p_transient_session.release());
515+
}
516+
}
501517
}
502518

503519
if (connected) {
@@ -1053,6 +1069,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
10531069

10541070
bool CConnman::AddConnection(const std::string& address, ConnectionType conn_type)
10551071
{
1072+
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
10561073
std::optional<int> max_connections;
10571074
switch (conn_type) {
10581075
case ConnectionType::INBOUND:
@@ -1517,6 +1534,7 @@ void CConnman::DumpAddresses()
15171534

15181535
void CConnman::ProcessAddrFetch()
15191536
{
1537+
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
15201538
std::string strDest;
15211539
{
15221540
LOCK(m_addr_fetches_mutex);
@@ -1598,6 +1616,7 @@ std::unordered_set<Network> CConnman::GetReachableEmptyNetworks() const
15981616

15991617
void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
16001618
{
1619+
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
16011620
SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_OPEN_CONNECTION);
16021621
FastRandomContext rng;
16031622
// Connect to specific addresses
@@ -1947,6 +1966,7 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
19471966

19481967
void CConnman::ThreadOpenAddedConnections()
19491968
{
1969+
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
19501970
SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_ADD_CONNECTION);
19511971
while (true)
19521972
{
@@ -1976,6 +1996,7 @@ void CConnman::ThreadOpenAddedConnections()
19761996
// if successful, this moves the passed grant to the constructed node
19771997
void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, ConnectionType conn_type)
19781998
{
1999+
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
19792000
assert(conn_type != ConnectionType::INBOUND);
19802001

19812002
//

src/net.h

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <map>
3939
#include <memory>
4040
#include <optional>
41+
#include <queue>
4142
#include <thread>
4243
#include <unordered_set>
4344
#include <vector>
@@ -744,7 +745,7 @@ class CConnman
744745
bool GetNetworkActive() const { return fNetworkActive; };
745746
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
746747
void SetNetworkActive(bool active);
747-
void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant* grantOutbound, const char* strDest, ConnectionType conn_type);
748+
void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant* grantOutbound, const char* strDest, ConnectionType conn_type) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
748749
bool CheckIncomingNonce(uint64_t nonce);
749750

750751
bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
@@ -820,7 +821,7 @@ class CConnman
820821
* - Max total outbound connection capacity filled
821822
* - Max connection capacity for type is filled
822823
*/
823-
bool AddConnection(const std::string& address, ConnectionType conn_type);
824+
bool AddConnection(const std::string& address, ConnectionType conn_type) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
824825

825826
size_t GetNodeCount(ConnectionDirection) const;
826827
void GetNodeStats(std::vector<CNodeStats>& vstats) const;
@@ -886,10 +887,10 @@ class CConnman
886887
bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions);
887888
bool InitBinds(const Options& options);
888889

889-
void ThreadOpenAddedConnections() EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
890+
void ThreadOpenAddedConnections() EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex, !m_unused_i2p_sessions_mutex);
890891
void AddAddrFetch(const std::string& strDest) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex);
891-
void ProcessAddrFetch() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex);
892-
void ThreadOpenConnections(std::vector<std::string> connect) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_added_nodes_mutex, !m_nodes_mutex);
892+
void ProcessAddrFetch() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_unused_i2p_sessions_mutex);
893+
void ThreadOpenConnections(std::vector<std::string> connect) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_added_nodes_mutex, !m_nodes_mutex, !m_unused_i2p_sessions_mutex);
893894
void ThreadMessageHandler() EXCLUSIVE_LOCKS_REQUIRED(!mutexMsgProc);
894895
void ThreadI2PAcceptIncoming();
895896
void AcceptConnection(const ListenSocket& hListenSocket);
@@ -956,7 +957,7 @@ class CConnman
956957
bool AlreadyConnectedToAddress(const CAddress& addr);
957958

958959
bool AttemptToEvictConnection();
959-
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type);
960+
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
960961
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const;
961962

962963
void DeleteNode(CNode* pnode);
@@ -1133,6 +1134,26 @@ class CConnman
11331134
*/
11341135
std::vector<CService> m_onion_binds;
11351136

1137+
/**
1138+
* Mutex protecting m_i2p_sam_sessions.
1139+
*/
1140+
Mutex m_unused_i2p_sessions_mutex;
1141+
1142+
/**
1143+
* A pool of created I2P SAM transient sessions that should be used instead
1144+
* of creating new ones in order to reduce the load on the I2P network.
1145+
* Creating a session in I2P is not cheap, thus if this is not empty, then
1146+
* pick an entry from it instead of creating a new session. If connecting to
1147+
* a host fails, then the created session is put to this pool for reuse.
1148+
*/
1149+
std::queue<std::unique_ptr<i2p::sam::Session>> m_unused_i2p_sessions GUARDED_BY(m_unused_i2p_sessions_mutex);
1150+
1151+
/**
1152+
* Cap on the size of `m_unused_i2p_sessions`, to ensure it does not
1153+
* unexpectedly use too much memory.
1154+
*/
1155+
static constexpr size_t MAX_UNUSED_I2P_SESSIONS_SIZE{10};
1156+
11361157
/**
11371158
* RAII helper to atomically create a copy of `m_nodes` and add a reference
11381159
* to each of the nodes. The nodes are released when this object is destroyed.

0 commit comments

Comments
 (0)