diff --git a/doc/release-notes.md b/doc/release-notes.md --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -5,3 +5,4 @@ This release includes the following features and fixes: + - Add an extra 64 bits of entropy in the initial version message. diff --git a/src/avalanche/test/processor_tests.cpp b/src/avalanche/test/processor_tests.cpp --- a/src/avalanche/test/processor_tests.cpp +++ b/src/avalanche/test/processor_tests.cpp @@ -101,7 +101,7 @@ CAddress addr(ip(GetRandInt(0xffffffff)), NODE_NONE); auto node = new CNode(id++, ServiceFlags(NODE_NETWORK), 0, INVALID_SOCKET, addr, - 0, 0, CAddress(), "", ConnectionType::OUTBOUND); + 0, 0, 0, CAddress(), "", ConnectionType::OUTBOUND); node->SetSendVersion(PROTOCOL_VERSION); node->nServices = nServices; m_node.peer_logic->InitializeNode(config, node); diff --git a/src/net.h b/src/net.h --- a/src/net.h +++ b/src/net.h @@ -854,11 +854,15 @@ // Bind address of our side of the connection const CAddress addrBind; std::atomic nVersion{0}; - RecursiveMutex cs_SubVer; + // The nonce provided by the remote host. + uint64_t nRemoteHostNonce{0}; + // The extra entropy provided by the remote host. + uint64_t nRemoteExtraEntropy{0}; /** * cleanSubVer is a sanitized string of the user agent byte array we read * from the wire. This cleaned string can safely be logged or displayed. */ + RecursiveMutex cs_SubVer; std::string cleanSubVer GUARDED_BY(cs_SubVer){}; // This peer is preferred for eviction. bool m_prefer_evict{false}; @@ -1017,8 +1021,9 @@ CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, - uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, - const std::string &addrNameIn, ConnectionType conn_type_in); + uint64_t nLocalHostNonceIn, uint64_t nLocalExtraEntropyIn, + const CAddress &addrBindIn, const std::string &addrNameIn, + ConnectionType conn_type_in); ~CNode(); CNode(const CNode &) = delete; CNode &operator=(const CNode &) = delete; @@ -1026,6 +1031,7 @@ private: const NodeId id; const uint64_t nLocalHostNonce; + const uint64_t nLocalExtraEntropy; const ConnectionType m_conn_type; //! Services offered to this peer. @@ -1062,6 +1068,7 @@ NodeId GetId() const { return id; } uint64_t GetLocalNonce() const { return nLocalHostNonce; } + uint64_t GetLocalExtraEntropy() const { return nLocalExtraEntropy; } int GetMyStartingHeight() const { return nMyStartingHeight; } diff --git a/src/net.cpp b/src/net.cpp --- a/src/net.cpp +++ b/src/net.cpp @@ -101,6 +101,8 @@ static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("localhostnonce")[0:8] static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; +// SHA256("localhostnonce")[8:16] +static const uint64_t RANDOMIZER_ID_EXTRAENTROPY = 0x94b05d41679a4ff7ULL; // // Global state variables // @@ -469,16 +471,20 @@ uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE) .Write(id) .Finalize(); + uint64_t extra_entropy = + GetDeterministicRandomizer(RANDOMIZER_ID_EXTRAENTROPY) + .Write(id) + .Finalize(); CAddress addr_bind = GetBindAddress(hSocket); CNode *pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, - CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, - pszDest ? pszDest : "", conn_type); + CalculateKeyedNetGroup(addrConnect), nonce, extra_entropy, + addr_bind, pszDest ? pszDest : "", conn_type); pnode->AddRef(); // We're making a new connection, harvest entropy from the time (and our // peer count) - RandAddEvent((uint32_t)id); + RandAddEvent(uint32_t(id)); return pnode; } @@ -1151,6 +1157,10 @@ uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE) .Write(id) .Finalize(); + uint64_t extra_entropy = + GetDeterministicRandomizer(RANDOMIZER_ID_EXTRAENTROPY) + .Write(id) + .Finalize(); CAddress addr_bind = GetBindAddress(hSocket); ServiceFlags nodeServices = nLocalServices; @@ -1158,8 +1168,8 @@ nodeServices = static_cast(nodeServices | NODE_BLOOM); } CNode *pnode = new CNode(id, nodeServices, GetBestHeight(), hSocket, addr, - CalculateKeyedNetGroup(addr), nonce, addr_bind, "", - ConnectionType::INBOUND); + CalculateKeyedNetGroup(addr), nonce, extra_entropy, + addr_bind, "", ConnectionType::INBOUND); pnode->AddRef(); pnode->m_permissionFlags = permissionFlags; // If this flag is present, the user probably expect that RPC and QT report @@ -1177,7 +1187,7 @@ // We received a new connection, harvest entropy from the time (and our peer // count) - RandAddEvent((uint32_t)id); + RandAddEvent(uint32_t(id)); } void CConnman::DisconnectNodes() { @@ -2998,14 +3008,15 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, - const CAddress &addrBindIn, const std::string &addrNameIn, - ConnectionType conn_type_in) + uint64_t nLocalExtraEntropyIn, const CAddress &addrBindIn, + const std::string &addrNameIn, ConnectionType conn_type_in) : nTimeConnected(GetSystemTimeInSeconds()), addr(addrIn), addrBind(addrBindIn), nKeyedNetGroup(nKeyedNetGroupIn), // Don't relay addr messages to peers that we connect to as // block-relay-only peers (to prevent adversaries from inferring these // links from addr traffic). - id(idIn), nLocalHostNonce(nLocalHostNonceIn), m_conn_type(conn_type_in), + id(idIn), nLocalHostNonce(nLocalHostNonceIn), + nLocalExtraEntropy(nLocalExtraEntropyIn), m_conn_type(conn_type_in), nLocalServices(nLocalServicesIn), nMyStartingHeight(nMyStartingHeightIn) { hSocket = hSocketIn; addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; diff --git a/src/net_processing.cpp b/src/net_processing.cpp --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -549,6 +549,7 @@ int nNodeStartingHeight = pnode.GetMyStartingHeight(); NodeId nodeid = pnode.GetId(); CAddress addr = pnode.addr; + uint64_t extraEntropy = pnode.GetLocalExtraEntropy(); CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr @@ -556,11 +557,12 @@ CAddress addrMe = CAddress(CService(), nLocalNodeServices); connman.PushMessage( - &pnode, CNetMsgMaker(INIT_PROTO_VERSION) - .Make(NetMsgType::VERSION, PROTOCOL_VERSION, - uint64_t(nLocalNodeServices), nTime, addrYou, addrMe, - nonce, userAgent(config), nNodeStartingHeight, - ::g_relay_txes && pnode.m_tx_relay != nullptr)); + &pnode, + CNetMsgMaker(INIT_PROTO_VERSION) + .Make(NetMsgType::VERSION, PROTOCOL_VERSION, + uint64_t(nLocalNodeServices), nTime, addrYou, addrMe, nonce, + userAgent(config), nNodeStartingHeight, + ::g_relay_txes && pnode.m_tx_relay != nullptr, extraEntropy)); if (fLogIPs) { LogPrint(BCLog::NET, @@ -2612,6 +2614,7 @@ std::string cleanSubVer; int nStartingHeight = -1; bool fRelay = true; + uint64_t nExtraEntropy = 1; vRecv >> nVersion >> nServiceInt >> nTime >> addrMe; nSendVersion = std::min(nVersion, PROTOCOL_VERSION); @@ -2653,6 +2656,9 @@ if (!vRecv.empty()) { vRecv >> fRelay; } + if (!vRecv.empty()) { + vRecv >> nExtraEntropy; + } // Disconnect if we connected to ourself if (pfrom.IsInboundConn() && !m_connman.CheckIncomingNonce(nNonce)) { LogPrintf("connected to self at %s, disconnecting\n", @@ -2700,6 +2706,8 @@ // Change version pfrom.SetSendVersion(nSendVersion); pfrom.nVersion = nVersion; + pfrom.nRemoteHostNonce = nNonce; + pfrom.nRemoteExtraEntropy = nExtraEntropy; // Potentially mark this peer as a preferred download peer. { diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp --- a/src/test/denialofservice_tests.cpp +++ b/src/test/denialofservice_tests.cpp @@ -85,7 +85,7 @@ // Mock an outbound peer CAddress addr1(ip(0xa0b0c001), NODE_NONE); CNode dummyNode1(id++, ServiceFlags(NODE_NETWORK), 0, INVALID_SOCKET, addr1, - 0, 0, CAddress(), "", ConnectionType::OUTBOUND); + 0, 0, 0, CAddress(), "", ConnectionType::OUTBOUND); dummyNode1.SetSendVersion(PROTOCOL_VERSION); peerLogic->InitializeNode(config, &dummyNode1); @@ -146,7 +146,7 @@ CConnmanTest *connman) { CAddress addr(ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE); vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK), 0, - INVALID_SOCKET, addr, 0, 0, CAddress(), "", + INVALID_SOCKET, addr, 0, 0, 0, CAddress(), "", ConnectionType::OUTBOUND)); CNode &node = *vNodes.back(); node.SetSendVersion(PROTOCOL_VERSION); @@ -248,7 +248,7 @@ banman->ClearBanned(); CAddress addr1(ip(0xa0b0c001), NODE_NONE); - CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, 0, + CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, 0, 0, CAddress(), "", ConnectionType::INBOUND); dummyNode1.SetSendVersion(PROTOCOL_VERSION); peerLogic->InitializeNode(config, &dummyNode1); @@ -266,7 +266,7 @@ BOOST_CHECK(!banman->IsDiscouraged(ip(0xa0b0c001 | 0x0000ff00))); CAddress addr2(ip(0xa0b0c002), NODE_NONE); - CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, 1, + CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, 1, 1, CAddress(), "", ConnectionType::INBOUND); dummyNode2.SetSendVersion(PROTOCOL_VERSION); peerLogic->InitializeNode(config, &dummyNode2); @@ -315,7 +315,7 @@ SetMockTime(nStartTime); CAddress addr(ip(0xa0b0c001), NODE_NONE); - CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr, 4, 4, + CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr, 4, 4, 4, CAddress(), "", ConnectionType::INBOUND); dummyNode.SetSendVersion(PROTOCOL_VERSION); peerLogic->InitializeNode(config, &dummyNode); diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -185,14 +185,14 @@ CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK); std::string pszDest; - auto pnode1 = - std::make_unique(id++, NODE_NETWORK, height, hSocket, addr, 0, 0, - CAddress(), pszDest, ConnectionType::OUTBOUND); + auto pnode1 = std::make_unique(id++, NODE_NETWORK, height, hSocket, + addr, 0, 0, 0, CAddress(), pszDest, + ConnectionType::OUTBOUND); BOOST_CHECK(pnode1->IsInboundConn() == false); - auto pnode2 = - std::make_unique(id++, NODE_NETWORK, height, hSocket, addr, 1, 1, - CAddress(), pszDest, ConnectionType::INBOUND); + auto pnode2 = std::make_unique(id++, NODE_NETWORK, height, hSocket, + addr, 1, 1, 1, CAddress(), pszDest, + ConnectionType::INBOUND); BOOST_CHECK(pnode2->IsInboundConn() == true); } @@ -316,7 +316,7 @@ ipv4AddrPeer.s_addr = 0xa0b0c001; CAddress addr = CAddress(CService(ipv4AddrPeer, 7777), NODE_NETWORK); std::unique_ptr pnode = std::make_unique( - 0, NODE_NETWORK, 0, INVALID_SOCKET, addr, 0, 0, CAddress{}, + 0, NODE_NETWORK, 0, INVALID_SOCKET, addr, 0, 0, 0, CAddress{}, std::string{}, ConnectionType::OUTBOUND); pnode->fSuccessfullyConnected.store(true); diff --git a/src/version.h b/src/version.h --- a/src/version.h +++ b/src/version.h @@ -8,7 +8,7 @@ /** * network protocol versioning */ -static const int PROTOCOL_VERSION = 70015; +static const int PROTOCOL_VERSION = 70016; //! initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; @@ -37,4 +37,7 @@ //! not banning for invalid compact blocks starts with this version static const int INVALID_CB_NO_BAN_VERSION = 70015; +//! send extra peer specific entropy in the version message +static const int PEER_ENTROPY_VERSION = 70016; + #endif // BITCOIN_VERSION_H diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -927,7 +927,7 @@ class msg_version: __slots__ = ("addrFrom", "addrTo", "nNonce", "nRelay", "nServices", - "nStartingHeight", "nTime", "nVersion", "strSubVer") + "nStartingHeight", "nTime", "nVersion", "strSubVer", "nExtraEntropy") msgtype = b"version" def __init__(self): @@ -940,6 +940,7 @@ self.strSubVer = MY_SUBVERSION self.nStartingHeight = -1 self.nRelay = MY_RELAY + self.nExtraEntropy = random.getrandbits(64) def deserialize(self, f): self.nVersion = struct.unpack("