From 473f14b4afca9d213a649c5e9728926b7c54d147 Mon Sep 17 00:00:00 2001 From: Greg-Griffith Date: Wed, 12 Feb 2020 05:49:40 -0800 Subject: [PATCH] add support for new map based version message, add as extension for now --- src/net/messages.cpp | 186 ++++++++++++++++++++++++++++++++++++++- src/net/protocol.cpp | 5 +- src/net/protocol.h | 8 ++ src/net/versionmessage.h | 106 ++++++++++++++++++++++ src/version.h | 6 +- 5 files changed, 305 insertions(+), 6 deletions(-) create mode 100644 src/net/versionmessage.h diff --git a/src/net/messages.cpp b/src/net/messages.cpp index 5e305401..d9eb5b52 100644 --- a/src/net/messages.cpp +++ b/src/net/messages.cpp @@ -23,6 +23,7 @@ #include "net/nodestate.h" #include "net/packetmanager.h" #include "net/protocol.h" +#include "net/versionmessage.h" #include "networks/netman.h" #include "networks/networktemplate.h" #include "policy/fees.h" @@ -117,6 +118,43 @@ void PushNodeVersion(CNode *pnode, CConnman &connman, int64_t nTime) } } +// should currently send the exact same data aside from nTime as version message +void PushDynamicVersion(CNode *pnode, CConnman &connman, int64_t nTime) +{ + uint64_t nLocalNodeServices = (uint64_t)pnode->GetLocalServices(); + int nNodeStartingHeight = pnetMan->getChainActive()->chainActive.Height(); + NodeId nodeid = pnode->GetId(); + CAddress addr = pnode->addr; + + CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService(), addr.nServices)); + CAddress addrMe = CAddress(CService(), nLocalNodeServices); + + CDynamicVersionMessage versionmsg; + versionmsg.write(VersionMsgCode::VERSION, PROTOCOL_VERSION); + versionmsg.write(VersionMsgCode::LOCAL_NODE_SERVICES, nLocalNodeServices); + versionmsg.write(VersionMsgCode::TIME, nTime); + versionmsg.write(VersionMsgCode::ADDR_YOU, addrYou); + versionmsg.write(VersionMsgCode::ADDR_ME, addrMe); + versionmsg.write(VersionMsgCode::NONCE, nLocalHostNonce); + versionmsg.write(VersionMsgCode::SUBVERSION, strSubVersion); + versionmsg.write(VersionMsgCode::NODE_START_HEIGHT, nNodeStartingHeight); + versionmsg.write(VersionMsgCode::RELAY_TXES, ::fRelayTxes); + + connman.PushMessage(pnode, NetMsgType::DYNAMICVERSION, versionmsg); + + if (g_logger->fLogIPs) + { + LogPrintf("send dynamic version message: version %d, blocks=%d, " + "us=%s, them=%s, peer=%d\n", + PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), addrYou.ToString(), nodeid); + } + else + { + LogPrintf("send dynamic version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, + nNodeStartingHeight, addrMe.ToString(), nodeid); + } +} + void InitializeNode(CNode *pnode, CConnman &connman) { nodestateman.InitializeNodeState(pnode); @@ -1064,7 +1102,16 @@ bool static ProcessMessage(CNode *pfrom, PushNodeVersion(pfrom, connman, GetAdjustedTime()); } - connman.PushMessage(pfrom, NetMsgType::VERACK); + if (pfrom->nVersion >= MIN_NETVERSION_EXTENSION_VERSION) + { + // until this replaces the version message completely, a lot of this message is + // duplicated data as the version message + PushDynamicVersion(pfrom, connman, GetAdjustedTime()); + } + else + { + connman.PushMessage(pfrom, NetMsgType::VERACK); + } pfrom->nServices = nServices; pfrom->SetAddrLocal(addrMe); @@ -1141,6 +1188,115 @@ bool static ProcessMessage(CNode *pfrom, return true; } + else if (strCommand == NetMsgType::DYNAMICVERSION) + { + CDynamicVersionMessage versionmsg; + vRecv >> versionmsg; + + int64_t nTime; + CAddress addrMe; + CAddress addrFrom; + uint64_t nNonce = 1; + uint64_t nServiceInt; + ServiceFlags nServices; + int nVersion; + std::string strSubVer; + std::string cleanSubVer; + int nStartingHeight = -1; + bool fRelay = true; + + // get the value from the map + int *temp_version = (int *)versionmsg.read(VersionMsgCode::VERSION); + nVersion = *temp_version; + free(temp_version); + //verify it with the current version message + if (nVersion != pfrom->nVersion) + { + LogPrint("NET", "there is a mismatch between the version message and dynamic version message nVersion field \n"); + } + + uint64_t *temp_serviceint = (uint64_t *)versionmsg.read(VersionMsgCode::LOCAL_NODE_SERVICES); + nServiceInt = *temp_serviceint; + free(temp_serviceint); + nServices = ServiceFlags(nServiceInt); + if (nServices != pfrom->nServices) + { + LogPrint("NET", "there is a mismatch between the version message and dynamic version message nServices field \n"); + } + + int64_t *temp_time = (int64_t *)versionmsg.read(VersionMsgCode::TIME); + nTime = *temp_time; + free(temp_time); + int64_t nTimeOffset = nTime - GetTime(); + if (nTimeOffset != pfrom->nTimeOffset) + { + LogPrint("NET", "there is a mismatch between the version message and dynamic version message nTime field \n"); + } + + + CAddress *temp_addryou = (CAddress *)versionmsg.read(VersionMsgCode::ADDR_YOU); + addrMe = *temp_addryou; + free(temp_addryou); + if (addrMe != pfrom->GetAddrLocal()) + { + LogPrint("NET", "there is a mismatch between the version message and dynamic version message addrMe field \n"); + } + + + if (!vRecv.empty()) + { + CAddress *temp_addrme = (CAddress *)versionmsg.read(VersionMsgCode::ADDR_ME); + addrFrom = *temp_addrme; + free(temp_addrme); + + uint64_t *temp_nonce = (uint64_t *)versionmsg.read(VersionMsgCode::NONCE); + nNonce = *temp_nonce; + free(temp_nonce); + // no need to check nonce + } + if (!vRecv.empty()) + { + char *temp_substr = (char *)versionmsg.read(VersionMsgCode::SUBVERSION); + strSubVer = std::string(temp_substr); + cleanSubVer = SanitizeString(strSubVer); + free(temp_substr); + // this should never trigger because it was previously checked by version + if (strSubVer.size() > MAX_SUBVERSION_LENGTH) + { + LogPrintf("ERROR WITH DYNAMIC VERSION STRSUBVER CHECKS, ASSERTING FALSE \n"); + assert(false); + LOCK(cs_main); + Misbehaving(pfrom, 50, "bad strsubver"); + return false; + } + if (cleanSubVer != pfrom->cleanSubVer) + { + LogPrint("NET", "there is a mismatch between the version message and dynamic version message cleanSubVer field \n"); + } + } + if (!vRecv.empty()) + { + int *temp_startheight = (int *)versionmsg.read(VersionMsgCode::NODE_START_HEIGHT); + nStartingHeight = *temp_startheight; + free(temp_startheight); + if (nStartingHeight != pfrom->nStartingHeight) + { + LogPrint("NET", "there is a mismatch between the version message and dynamic version message nStartingHeight field \n"); + } + } + if (!vRecv.empty()) + { + bool *temp_relay = (bool *)versionmsg.read(VersionMsgCode::RELAY_TXES); + fRelay = *temp_relay; + free(temp_relay); + if (fRelay != pfrom->fRelayTxes) + { + LogPrint("NET", "there is a mismatch between the version message and dynamic version message fRelay field \n"); + } + } + + connman.PushMessage(pfrom, NetMsgType::DYNAMICVERACK); + } else if (pfrom->nVersion == 0) { @@ -1149,8 +1305,7 @@ bool static ProcessMessage(CNode *pfrom, return false; } - - if (strCommand == NetMsgType::VERACK) + else if (strCommand == NetMsgType::VERACK) { pfrom->SetRecvVersion(std::min(pfrom->nVersion.load(), PROTOCOL_VERSION)); @@ -1172,7 +1327,32 @@ bool static ProcessMessage(CNode *pfrom, { connman.PushMessage(pfrom, NetMsgType::NSVERSION, NETWORK_SERVICE_VERSION); } + pfrom->fSuccessfullyConnected = true; + } + + // dynamic verack is the same as a verack for now + else if (strCommand == NetMsgType::DYNAMICVERACK) + { + pfrom->SetRecvVersion(std::min(pfrom->nVersion.load(), PROTOCOL_VERSION)); + + if (!pfrom->fInbound) + { + // Mark this node as currently connected, so we update its timestamp + // later. + CNodeStateAccessor state(nodestateman, pfrom->GetId()); + state->fCurrentlyConnected = true; + } + // Tell our peer we prefer to receive headers rather than inv's + // We send this to non-NODE NETWORK peers as well, because even + // non-NODE NETWORK peers can announce blocks (such as pruning + // nodes) + connman.PushMessage(pfrom, NetMsgType::SENDHEADERS); + + if (pfrom->nVersion >= NETWORK_SERVICE_PROTOCOL_VERSION && IsBetaEnabled()) + { + connman.PushMessage(pfrom, NetMsgType::NSVERSION, NETWORK_SERVICE_VERSION); + } pfrom->fSuccessfullyConnected = true; } diff --git a/src/net/protocol.cpp b/src/net/protocol.cpp index d1642f52..3d44a2ca 100644 --- a/src/net/protocol.cpp +++ b/src/net/protocol.cpp @@ -19,7 +19,9 @@ namespace NetMsgType { const char *VERSION = "version"; +const char *DYNAMICVERSION = "dversion"; const char *VERACK = "verack"; +const char *DYNAMICVERACK = "dverack"; const char *ADDR = "addr"; const char *INV = "inv"; const char *GETDATA = "getdata"; @@ -55,7 +57,8 @@ static const char *ppszTypeName[] = { /** All known message types. Keep this in the same order as the list of * messages above and in protocol.h. */ -const static std::string allNetMessageTypes[] = {NetMsgType::VERSION, NetMsgType::VERACK, NetMsgType::ADDR, +const static std::string allNetMessageTypes[] = {NetMsgType::VERSION, NetMsgType::VERACK, NetMsgType::DYNAMICVERSION, NetMsgType::DYNAMICVERACK, + NetMsgType::ADDR, NetMsgType::INV, NetMsgType::GETDATA, NetMsgType::MERKLEBLOCK, NetMsgType::GETHEADERS, NetMsgType::TX, NetMsgType::HEADERS, NetMsgType::BLOCK, NetMsgType::GETADDR, NetMsgType::PING, NetMsgType::PONG, NetMsgType::NOTFOUND, NetMsgType::FILTERLOAD, NetMsgType::FILTERADD, NetMsgType::FILTERCLEAR, NetMsgType::REJECT, diff --git a/src/net/protocol.h b/src/net/protocol.h index a49a174b..4c624327 100644 --- a/src/net/protocol.h +++ b/src/net/protocol.h @@ -87,6 +87,14 @@ extern const char *VERSION; * @see https://bitcoin.org/en/developer-reference#verack */ extern const char *VERACK; + +// dynamic version is to replace the fixed version message at some future date +extern const char *DYNAMICVERSION; + +// dynamic verack is to replace the verack message at some future date, corresponds +// with dyamic version +extern const char *DYNAMICVERACK; + /** * The addr (IP address) message relays connection information for peers on the * network. diff --git a/src/net/versionmessage.h b/src/net/versionmessage.h new file mode 100644 index 00000000..0568998b --- /dev/null +++ b/src/net/versionmessage.h @@ -0,0 +1,106 @@ +// This file is part of the Eccoin project +// Copyright (c) 2020 The Eccoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef ECCOIN_VERSIONMESSAGE_H +#define ECCOIN_VERSIONMESSAGE_H + + +#include "protocol.h" +#include "serialize.h" +#include "tinyformat.h" +#include "util/utilstrencodings.h" +#include +#include +#include + +// clang-format off + +enum VersionMsgCode +{ + VERSION = 0x0000000000000000UL, + LOCAL_NODE_SERVICES = 0x0000000000000001UL, + TIME = 0x0000000000000002UL, + ADDR_YOU = 0x0000000000000003UL, + ADDR_ME = 0x0000000000000004UL, + NONCE = 0x0000000000000005UL, + SUBVERSION = 0x0000000000000006UL, + NODE_START_HEIGHT = 0x0000000000000007UL, + RELAY_TXES = 0x0000000000000008UL, +}; + +// clang-format on + +/* +// not used. this is just for reference +static const std::unordered_map VersionMsgType ( +{ + {VersionMsgCode::PROTOCOL_VERSION, int }, + {VersionMsgCode::LOCAL_NODE_SERVICES, uint64_t }, + {VersionMsgCode::ADDR_YOU, CAddress }, + {VersionMsgCode::TIME, int64_t }, + {VersionMsgCode::ADDR_ME, CAddress }, + {VersionMsgCode::NONCE, uint64_t }, + {VersionMsgCode::SUBVERSION, std::string }, + {VersionMsgCode::NODE_START_HEIGHT, int }, + {VersionMsgCode::RELAY_TXES, bool }, +); +*/ + +const size_t MAX_VERSION_MAP_SIZE = 100000; + +class CDynamicVersionMessage +{ +public: + std::map > version_map; + + CDynamicVersionMessage() {} + ADD_SERIALIZE_METHODS; + + void *read(const VersionMsgCode k) const + { + if (version_map.count(k) == 0) + { + return nullptr; + } + const std::vector &vec = version_map.at(k); + char* v = nullptr; + try + { + CDataStream s(vec, SER_NETWORK, PROTOCOL_VERSION); + v = (char *)malloc(s.size()); + s.read(v, s.size()); + } + catch (...) + { + LogPrintf("Error reading version message key %016llx. Assuming zero.\n", k); + v = nullptr; + } + return v; + } + + template + void write(const VersionMsgCode key, const T &val) + { + CDataStream s(SER_NETWORK, PROTOCOL_VERSION); + s << val; + + std::vector vec; + vec.insert(vec.begin(), s.begin(), s.end()); + version_map[key] = vec; + } + + template + inline void SerializationOp(Stream &s, Operation ser_action) + { + READWRITE(version_map); + if (GetSerializeSize(version_map, SER_NETWORK, PROTOCOL_VERSION) > MAX_VERSION_MAP_SIZE) + { + throw std::ios_base::failure( + strprintf("A version message version_map might at most be %d bytes.", MAX_VERSION_MAP_SIZE)); + } + } +}; + +#endif diff --git a/src/version.h b/src/version.h index 945b6c6f..76a00d57 100644 --- a/src/version.h +++ b/src/version.h @@ -13,16 +13,18 @@ */ -static const int PROTOCOL_VERSION = 60040; +static const int PROTOCOL_VERSION = 60042; // earlier versions not supported as of Feb 2012, and are disconnected -static const int MIN_PROTO_VERSION = 60037; +static const int MIN_PROTO_VERSION = 60041; //! "filter*" commands are disabled without NODE_BLOOM after and including this version static const int NO_BLOOM_VERSION = 60034; static const int NETWORK_SERVICE_PROTOCOL_VERSION = 60040; +static const int MIN_NETVERSION_EXTENSION_VERSION = 60042; + /** * Versioning for network services */