Skip to content

Commit

Permalink
[p2p] Use v2 transport between supportive peers
Browse files Browse the repository at this point in the history
  • Loading branch information
dhruv committed Feb 14, 2022
1 parent 20688b0 commit 2155bb8
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 7 deletions.
69 changes: 63 additions & 6 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1416,6 +1416,9 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
}

const bool inbound_onion = std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();

// A listening v2 peer does not know the advertised services for the initiating peer at this point.
// Assume a v1 connection for now.
CNode* pnode = new CNode(id,
nodeServices,
std::move(sock),
Expand Down Expand Up @@ -1813,8 +1816,35 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
if (nBytes > 0)
{
bool notify = false;
if (!pnode->ReceiveMsgBytes({pchBuf, (size_t)nBytes}, notify)) {
pnode->CloseSocketDisconnect();
if (!pnode->m_deserializer ||
!pnode->ReceiveMsgBytes({pchBuf, (size_t)nBytes}, notify)) {
if (!pnode->tried_v2_handshake && nBytes == ELLSQ_ENCODED_SIZE) {
pnode->EnsureInitV2Key(!pnode->IsInboundConn());
EllSqPubKey peer_ellsq;
std::copy(pchBuf, pchBuf + ELLSQ_ENCODED_SIZE, peer_ellsq.data());
CPubKey v2_peer_pubkey = CPubKey(peer_ellsq);

Span<uint8_t> initiator_hdata, responder_hdata;

if (pnode->IsInboundConn()) {
// We are the responder
initiator_hdata = Span(pchBuf, nBytes);
responder_hdata = pnode->hdata_ellsq_pubkey;
} else {
initiator_hdata = pnode->hdata_ellsq_pubkey;
responder_hdata = Span(pchBuf, nBytes);
}

pnode->InitV2P2P(v2_peer_pubkey, initiator_hdata, responder_hdata, !pnode->IsInboundConn());
if (pnode->IsInboundConn()) {
PushV2EllSqPubkey(pnode);
}

pnode->tried_v2_handshake = true;
m_msgproc->InitializeNode(pnode);
} else {
pnode->CloseSocketDisconnect();
}
}
RecordBytesRecv(nBytes);
if (notify) {
Expand Down Expand Up @@ -2490,7 +2520,11 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
if (grantOutbound)
grantOutbound->MoveTo(pnode->grantOutbound);

m_msgproc->InitializeNode(pnode);
if (pnode->PreferV2Conn()) {
PushV2EllSqPubkey(pnode);
} else {
m_msgproc->InitializeNode(pnode);
}
{
LOCK(m_nodes_mutex);
m_nodes.push_back(pnode);
Expand Down Expand Up @@ -3234,8 +3268,12 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, std::shared_ptr<Sock> s
LogPrint(BCLog::NET, "Added connection peer=%d\n", id);
}

m_deserializer = std::make_unique<V1TransportDeserializer>(V1TransportDeserializer(Params(), id, SER_NETWORK, INIT_PROTO_VERSION));
m_serializer = std::make_unique<V1TransportSerializer>(V1TransportSerializer());
if (PreferV2Conn()) {
EnsureInitV2Key(!IsInboundConn());
} else {
m_deserializer = std::make_unique<V1TransportDeserializer>(V1TransportDeserializer(Params(), id, SER_NETWORK, INIT_PROTO_VERSION));
m_serializer = std::make_unique<V1TransportSerializer>(V1TransportSerializer());
}
}

bool CConnman::NodeFullyConnected(const CNode* pnode)
Expand Down Expand Up @@ -3278,7 +3316,9 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
pnode->nSendSize += nTotalSize;

if (pnode->nSendSize > nSendBufferMaxSize) pnode->fPauseSend = true;
pnode->vSendMsg.push_back(std::move(serializedHeader));

// The serializedHeader is empty for v2 p2p messages since all the bytes are in msg.data
if (!serializedHeader.empty()) pnode->vSendMsg.push_back(std::move(serializedHeader));
if (nMessageSize) pnode->vSendMsg.push_back(std::move(msg.data));

// If write queue empty, attempt "optimistic write"
Expand All @@ -3287,6 +3327,23 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
if (nBytesSent) RecordBytesSent(nBytesSent);
}

void CConnman::PushV2EllSqPubkey(CNode* pnode) {
std::vector<unsigned char> ellsq_bytes;
ellsq_bytes.resize(ELLSQ_ENCODED_SIZE);
std::copy(pnode->hdata_ellsq_pubkey.begin(), pnode->hdata_ellsq_pubkey.end(), ellsq_bytes.begin());
size_t nBytesSent;
{
LOCK(pnode->cs_vSend);
pnode->nSendSize += ellsq_bytes.size();
pnode->vSendMsg.push_back(ellsq_bytes);

// Send immediately.
nBytesSent = SocketSendData(*pnode);
}

if (nBytesSent) RecordBytesSent(nBytesSent);
}

bool CConnman::ForNode(NodeId id, std::function<bool(CNode* pnode)> func)
{
CNode* found = nullptr;
Expand Down
43 changes: 42 additions & 1 deletion src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <netbase.h>
#include <policy/feerate.h>
#include <protocol.h>
#include <pubkey.h>
#include <random.h>
#include <span.h>
#include <streams.h>
Expand Down Expand Up @@ -467,7 +468,7 @@ class V2TransportSerializer : public TransportSerializer

constexpr size_t BIP324_KEY_LEN = 32;

using BIP324Key = std::vector<uint8_t, secure_allocator<uint8_t>>;
using BIP324Key = CPrivKey;

struct BIP324Keys {
BIP324Key initiator_F;
Expand Down Expand Up @@ -557,6 +558,7 @@ class CNode
const uint64_t nKeyedNetGroup;
std::atomic_bool fPauseRecv{false};
std::atomic_bool fPauseSend{false};
std::atomic_bool tried_v2_handshake{false};

bool IsOutboundOrBlockRelayConn() const {
switch (m_conn_type) {
Expand Down Expand Up @@ -597,6 +599,10 @@ class CNode
return m_conn_type == ConnectionType::INBOUND;
}

bool PreferV2Conn() const {
return SupportsV2Transport(nServices) && SupportsV2Transport(nLocalServices);
}

bool ExpectServicesFromConn() const {
switch (m_conn_type) {
case ConnectionType::INBOUND:
Expand Down Expand Up @@ -678,6 +684,8 @@ class CNode
* criterium in CConnman::AttemptToEvictConnection. */
std::atomic<std::chrono::microseconds> m_min_ping_time{std::chrono::microseconds::max()};

EllSqPubKey hdata_ellsq_pubkey;

CNode(NodeId id, ServiceFlags nLocalServicesIn, std::shared_ptr<Sock> sock, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, ConnectionType conn_type_in, bool inbound_onion);
CNode(const CNode&) = delete;
CNode& operator=(const CNode&) = delete;
Expand Down Expand Up @@ -766,6 +774,37 @@ class CNode
m_min_ping_time = std::min(m_min_ping_time.load(), ping_time);
}

void InitV2P2P(const CPubKey& peer_pubkey, const Span<uint8_t> initiator_hdata, const Span<uint8_t> responder_hdata, bool initiating) {
ECDHSecret ecdh_secret;
v2_priv_key.ComputeECDHSecret(peer_pubkey, ecdh_secret);

BIP324Keys v2_keys;
DeriveBIP324Keys(std::move(ecdh_secret), initiator_hdata, responder_hdata, v2_keys);

if (initiating) {
m_deserializer = std::make_unique<V2TransportDeserializer>(V2TransportDeserializer(NodeId{0}, v2_keys.responder_F, v2_keys.responder_V));
m_serializer = std::make_unique<V2TransportSerializer>(V2TransportSerializer(v2_keys.initiator_F, v2_keys.initiator_V));
} else {
m_deserializer = std::make_unique<V2TransportDeserializer>(V2TransportDeserializer(NodeId{0}, v2_keys.initiator_F, v2_keys.initiator_V));
m_serializer = std::make_unique<V2TransportSerializer>(V2TransportSerializer(v2_keys.responder_F, v2_keys.responder_V));
}
}

void EnsureInitV2Key(bool initiating) {
static const std::string version = "version\x00";

// v2_priv_key must be valid and the initiator's pubkey cannot begin with NETWORK_MAGIC || "version\x00"
while (!v2_priv_key.IsValid() || (initiating &&
memcmp(hdata_ellsq_pubkey.data(), Params().MessageStart(), CMessageHeader::MESSAGE_START_SIZE) == 0 &&
memcmp(hdata_ellsq_pubkey.data() + CMessageHeader::MESSAGE_START_SIZE, version.data(), version.size()) == 0)) {
v2_priv_key.MakeNewKey(true);

std::array<uint8_t, 32> rnd32;
GetRandBytes(rnd32.data(), 32);
hdata_ellsq_pubkey = v2_priv_key.GetPubKey().EllSqEncode(rnd32).value();
}
}

private:
const NodeId id;
const uint64_t nLocalHostNonce;
Expand All @@ -790,6 +829,7 @@ class CNode
const ServiceFlags nLocalServices;

std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread
CKey v2_priv_key;

// Our address, as reported by the peer
CService addrLocal GUARDED_BY(m_addr_local_mutex);
Expand Down Expand Up @@ -922,6 +962,7 @@ class CConnman
bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);

void PushMessage(CNode* pnode, CSerializedNetMsg&& msg);
void PushV2EllSqPubkey(CNode* pnode);

using NodeFn = std::function<void(CNode*)>;
void ForEachNode(const NodeFn& func)
Expand Down
5 changes: 5 additions & 0 deletions src/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,11 @@ static inline bool MayHaveUsefulAddressDB(ServiceFlags services)
return (services & NODE_NETWORK) || (services & NODE_NETWORK_LIMITED);
}

static inline bool SupportsV2Transport(ServiceFlags services)
{
return (services & NODE_P2P_V2);
}

/** A CService with information about it as peer */
class CAddress : public CService
{
Expand Down

0 comments on commit 2155bb8

Please sign in to comment.