From 9b291120493f3a801275aa023131d8828705d817 Mon Sep 17 00:00:00 2001 From: pythonix <9782029+Pythonix@users.noreply.github.com> Date: Mon, 4 Jul 2022 20:30:15 +0200 Subject: [PATCH 01/94] Add and document network messages in protocol.h --- src/alert.cpp | 2 +- src/gridcoin/scraper/scraper_net.cpp | 4 +- src/main.cpp | 65 ++++++------ src/net.cpp | 4 +- src/protocol.cpp | 73 +++++++++++++- src/protocol.h | 141 +++++++++++++++++++++++++++ 6 files changed, 246 insertions(+), 43 deletions(-) diff --git a/src/alert.cpp b/src/alert.cpp index e8d4aaacc2..8ffca50ec2 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -137,7 +137,7 @@ bool CAlert::RelayTo(CNode* pnode) const AppliesToMe() || GetAdjustedTime() < nRelayUntil) { - pnode->PushMessage("alert", *this); + pnode->PushMessage(NetMsgType::ALERT, *this); return true; } } diff --git a/src/gridcoin/scraper/scraper_net.cpp b/src/gridcoin/scraper/scraper_net.cpp index 7e06eef98b..43f5dd2df5 100644 --- a/src/gridcoin/scraper/scraper_net.cpp +++ b/src/gridcoin/scraper/scraper_net.cpp @@ -184,7 +184,7 @@ bool CSplitBlob::SendPartTo(CNode* pto, const uint256& hash) EXCLUSIVE_LOCKS_REQ { if (ipart->second.present()) { - pto->PushMessage("part",ipart->second.getReader()); + pto->PushMessage(NetMsgType::PART,ipart->second.getReader()); return true; } } @@ -275,7 +275,7 @@ EXCLUSIVE_LOCKS_REQUIRED(CSplitBlob::cs_mapParts) { LOCK(manifest->cs_manifest); - pto->PushMessage("scraperindex", *manifest); + pto->PushMessage(NetMsgType::SCRAPERINDEX, *manifest); return true; } diff --git a/src/main.cpp b/src/main.cpp index af5db598e8..6e10c88ce8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3499,7 +3499,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { LogPrint(BCLog::LogFlags::NOISY, "received: %s from %s (%" PRIszu " bytes)", strCommand, pfrom->addrName, vRecv.size()); - if (strCommand == "aries") + if (strCommand == NetMsgType::ARIES || strCommand == NetMsgType::VERSION) { // Each connection can only send one version message if (pfrom->nVersion != 0) @@ -3615,7 +3615,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, AddTimeData(pfrom->addr, nOffsetSample); // Change version - pfrom->PushMessage("verack"); + pfrom->PushMessage(NetMsgType::VERACK); pfrom->ssSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); @@ -3628,7 +3628,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } // Get recent addresses - pfrom->PushMessage("getaddr"); + pfrom->PushMessage(NetMsgType::GETADDR); pfrom->fGetAddr = true; addrman.Good(pfrom->addr); } @@ -3672,13 +3672,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fDisconnect=true; return false; } - else if (strCommand == "verack") + else if (strCommand == NetMsgType::VERACK) { pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); } - else if (strCommand == "gridaddr") + else if (strCommand == NetMsgType::GRIDADDR || strCommand == NetMsgType::ADDR) { - //addr->gridaddr vector vAddr; vRecv >> vAddr; @@ -3742,7 +3741,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fDisconnect = true; } - else if (strCommand == "inv") + else if (strCommand == NetMsgType::INV) { vector vInv; vRecv >> vInv; @@ -3815,7 +3814,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "getdata") + else if (strCommand == NetMsgType::GETDATA) { vector vInv; vRecv >> vInv; @@ -3849,7 +3848,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CBlock block; ReadBlockFromDisk(block, mi->second, Params().GetConsensus()); - pfrom->PushMessage("encrypt", block); + pfrom->PushMessage(NetMsgType::ENCRYPT, block); // Trigger them to send a getblocks request for the next batch of inventory if (inv.hash == pfrom->hashContinue) @@ -3859,7 +3858,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // wait for other stuff first. vector vInv; vInv.push_back(CInv(MSG_BLOCK, hashBestChain)); - pfrom->PushMessage("inv", vInv); + pfrom->PushMessage(NetMsgType::INV, vInv); pfrom->hashContinue.SetNull(); } } @@ -3882,7 +3881,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss.reserve(1000); ss << tx; - pfrom->PushMessage("tx", ss); + pfrom->PushMessage(NetMsgType::TX, ss); } } else if(!pushed && inv.type == MSG_PART) { @@ -3947,7 +3946,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } - else if (strCommand == "getblocks") + else if (strCommand == NetMsgType::GETBLOCKS) { CBlockLocator locator; uint256 hashStop; @@ -3986,7 +3985,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } } - else if (strCommand == "getheaders") + else if (strCommand == NetMsgType::GETHEADERS) { CBlockLocator locator; uint256 hashStop; @@ -4020,9 +4019,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) break; } - pfrom->PushMessage("headers", vHeaders); + pfrom->PushMessage(NetMsgType::HEADERS, vHeaders); } - else if (strCommand == "tx") + else if (strCommand == NetMsgType::TX) { vector vWorkQueue; vector vEraseQueue; @@ -4088,7 +4087,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "encrypt") + else if (strCommand == NetMsgType::ENCRYPT || strCommand == NetMsgType::BLOCK) { //Response from getblocks, message = block @@ -4119,7 +4118,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "getaddr") + else if (strCommand == NetMsgType::GETADDR) { // Don't return addresses older than nCutOff timestamp int64_t nCutOff = GetAdjustedTime() - (nNodeLifespan * 24 * 60 * 60); @@ -4131,7 +4130,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "mempool") + else if (strCommand == NetMsgType::MEMPOOL) { LOCK(cs_main); @@ -4145,9 +4144,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, break; } if (vInv.size() > 0) - pfrom->PushMessage("inv", vInv); + pfrom->PushMessage(NetMsgType::INV, vInv); } - else if (strCommand == "ping") + else if (strCommand == NetMsgType::PING) { uint64_t nonce = 0; vRecv >> nonce; @@ -4163,9 +4162,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // it, if the remote node sends a ping once per second and this node takes 5 // seconds to respond to each, the 5th ping the remote sends would appear to // return very quickly. - pfrom->PushMessage("pong", nonce); + pfrom->PushMessage(NetMsgType::PONG, nonce); } - else if (strCommand == "pong") + else if (strCommand == NetMsgType::PONG) { int64_t pingUsecEnd = GetTimeMicros(); uint64_t nonce = 0; @@ -4220,7 +4219,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->nPingNonceSent = 0; } } - else if (strCommand == "alert") + else if (strCommand == NetMsgType::ALERT) { CAlert alert; vRecv >> alert; @@ -4250,11 +4249,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } - else if (strCommand == "scraperindex") + else if (strCommand == NetMsgType::SCRAPERINDEX) { CScraperManifest::RecvManifest(pfrom, vRecv); } - else if (strCommand == "part") + else if (strCommand == NetMsgType::PART) { CSplitBlob::RecvPart(pfrom, vRecv); } @@ -4276,7 +4275,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Update the last seen time for this node's address if (pfrom->fNetworkNode) - if (strCommand == "aries" || strCommand == "gridaddr" || strCommand == "inv" || strCommand == "getdata" || strCommand == "ping") + if (strCommand == NetMsgType::ARIES || strCommand == NetMsgType::GRIDADDR || strCommand == NetMsgType::INV || strCommand == NetMsgType::GETDATA || strCommand == NetMsgType::PING || strCommand == NetMsgType::VERSION || strCommand == NetMsgType::ADDR) AddressCurrentlyConnected(pfrom->addr); return true; @@ -4444,7 +4443,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->nPingUsecStart = GetTimeMicros(); pto->nPingNonceSent = nonce; - pto->PushMessage("ping", nonce); + pto->PushMessage(NetMsgType::PING, nonce); } // Resend wallet transactions that haven't gotten in a block yet @@ -4482,14 +4481,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // receiver rejects addr messages larger than 1000 if (vAddr.size() >= 1000) { - pto->PushMessage("gridaddr", vAddr); + pto->PushMessage(NetMsgType::GRIDADDR, vAddr); vAddr.clear(); } } } pto->vAddrToSend.clear(); if (!vAddr.empty()) - pto->PushMessage("gridaddr", vAddr); + pto->PushMessage(NetMsgType::GRIDADDR, vAddr); } @@ -4540,7 +4539,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) vInv.push_back(inv); if (vInv.size() >= 1000) { - pto->PushMessage("inv", vInv); + pto->PushMessage(NetMsgType::INV, vInv); vInv.clear(); } } @@ -4548,7 +4547,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->vInventoryToSend = vInvWait; } if (!vInv.empty()) - pto->PushMessage("inv", vInv); + pto->PushMessage(NetMsgType::INV, vInv); // @@ -4588,7 +4587,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) vGetData.push_back(inv); if (vGetData.size() >= 1000) { - pto->PushMessage("getdata", vGetData); + pto->PushMessage(NetMsgType::GETDATA, vGetData); vGetData.clear(); } @@ -4597,7 +4596,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->mapAskFor.erase(pto->mapAskFor.begin()); } if (!vGetData.empty()) - pto->PushMessage("getdata", vGetData); + pto->PushMessage(NetMsgType::GETDATA, vGetData); return true; } diff --git a/src/net.cpp b/src/net.cpp index 478ebe0ee9..ac6393ccbd 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -141,7 +141,7 @@ void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) g_getblocks_locator = CBlockLocator(pindexBegin); } - PushMessage("getblocks", g_getblocks_locator, hashEnd); + PushMessage(NetMsgType::GETBLOCKS, g_getblocks_locator, hashEnd); } // find 'best' local address for a particular peer @@ -492,7 +492,7 @@ void CNode::PushVersion() //TODO: change `PushMessage()` to use ServiceFlags so we don't need to cast nLocalServices PushMessage( - "aries", + NetMsgType::ARIES, PROTOCOL_VERSION, (uint64_t)nLocalServices, nTime, diff --git a/src/protocol.cpp b/src/protocol.cpp index d26f47cd66..a893ce4d71 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -12,15 +12,74 @@ # include #endif +namespace NetMsgType { + const char *VERSION="version"; + const char *VERACK="verack"; + const char *ADDR="addr"; + const char *INV="inv"; + const char *GETDATA="getdata"; + const char *GETBLOCKS="getblocks"; + const char *GETHEADERS="getheaders"; + const char *TX="tx"; + const char *HEADERS="headers"; + const char *BLOCK="block"; + const char *GETADDR="getaddr"; + const char *MEMPOOL="mempool"; + const char *PING="ping"; + const char *PONG="pong"; + const char *ALERT="alert"; + + // Gridcoin aliases (to be removed) + const char *ENCRYPT="encrypt"; + const char *GRIDADDR="gridaddr"; + const char *ARIES="aries"; + + // Gridcoin specific + const char *SCRAPERINDEX="scraperindex"; + const char *PART="part"; +} + static const char* ppszTypeName[] = { - "ERROR", - "tx", - "block", - "part", - "scraperindex", + "ERROR", // Should never occur + NetMsgType::TX, + NetMsgType::BLOCK, + NetMsgType::PART, + NetMsgType::SCRAPERINDEX, +}; + +/** 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, + NetMsgType::INV, + NetMsgType::GETDATA, + NetMsgType::GETBLOCKS, + NetMsgType::GETHEADERS, + NetMsgType::TX, + NetMsgType::HEADERS, + NetMsgType::BLOCK, + NetMsgType::GETADDR, + NetMsgType::MEMPOOL, + NetMsgType::PING, + NetMsgType::PONG, + NetMsgType::ALERT, + + // Gridcoin aliases (to be removed) + NetMsgType::ENCRYPT, + NetMsgType::GRIDADDR, + NetMsgType::ARIES, + + // Gridcoin specific + NetMsgType::SCRAPERINDEX, + NetMsgType::PART, }; +const static std::vector allNetMessageTypesVec(std::begin(allNetMessageTypes), std::end(allNetMessageTypes)); + CMessageHeader::CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn) { memcpy(pchMessageStart, Params().MessageStart(), CMessageHeader::MESSAGE_START_SIZE); @@ -122,3 +181,7 @@ void CInv::print() const LogPrintf("CInv(%s)", ToString()); } +const std::vector &getAllNetMessageTypes() +{ + return allNetMessageTypesVec; +} \ No newline at end of file diff --git a/src/protocol.h b/src/protocol.h index 00485c2955..b3fe809637 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -117,5 +117,146 @@ class CInv uint256 hash; }; +/** + * Bitcoin protocol message types. When adding new message types, don't forget + * to update allNetMessageTypes in protocol.cpp. + */ +namespace NetMsgType { + + /** + * The version message provides information about the transmitting node to the + * receiving node at the beginning of a connection. + * @see https://bitcoin.org/en/developer-reference#version + */ + extern const char *VERSION; + + /** + * The verack message acknowledges a previously-received version message, + * informing the connecting node that it can begin to send other messages. + * @see https://bitcoin.org/en/developer-reference#verack + */ + extern const char *VERACK; + + /** + * The addr (IP address) message relays connection information for peers on the + * network. + * @see https://bitcoin.org/en/developer-reference#addr + */ + extern const char *ADDR; + + /** + * The inv message (inventory message) transmits one or more inventories of + * objects known to the transmitting peer. + * @see https://bitcoin.org/en/developer-reference#inv + */ + extern const char *INV; + + /** + * The getdata message requests one or more data objects from another node. + * @see https://bitcoin.org/en/developer-reference#getdata + */ + extern const char *GETDATA; + + /** + * The getblocks message requests an inv message that provides block header + * hashes starting from a particular point in the block chain. + * @see https://bitcoin.org/en/developer-reference#getblocks + */ + extern const char *GETBLOCKS; + + /** + * The getheaders message requests a headers message that provides block + * headers starting from a particular point in the block chain. + * @since protocol version 31800. + * @see https://bitcoin.org/en/developer-reference#getheaders + */ + extern const char *GETHEADERS; + + /** + * The tx message transmits a single transaction. + * @see https://bitcoin.org/en/developer-reference#tx + */ + extern const char *TX; + + /** + * The headers message sends one or more block headers to a node which + * previously requested certain headers with a getheaders message. + * @since protocol version 31800. + * @see https://bitcoin.org/en/developer-reference#headers + */ + extern const char *HEADERS; + + /** + * The block message transmits a single serialized block. + * @see https://bitcoin.org/en/developer-reference#block + */ + extern const char *BLOCK; + + /** + * The ping message is sent periodically to help confirm that the receiving + * peer is still connected. + * @see https://bitcoin.org/en/developer-reference#ping + */ + extern const char *PING; + + /** + * The pong message replies to a ping message, proving to the pinging node that + * the ponging node is still alive. + * @since protocol version 60001 as described by BIP31. + * @see https://bitcoin.org/en/developer-reference#pong + */ + extern const char *PONG; + + /** + * The alert message warns nodes of problems that may affect them or the rest + * of the network. + * @since protocol version 311. + * @see https://bitcoin.org/en/developer-reference#alert + */ + extern const char *ALERT; + + /** + * The getaddr message requests an addr message from the receiving node, + * preferably one with lots of IP addresses of other receiving nodes. + * @see https://bitcoin.org/en/developer-reference#getaddr + */ + extern const char *GETADDR; + + /** + * The mempool message requests the TXIDs of transactions that the receiving + * node has verified as valid but which have not yet appeared in a block. + * @since protocol version 60002. + * @see https://bitcoin.org/en/developer-reference#mempool + */ + extern const char *MEMPOOL; + + /** + * Gridcoin alias for block message (will be removed) + */ + extern const char *ENCRYPT; + + /** + * Gridcoin alias for addr message (will be removed) + */ + extern const char *GRIDADDR; + + /** + * Gridcoin specific message + */ + extern const char *SCRAPERINDEX; + + /** + * Gridcoin specific message + */ + extern const char *PART; + + /** + * Gridcoin alias for version message (will be removed) + */ + extern const char *ARIES; +}; +/* Get a vector of all valid message types (see above) */ +const std::vector &getAllNetMessageTypes(); + #endif // BITCOIN_PROTOCOL_H From e166c2def98871c7e7128a2f61db08a453ac4b10 Mon Sep 17 00:00:00 2001 From: pythonix <9782029+Pythonix@users.noreply.github.com> Date: Mon, 4 Jul 2022 19:35:48 +0200 Subject: [PATCH 02/94] Fix some benign races --- src/addrman.h | 63 ++++++++++++++++++++++----------------------------- src/net.h | 2 +- 2 files changed, 28 insertions(+), 37 deletions(-) diff --git a/src/addrman.h b/src/addrman.h index 8f2606cb38..45daafa02c 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -460,6 +460,7 @@ class CAddrMan //! Return the number of (unique) addresses in all tables. int size() { + LOCK(cs); // TODO: Cache this in an atomic to avoid this overhead return vRandom.size(); } @@ -476,57 +477,49 @@ class CAddrMan #endif } - // Add a single address. + //! Add a single address. bool Add(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty = 0) { + LOCK(cs); bool fRet = false; - { - LOCK(cs); - Check(); - fRet |= Add_(addr, source, nTimePenalty); - Check(); - } + Check(); + fRet |= Add_(addr, source, nTimePenalty); + Check(); if (fRet) LogPrint(BCLog::LogFlags::ADDRMAN,"Added %s from %s: %i tried, %i new", addr.ToStringIPPort(), source.ToString(), nTried, nNew); return fRet; } - // Add multiple addresses. + //! Add multiple addresses. bool Add(const std::vector &vAddr, const CNetAddr& source, int64_t nTimePenalty = 0) { + LOCK(cs); int nAdd = 0; - { - LOCK(cs); - Check(); - for (std::vector::const_iterator it = vAddr.begin(); it != vAddr.end(); it++) - nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0; - Check(); - } + Check(); + for (std::vector::const_iterator it = vAddr.begin(); it != vAddr.end(); it++) + nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0; + Check(); if (nAdd) LogPrint(BCLog::LogFlags::ADDRMAN,"Added %i addresses from %s: %i tried, %i new", nAdd, source.ToString(), nTried, nNew); return nAdd > 0; } - // Mark an entry as accessible. + //! Mark an entry as accessible. void Good(const CService &addr, int64_t nTime = GetAdjustedTime()) { - { - LOCK(cs); - Check(); - Good_(addr, nTime); - Check(); - } + LOCK(cs); + Check(); + Good_(addr, nTime); + Check(); } - // Mark an entry as connection attempted to. + //! Mark an entry as connection attempted to. void Attempt(const CService &addr, int64_t nTime = GetAdjustedTime()) { - { - LOCK(cs); - Check(); - Attempt_(addr, nTime); - Check(); - } + LOCK(cs); + Check(); + Attempt_(addr, nTime); + Check(); } //! Choose an address to connect to. @@ -555,15 +548,13 @@ class CAddrMan return vAddr; } - // Mark an entry as currently-connected-to. + //! Mark an entry as currently-connected-to. void Connected(const CService &addr, int64_t nTime = GetAdjustedTime()) { - { - LOCK(cs); - Check(); - Connected_(addr, nTime); - Check(); - } + LOCK(cs); + Check(); + Connected_(addr, nTime); + Check(); } }; diff --git a/src/net.h b/src/net.h index 7f8a9b4ce1..bde90963aa 100644 --- a/src/net.h +++ b/src/net.h @@ -226,7 +226,7 @@ class CNode bool fInbound; bool fNetworkNode; bool fSuccessfullyConnected; - bool fDisconnect; + std::atomic_bool fDisconnect; CSemaphoreGrant grantOutbound; int nRefCount; protected: From 9b8a1fb79cc964d61204e4178f4967f02619855c Mon Sep 17 00:00:00 2001 From: Oskar Mendel Date: Fri, 10 Jun 2022 14:44:23 +0200 Subject: [PATCH 03/94] util: modify Win32LockedPageAllocator to query windows for limit --- src/support/lockedpool.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp index 2f97b075a1..778a9a9859 100644 --- a/src/support/lockedpool.cpp +++ b/src/support/lockedpool.cpp @@ -200,7 +200,10 @@ void Win32LockedPageAllocator::FreeLocked(void* addr, size_t len) size_t Win32LockedPageAllocator::GetLimit() { - // TODO is there a limit on Windows, how to get it? + size_t min, max; + if(GetProcessWorkingSetSize(GetCurrentProcess(), &min, &max) != 0) { + return min; + } return std::numeric_limits::max(); } #endif From 99be5989ffda2a9c4d4b4508f8ec0e2dc6bd8159 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sun, 31 Jul 2022 17:15:28 -0400 Subject: [PATCH 04/94] Increment version to 5.4.0.1 for development --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 4752a26c34..3cf0a4e0d0 100755 --- a/configure.ac +++ b/configure.ac @@ -3,8 +3,8 @@ AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 5) define(_CLIENT_VERSION_MINOR, 4) define(_CLIENT_VERSION_REVISION, 0) -define(_CLIENT_VERSION_BUILD, 0) -define(_CLIENT_VERSION_IS_RELEASE, true) +define(_CLIENT_VERSION_BUILD, 1) +define(_CLIENT_VERSION_IS_RELEASE, false) define(_COPYRIGHT_YEAR, 2022) define(_COPYRIGHT_HOLDERS,[The %s developers]) define(_COPYRIGHT_HOLDERS_SUBSTITUTION,[[Gridcoin]]) From f9f2e2ba03e1332d39c7fa90ce87853dbb59df2c Mon Sep 17 00:00:00 2001 From: barton26 Date: Mon, 1 Aug 2022 16:23:28 -0400 Subject: [PATCH 05/94] rpc: fix invalid parameter error codes for {sign,verify}message RPCs --- src/wallet/rpcwallet.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index b822d4bbf7..3f69d3f96a 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -458,7 +458,7 @@ UniValue signmessage(const UniValue& params, bool fHelp) CBitcoinAddress addr(strAddress); if (!addr.IsValid()) - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); CKeyID keyID; if (!addr.GetKeyID(keyID)) @@ -495,7 +495,7 @@ UniValue verifymessage(const UniValue& params, bool fHelp) CBitcoinAddress addr(strAddress); if (!addr.IsValid()) - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); CKeyID keyID; if (!addr.GetKeyID(keyID)) @@ -505,7 +505,7 @@ UniValue verifymessage(const UniValue& params, bool fHelp) vector vchSig = DecodeBase64(strSign.c_str(), &fInvalid); if (fInvalid) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding"); + throw JSONRPCError(RPC_TYPE_ERROR, "Malformed base64 encoding"); CDataStream ss(SER_GETHASH, 0); ss << strMessageMagic; From bf63f43ab7a643c4a43d0813f658568a9aca4f36 Mon Sep 17 00:00:00 2001 From: barton26 Date: Mon, 1 Aug 2022 16:03:07 -0400 Subject: [PATCH 06/94] Define MAX_DIGITS_BTC for magic number in BitcoinUnits::format --- src/qt/bitcoinunits.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index fd713fcd7e..baf3a6cd30 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -3,6 +3,8 @@ #include +static constexpr auto MAX_DIGITS_BTC = 16; + BitcoinUnits::BitcoinUnits(QObject *parent): QAbstractListModel(parent), unitlist(availableUnits()) @@ -99,7 +101,9 @@ QString BitcoinUnits::format(int unit, qint64 n, bool fPlus, bool justify) qint64 remainder = n_abs % coin; QString quotient_str = QString::number(quotient); - if (justify) quotient_str = quotient_str.rightJustified(16 - num_decimals, ' '); + if (justify) { + quotient_str = quotient_str.rightJustified(MAX_DIGITS_BTC - num_decimals, ' '); + } QString remainder_str = QString::number(remainder).rightJustified(num_decimals, '0'); From dcf4666e53c1f048f7524f61304fe623846f3940 Mon Sep 17 00:00:00 2001 From: barton26 Date: Mon, 1 Aug 2022 16:09:38 -0400 Subject: [PATCH 07/94] scripted-diff: Drop Darwin version for better maintainability -BEGIN VERIFY SCRIPT- sed -i 's/darwin18/darwin/g' $(git grep --files-with-matches 'darwin18') -END VERIFY SCRIPT- --- cd/00_setup_env_mac.sh | 2 +- ci/test/00_setup_env_mac.sh | 2 +- ci/test/00_setup_env_mac_host.sh | 2 +- contrib/macdeploy/README.md | 4 ++-- depends/README.md | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cd/00_setup_env_mac.sh b/cd/00_setup_env_mac.sh index 64beb8b0bb..68b002b0e4 100755 --- a/cd/00_setup_env_mac.sh +++ b/cd/00_setup_env_mac.sh @@ -8,7 +8,7 @@ export LC_ALL=C.UTF-8 export CONTAINER_NAME=ci_macos_cross export DOCKER_NAME_TAG=ubuntu:20.04 -export HOST=x86_64-apple-darwin18 +export HOST=x86_64-apple-darwin export PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools libtinfo5 python3-dev python3-setuptools xorriso" export XCODE_VERSION=12.1 export XCODE_BUILD_ID=12A7403 diff --git a/ci/test/00_setup_env_mac.sh b/ci/test/00_setup_env_mac.sh index deb273963e..237077efd1 100755 --- a/ci/test/00_setup_env_mac.sh +++ b/ci/test/00_setup_env_mac.sh @@ -8,7 +8,7 @@ export LC_ALL=C.UTF-8 export CONTAINER_NAME=ci_macos_cross export DOCKER_NAME_TAG=ubuntu:20.04 # Check that Focal can cross-compile to macos -export HOST=x86_64-apple-darwin18 +export HOST=x86_64-apple-darwin export PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools libtinfo5 python3-dev python3-setuptools xorriso" export XCODE_VERSION=12.1 export XCODE_BUILD_ID=12A7403 diff --git a/ci/test/00_setup_env_mac_host.sh b/ci/test/00_setup_env_mac_host.sh index e3c7bbbeea..cc55ae79b4 100755 --- a/ci/test/00_setup_env_mac_host.sh +++ b/ci/test/00_setup_env_mac_host.sh @@ -6,7 +6,7 @@ export LC_ALL=C.UTF-8 -export HOST=x86_64-apple-darwin18 +export HOST=x86_64-apple-darwin export GOAL="install" # We run the contrib/install_db4.sh script rather than installing the # Homebrew berkeley-db4 formula to add the Berkeley DB 4.8 dependency diff --git a/contrib/macdeploy/README.md b/contrib/macdeploy/README.md index 0750e75547..602b120e56 100644 --- a/contrib/macdeploy/README.md +++ b/contrib/macdeploy/README.md @@ -57,10 +57,10 @@ the depends directory and deploy the .dmg: ```bash cd depends -make HOST=x86_64-apple-darwin18 +make HOST=x86_64-apple-darwin cd .. ./autogen.sh # not required when building from tarball -CONFIG_SITE=$PWD/depends/x86_64-apple-darwin18/share/config.site ./configure --prefix=/ +CONFIG_SITE=$PWD/depends/x86_64-apple-darwin/share/config.site ./configure --prefix=/ make make deploy ``` diff --git a/depends/README.md b/depends/README.md index 5681b288fe..64627b7abd 100644 --- a/depends/README.md +++ b/depends/README.md @@ -22,7 +22,7 @@ Common `host-platform-triplets` for cross compilation are: - `i686-w64-mingw32` for Win32 - `x86_64-w64-mingw32` for Win64 -- `x86_64-apple-darwin18` for macOS +- `x86_64-apple-darwin` for macOS - `arm-linux-gnueabihf` for Linux ARM 32 bit - `aarch64-linux-gnu` for Linux ARM 64 bit From 826636d66db9bfad5e4a5edb15bc98dc4af1757f Mon Sep 17 00:00:00 2001 From: barton26 Date: Tue, 2 Aug 2022 18:59:18 -0400 Subject: [PATCH 08/94] build: Fix x86_64 <-> arm64 cross-compiling for macOS --- depends/builders/darwin.mk | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/depends/builders/darwin.mk b/depends/builders/darwin.mk index a6978a91d3..521af705eb 100644 --- a/depends/builders/darwin.mk +++ b/depends/builders/darwin.mk @@ -20,3 +20,8 @@ darwin_OTOOL:=$(shell xcrun -f otool) darwin_NM:=$(shell xcrun -f nm) darwin_INSTALL_NAME_TOOL:=$(shell xcrun -f install_name_tool) darwin_native_toolchain= + +x86_64_darwin_CFLAGS = -arch x86_64 +x86_64_darwin_CXXFLAGS = $(x86_64_darwin_CFLAGS) +aarch64_darwin_CFLAGS = -arch arm64 +aarch64_darwin_CXXFLAGS = $(aarch64_darwin_CFLAGS) From 4d82fa3be9c1265d8fb2aecc54d6f74a8547884b Mon Sep 17 00:00:00 2001 From: barton26 Date: Tue, 2 Aug 2022 18:59:51 -0400 Subject: [PATCH 09/94] build: Fix regression introduced in PR23603 --- depends/builders/darwin.mk | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/depends/builders/darwin.mk b/depends/builders/darwin.mk index 521af705eb..4eda404621 100644 --- a/depends/builders/darwin.mk +++ b/depends/builders/darwin.mk @@ -21,7 +21,7 @@ darwin_NM:=$(shell xcrun -f nm) darwin_INSTALL_NAME_TOOL:=$(shell xcrun -f install_name_tool) darwin_native_toolchain= -x86_64_darwin_CFLAGS = -arch x86_64 -x86_64_darwin_CXXFLAGS = $(x86_64_darwin_CFLAGS) -aarch64_darwin_CFLAGS = -arch arm64 -aarch64_darwin_CXXFLAGS = $(aarch64_darwin_CFLAGS) +x86_64_darwin_CFLAGS += -arch x86_64 +x86_64_darwin_CXXFLAGS += -arch x86_64 +aarch64_darwin_CFLAGS += -arch arm64 +aarch64_darwin_CXXFLAGS += -arch arm64 From 0cfe33f4f31b468f6b04ea29fe2c2fa35e47b673 Mon Sep 17 00:00:00 2001 From: barton26 Date: Fri, 5 Aug 2022 16:55:08 -0400 Subject: [PATCH 10/94] refactor: remove unused c-string variant of atoi64() --- src/util/strencodings.cpp | 9 --------- src/util/strencodings.h | 1 - 2 files changed, 10 deletions(-) diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp index a1b05f29e1..67902c84e7 100644 --- a/src/util/strencodings.cpp +++ b/src/util/strencodings.cpp @@ -440,15 +440,6 @@ std::string FormatParagraph(const std::string& in, size_t width, size_t indent) return out.str(); } -int64_t atoi64(const char* psz) -{ -#ifdef _MSC_VER - return _atoi64(psz); -#else - return strtoll(psz, nullptr, 10); -#endif -} - int64_t atoi64(const std::string& str) { #ifdef _MSC_VER diff --git a/src/util/strencodings.h b/src/util/strencodings.h index fd159bc980..5df6616be9 100644 --- a/src/util/strencodings.h +++ b/src/util/strencodings.h @@ -55,7 +55,6 @@ std::string EncodeBase32(const unsigned char* pch, size_t len); std::string EncodeBase32(const std::string& str); void SplitHostPort(std::string in, int &portOut, std::string &hostOut); -int64_t atoi64(const char* psz); int64_t atoi64(const std::string& str); int atoi(const std::string& str); From 1c0b8466ee08b34a21a183a9b1924790d88205c0 Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Sat, 13 Aug 2022 21:39:10 -0400 Subject: [PATCH 11/94] Remove unused m_mrc_fees_to_staker in Claim class Implement GetMRCFees in CBlock(). Place output of GetMRCFees() in blocktoJSON conditioned on block.nVersion >= 12. --- src/gridcoin/claim.h | 7 ------- src/main.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ src/main.h | 11 +++++++++++ src/miner.cpp | 7 ++----- src/rpc/blockchain.cpp | 9 ++++++++- 5 files changed, 61 insertions(+), 13 deletions(-) diff --git a/src/gridcoin/claim.h b/src/gridcoin/claim.h index 36021597f7..0c822fcdab 100644 --- a/src/gridcoin/claim.h +++ b/src/gridcoin/claim.h @@ -193,13 +193,6 @@ class Claim : public IContractPayload //! std::map m_mrc_tx_map; - //! - //! \brief This represents the fees taken from the MRC research subsidies that are awarded to the staker. - //! This must be tracked because this value is added to coinstake award for the staker and must be - //! included in the claim validation. - //! - CAmount m_mrc_fees_to_staker; - //! //! \brief Initialize an empty, invalid reward claim object. //! diff --git a/src/main.cpp b/src/main.cpp index 8109ce57ac..d7e6302af8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3095,3 +3095,43 @@ GRC::MintSummary CBlock::GetMint() const EXCLUSIVE_LOCKS_REQUIRED(cs_main) return mint; } +GRC::MRCFees CBlock::GetMRCFees() const EXCLUSIVE_LOCKS_REQUIRED(cs_main) +{ + GRC::MRCFees mrc_fees; + unsigned int mrc_output_limit = GetMRCOutputLimit(nVersion, false); + + // Return zeroes for mrc fees if MRC not allowed. (This could have also been done + // by block version check, but this is more correct.) + if (!mrc_output_limit) { + return mrc_fees; + } + + Fraction foundation_fee_fraction = FoundationSideStakeAllocation(); + + const GRC::Claim claim = NCONST_PTR(this)->PullClaim(); + + CAmount mrc_total_fees = 0; + + // This is similar to the code in CheckMRCRewards in the Validator class, but with the validation removed because + // the block has already been validated. We also only need the MRC fee calculation portion. + for (const auto& tx: vtx) { + for (const auto& mrc : claim.m_mrc_tx_map) { + if (mrc.second == tx.GetHash() && !tx.GetContracts().empty()) { + // An MRC contract must be the first and only contract on a transaction by protocol. + GRC::Contract contract = tx.GetContracts()[0]; + + if (contract.m_type != GRC::ContractType::MRC) continue; + + GRC::MRC mrc = contract.CopyPayloadAs(); + + mrc_total_fees += mrc.m_fee; + mrc_fees.m_mrc_foundation_fees += mrc.m_fee * foundation_fee_fraction.GetNumerator() + / foundation_fee_fraction.GetDenominator(); + } + } + } + + mrc_fees.m_mrc_staker_fees = mrc_total_fees - mrc_fees.m_mrc_foundation_fees; + + return mrc_fees; +} diff --git a/src/main.h b/src/main.h index 3f09326b9a..e04001d6a4 100644 --- a/src/main.h +++ b/src/main.h @@ -310,6 +310,16 @@ class CBlockHeader }; namespace GRC { +//! +//! \brief A report that contains the mrc fees paid in a block. +//! +class MRCFees +{ +public: + CAmount m_mrc_foundation_fees = 0; //!< mrc fees to the foundation + CAmount m_mrc_staker_fees = 0; //!< mrc fees to the staker +}; + //! //! \brief A report that contains the calculated subsidy claimed in a block. //! Produced by the CBlock::GetMint() method. @@ -393,6 +403,7 @@ class CBlock : public CBlockHeader GRC::SuperblockPtr GetSuperblock() const; GRC::SuperblockPtr GetSuperblock(const CBlockIndex* const pindex) const; GRC::MintSummary GetMint() const; + GRC::MRCFees GetMRCFees() const; // entropy bit for stake modifier if chosen by modifier unsigned int GetStakeEntropyBit() const diff --git a/src/miner.cpp b/src/miner.cpp index f7c1eaf0a1..118a968987 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -262,9 +262,6 @@ bool CreateMRCRewards(CBlock &blocknew, std::map= 4) { @@ -228,6 +227,14 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool fP } result.pushKV("fees_collected", ValueFromAmount(mint.m_fees)); + + if (block.nVersion >= 12) { + GRC::MRCFees mrc_fees = block.GetMRCFees(); + + result.pushKV("mrc_foundation_fees", ValueFromAmount(mrc_fees.m_mrc_foundation_fees)); + result.pushKV("mrc_staker_fees", ValueFromAmount(mrc_fees.m_mrc_staker_fees)); + } + result.pushKV("IsSuperBlock", blockindex->IsSuperblock()); result.pushKV("IsContract", blockindex->IsContract()); From 948ec474d330238130de6df99b4162897eb28b10 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sat, 27 Aug 2022 09:20:14 -0400 Subject: [PATCH 12/94] Add missing resizeTableColumns to fix send address book column widths --- src/qt/sendcoinsentry.cpp | 6 ++++-- src/qt/signverifymessagedialog.cpp | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 8321f7ff2a..df998c70bc 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -38,11 +38,13 @@ void SendCoinsEntry::on_pasteButton_clicked() void SendCoinsEntry::on_addressBookButton_clicked() { - if(!model) + if (!model) return; AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::SendingTab, this); dlg.setModel(model->getAddressTableModel()); - if(dlg.exec()) + dlg.open(); + dlg.resizeTableColumns(); + if (dlg.exec()) { ui->payTo->setText(dlg.getReturnValue()); ui->payAmount->setFocus(); diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 313eff5555..1589c8dc38 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -83,6 +83,8 @@ void SignVerifyMessageDialog::on_addressBookButton_SM_clicked() { AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::ReceivingTab, this); dlg.setModel(model->getAddressTableModel()); + dlg.open(); + dlg.resizeTableColumns(); if (dlg.exec()) { setAddress_SM(dlg.getReturnValue()); @@ -172,6 +174,8 @@ void SignVerifyMessageDialog::on_addressBookButton_VM_clicked() { AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::SendingTab, this); dlg.setModel(model->getAddressTableModel()); + dlg.open(); + dlg.resizeTableColumns(); if (dlg.exec()) { setAddress_VM(dlg.getReturnValue()); From ce87b8fee1ae3d1554c40a920fe79589a51a6867 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sun, 4 Sep 2022 01:59:28 -0400 Subject: [PATCH 13/94] Implementation of getmrcinfo This provides an rpc command to get a summary and if desired details of MRC payments. --- src/main.cpp | 2 +- src/rpc/blockchain.cpp | 135 +++++++++++++++++++++++++++++++++++++++++ src/rpc/client.cpp | 3 + src/rpc/server.cpp | 1 + src/rpc/server.h | 1 + 5 files changed, 141 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index d7e6302af8..2335e4a325 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3108,7 +3108,7 @@ GRC::MRCFees CBlock::GetMRCFees() const EXCLUSIVE_LOCKS_REQUIRED(cs_main) Fraction foundation_fee_fraction = FoundationSideStakeAllocation(); - const GRC::Claim claim = NCONST_PTR(this)->PullClaim(); + const GRC::Claim claim = GetClaim(); CAmount mrc_total_fees = 0; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 9a36a8d700..49fd063eaf 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -460,6 +460,141 @@ UniValue dumpcontracts(const UniValue& params, bool fHelp) return report; } +UniValue getmrcinfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 3) + throw runtime_error( + "getmrcinfo [detailed MRC info [low height [high height]]]\n" + "\n" + "[detailed MRC info]: optional boolean to output MRC details.\n" + " Defaults to false.\n" + "[low height]: optional low height for scope.\n" + " Defaults to V12 block height.\n" + "[high height]: optional high height for scope.\n" + " Defaults to current block.\n" + ); + + bool output_mrc_details = false; + + if (params.size() > 0) { + output_mrc_details = params[0].get_bool(); + } + + // No MRC's below V12 block height. + int low_height = Params().GetConsensus().BlockV12Height; + int high_height = 0; + + if (params.size() > 1) { + // If specified low height is lower than V12 height, set to V12 height. + low_height = std::max(params[1].get_int(), low_height); + } + + if (params.size() > 2) { + // High height can't be lower than the low height. + high_height = std::max(low_height, params[2].get_int()); + } + + UniValue report(UniValue::VOBJ); + UniValue block_output_array(UniValue::VARR); + + uint64_t total_mrcs_paid = 0; + + CAmount mrc_total_research_rewards = 0; + CAmount mrc_total_foundation_fees = 0; + CAmount mrc_total_staker_fees = 0; + + CBlock block; + UniValue block_output(UniValue::VOBJ); + + LOCK(cs_main); + + // Set default high_height here if not specified above now that lock on cs_main is taken. + if (!high_height) { + high_height = pindexBest->nHeight; + } + + CBlockIndex* blockindex = pindexBest; + + // Rewind to low height. + for (; blockindex; blockindex = blockindex->pprev) { + if (blockindex->nHeight == low_height) break; + } + + while (blockindex && blockindex->nHeight <= high_height) { + CAmount mrc_research_rewards = 0; + + for (const auto& mrc_context : blockindex->m_mrc_researchers) { + mrc_research_rewards += mrc_context->m_research_subsidy; + } + + ReadBlockFromDisk(block, blockindex, Params().GetConsensus()); + + // Get the claim which is where MRCs are actually paid. + GRC::Claim claim = block.GetClaim(); + GRC::MRCFees mrc_fees = block.GetMRCFees(); + + uint64_t mrcs_paid = claim.m_mrc_tx_map.size(); // This also matches the size of the blockindex->m_mrc_researchers + + if (output_mrc_details) { + UniValue mrc_requests_output_array(UniValue::VARR); + uint64_t mrc_requests = 0; + + block_output.pushKV("hash", block.GetHash().GetHex()); + block_output.pushKV("height", blockindex->nHeight); + block_output.pushKV("mrc_research_rewards", ValueFromAmount(mrc_research_rewards)); + block_output.pushKV("mrc_foundation_fees", ValueFromAmount(mrc_fees.m_mrc_foundation_fees)); + block_output.pushKV("mrc_staker_fees", ValueFromAmount(mrc_fees.m_mrc_staker_fees)); + block_output.pushKV("mrc_net_paid_to_researchers", ValueFromAmount(mrc_research_rewards + - mrc_fees.m_mrc_foundation_fees + - mrc_fees.m_mrc_staker_fees)); + block_output.pushKV("mrcs_paid", mrcs_paid); + block_output.pushKV("claim", ClaimToJson(block.GetClaim(), blockindex)); + + for (const auto& tx : block.vtx) { + for (const auto& contract : tx.GetContracts()) { + // We are only interested in MRC request contracts here. + if (contract.m_type != GRC::ContractType::MRC) continue; + + ++mrc_requests; + + UniValue mrc_output(UniValue::VOBJ); + + mrc_output.pushKV("txid", tx.GetHash().GetHex()); + mrc_output.pushKVs(MRCToJson(contract.CopyPayloadAs())); + + mrc_requests_output_array.push_back(mrc_output); + + } // contracts + } // transaction + + block_output.pushKV("mrc_requests", mrc_requests_output_array); + + if (mrc_requests) { + block_output_array.push_back(block_output); + } + } + + mrc_total_foundation_fees += mrc_fees.m_mrc_foundation_fees; + mrc_total_staker_fees += mrc_fees.m_mrc_staker_fees; + total_mrcs_paid += mrcs_paid; + mrc_total_research_rewards += mrc_research_rewards; + blockindex = blockindex->pnext; + } // while (pblockindex...) + + report.pushKV("total_mrcs_paid", total_mrcs_paid); + report.pushKV("mrc_total_research_rewards", ValueFromAmount(mrc_total_research_rewards)); + report.pushKV("mrc_total_foundation_fees", ValueFromAmount(mrc_total_foundation_fees)); + report.pushKV("mrc_total_staker_fees", ValueFromAmount(mrc_total_staker_fees)); + report.pushKV("mrc_total_net_paid_to_researchers", ValueFromAmount(mrc_total_research_rewards + - mrc_total_foundation_fees + - mrc_total_staker_fees)); + if (output_mrc_details) { + report.pushKV("mrc_details_by_block", block_output_array); + } + + return report; +} + UniValue showblock(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index b2af33fa03..988476b25b 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -191,6 +191,9 @@ static const CRPCConvertParam vRPCConvertParams[] = { "beaconreport" , 0 }, { "createmrcrequest" , 0 }, { "createmrcrequest" , 1 }, + { "getmrcinfo" , 0 }, + { "getmrcinfo" , 1 }, + { "getmrcinfo" , 2 }, { "superblocks" , 0 }, { "superblocks" , 1 }, diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 5ca1f2b253..90d6093c73 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -355,6 +355,7 @@ static const CRPCCommand vRPCCommands[] = { "createmrcrequest", &createmrcrequest, cat_staking }, { "explainmagnitude", &explainmagnitude, cat_staking }, { "getlaststake", &getlaststake, cat_staking }, + { "getmrcinfo", &getmrcinfo, cat_staking }, { "getstakinginfo", &getstakinginfo, cat_staking }, { "getmininginfo", &getstakinginfo, cat_staking }, //alias for getstakinginfo (compatibility) { "lifetime", &lifetime, cat_staking }, diff --git a/src/rpc/server.h b/src/rpc/server.h index 4885d4c3f9..2990137d1f 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -171,6 +171,7 @@ extern UniValue beaconstatus(const UniValue& params, bool fHelp); extern UniValue createmrcrequest(const UniValue& params, const bool fHelp); extern UniValue explainmagnitude(const UniValue& params, bool fHelp); extern UniValue getlaststake(const UniValue& params, bool fHelp); +extern UniValue getmrcinfo(const UniValue& params, bool fHelp); extern UniValue getstakinginfo(const UniValue& params, bool fHelp); extern UniValue lifetime(const UniValue& params, bool fHelp); extern UniValue magnitude(const UniValue& params, bool fHelp); From b4e2d222dd3fc11f94cdffbe488ea58cba4db499 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Mon, 5 Sep 2022 14:40:59 -0400 Subject: [PATCH 14/94] Add init error message if -printtoconsole and -daemon specified simultaneously --- src/gridcoinresearchd.cpp | 10 ++++++++++ src/init.cpp | 9 +++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/gridcoinresearchd.cpp b/src/gridcoinresearchd.cpp index 65ef60ce4f..c25ccc4f4b 100644 --- a/src/gridcoinresearchd.cpp +++ b/src/gridcoinresearchd.cpp @@ -196,6 +196,16 @@ bool AppInit(int argc, char* argv[]) return !CommandLineRPC(argc, argv); } + if (gArgs.GetBoolArg("-printtoconsole", false) && gArgs.GetBoolArg("-daemon", DEFAULT_DAEMON)) { + return InitError("-printtoconsole && -daemon cannot be specified at the same time,\n" + "because Gridcoin follows proper daemonization and disconnects the\n" + "console when the process is forked. This is consistent with Bitcoin\n" + "Core. Please see https://github.com/bitcoin/bitcoin/issues/10132.\n" + "If you are not specifying -daemon as a startup parameter, but only\n" + "-printtoconsole, and you are geting this error, please check the\n" + "gridcoinresearch.conf and comment out daemon=1, or use -nodaemon."); + } + // -server defaults to true for gridcoinresearchd but not for the GUI so do this here gArgs.SoftSetBoolArg("-server", true); // Initialize logging as early as possible. diff --git a/src/init.cpp b/src/init.cpp index 92d2c7a7e2..1aff9c137c 100755 --- a/src/init.cpp +++ b/src/init.cpp @@ -575,8 +575,13 @@ void SetupServerArgs() ArgsManager::ALLOW_ANY, OptionsCategory::RPC); #if HAVE_DECL_FORK - argsman.AddArg("-daemon", strprintf("Run in the background as a daemon and accept commands (default: %d)", DEFAULT_DAEMON), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-daemonwait", strprintf("Wait for initialization to be finished before exiting. This implies -daemon (default: %d)", DEFAULT_DAEMONWAIT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-daemon", + strprintf("Run in the background as a daemon and accept commands (default: %d)", DEFAULT_DAEMON), + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-daemonwait", + strprintf("Wait for initialization to be finished before exiting. " + "This implies -daemon (default: %d)", DEFAULT_DAEMONWAIT), + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); #else hidden_args.emplace_back("-daemon"); hidden_args.emplace_back("-daemonwait"); From 466af39e3ddc6e66684701e07e95c11baeaae6dd Mon Sep 17 00:00:00 2001 From: spider Date: Thu, 8 Sep 2022 22:19:31 -0700 Subject: [PATCH 15/94] Diagnose Lib Version #1 --- src/Makefile.qt.include | 3 + src/qt/Diagnose.cpp | 207 +++++++++++ src/qt/Diagnose.h | 648 +++++++++++++++++++++++++++++++++++ src/qt/diagnosticsdialog.cpp | 96 +++++- src/qt/diagnosticsdialog.h | 14 + 5 files changed, 949 insertions(+), 19 deletions(-) create mode 100644 src/qt/Diagnose.cpp create mode 100644 src/qt/Diagnose.h diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 16a6d5d863..17106ef6f4 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -249,6 +249,7 @@ GRIDCOINRESEARCH_QT_H = \ qt/csvmodelwriter.h \ qt/decoration.h \ qt/diagnosticsdialog.h \ + qt/Diagnose.h \ qt/editaddressdialog.h \ qt/favoritespage.h \ qt/guiconstants.h \ @@ -340,6 +341,8 @@ GRIDCOINRESEARCH_QT_CPP = \ qt/csvmodelwriter.cpp \ qt/decoration.cpp \ qt/diagnosticsdialog.cpp \ + qt/Diagnose.cpp \ + qt/Diagnose.cpp \ qt/editaddressdialog.cpp \ qt/favoritespage.cpp \ qt/guiutil.cpp \ diff --git a/src/qt/Diagnose.cpp b/src/qt/Diagnose.cpp new file mode 100644 index 0000000000..018e018c41 --- /dev/null +++ b/src/qt/Diagnose.cpp @@ -0,0 +1,207 @@ + +#include "Diagnose.h" +#include "net.h" +#include + +using namespace DiagnoseLib; +//Define the Static members +bool Diagnose::m_researcher_mode = false; +const ResearcherModel* Diagnose::m_researcher_model = nullptr; +std::unordered_map m_name_to_test_map; +CCriticalSection Diagnose::cs_diagnostictests; + +void VerifyClock::clkReportResults(const int64_t& time_offset, const bool& timeout_during_check) +{ + if (!timeout_during_check) + { + if (abs64(time_offset) < 3 * 60) + { + m_results = PASS; + } + else if (abs64(time_offset) < 5 * 60) + { + m_results_tip = "You should check your time and time zone settings for your computer."; + m_results = WARNING; + m_results_string = "Warning: Clock skew is between 3 and 5 minutes. Please check your clock settings."; + } + else + { + m_results = FAIL; + m_results_tip = "Your clock in your computer is significantly off from UTC or network time and " + "this may seriously degrade the operation of the wallet, including maintaining " + "connection to the network. You should check your time and time zone settings " + "for your computer. A very common problem is the off by one hour caused by a time " + "zone issue or problems with daylight savings time."; + m_results_string = "Error: Clock skew is 5 minutes or greater. Please check your clock settings."; + } + }else{ + m_results = WARNING; + m_results_tip = "The wallet has less than five connections to the network and is unable to connect " + "to an NTP server to check your computer clock. This is not necessarily a problem. " + "You can wait a few minutes and try the test again."; + m_results_string = "Warning: Cannot connect to NTP server"; + } +} + +void VerifyClock::clkStateChanged(QAbstractSocket::SocketState state) +{ + if (state == QAbstractSocket::ConnectedState) + { + connect(m_udpSocket, &QUdpSocket::readyRead, this, &VerifyClock::clkFinished); + + char NTPMessage[48] = {0x1b, 0, 0, 0 ,0, 0, 0, 0, 0}; + + m_udpSocket->write(NTPMessage, sizeof(NTPMessage)); + } +} + +void VerifyClock::clkFinished(){ + if (m_udpSocket->waitForReadyRead(10 * 1000)) + { + int64_t start_time = GetAdjustedTime(); + + // Only allow this loop to run for 5 seconds maximum. + while (m_udpSocket->hasPendingDatagrams() && GetAdjustedTime() - start_time <= 5) + { + QByteArray BufferSocket = m_udpSocket->readAll(); + + if (BufferSocket.size() == 48) + { + int nNTPCount = 40; + uint32_t DateTimeIn = uchar(BufferSocket.at(nNTPCount)) + + (uchar(BufferSocket.at(nNTPCount + 1)) << 8) + + (uchar(BufferSocket.at(nNTPCount + 2)) << 16) + + (uchar(BufferSocket.at(nNTPCount + 3)) << 24); + time_t tmit = ntohl(DateTimeIn) - 2208988800U; + + m_udpSocket->close(); + + boost::posix_time::ptime localTime = boost::posix_time::microsec_clock::universal_time(); + boost::posix_time::ptime networkTime = boost::posix_time::from_time_t(tmit); + boost::posix_time::time_duration timeDiff = networkTime - localTime; + + clkReportResults(timeDiff.total_seconds()); + + return; + } + } + } + else // The other state here is a socket or other indeterminate error such as a timeout (coming from clkSocketError). + { + // This is needed to "cancel" the timeout timer. Essentially if the test was marked completed via the normal exits + // above, then when the timer calls clkFinished again, it will hit this conditional and be a no-op. + + auto VerifyClock_Test = getTest(Diagnose::VerifyClock); + if (VerifyClock_Test->getResults() != Diagnose::NONE) + { + clkReportResults(0, true); + } + + return; + } +} + +void VerifyClock::clkSocketError() +{ + m_udpSocket->close(); + + clkReportResults(0, true); +} + +void VerifyTCPPort::TCPFailed(QAbstractSocket::SocketError socket_error){ + m_results = WARNING; + m_results_tip = "Outbound communication to TCP port %1 appears to be blocked. "; + m_results_string_arg.push_back( std::to_string(GetListenPort())); + + switch (socket_error){ + case QAbstractSocket::SocketError::ConnectionRefusedError: + m_results_tip += "The connection to the port test site was refused. This could be a transient problem with the " + "port test site, but could also be an issue with your firewall. If you are also failing the " + "connection test, your firewall is most likely blocking network communications from the " + "Gridcoin client."; + + break; + + case QAbstractSocket::SocketError::RemoteHostClosedError: + m_results_tip += "The port test site is closed on port. This could be a transient problem with the " + "port test site, but could also be an issue with your firewall. If you are also failing the " + "connection test, your firewall is most likely blocking network communications from the " + "Gridcoin client."; + + break; + + case QAbstractSocket::SocketError::HostNotFoundError: + // This does not include the common text above on purpose. + m_results_tip = "The IP for the port test site is unable to be resolved. This could mean your DNS is not working " + "correctly. The wallet may operate without DNS, but it could be severely degraded, especially if " + "the wallet is new and a database of prior successful connections has not been built up. Please " + "check your computer and ensure name resolution is operating correctly."; + + break; + + case QAbstractSocket::SocketError::SocketAccessError: + case QAbstractSocket::SocketError::SocketResourceError: + case QAbstractSocket::SocketError::SocketTimeoutError: + case QAbstractSocket::SocketError::DatagramTooLargeError: + case QAbstractSocket::SocketError::NetworkError: + case QAbstractSocket::SocketError::AddressInUseError: + case QAbstractSocket::SocketError::SocketAddressNotAvailableError: + case QAbstractSocket::SocketError::UnsupportedSocketOperationError: + case QAbstractSocket::SocketError::UnfinishedSocketOperationError: + case QAbstractSocket::SocketError::OperationError: + m_results = FAIL; + + // This does not include the common text above on purpose. + m_results_tip = "The network has experienced a low-level error and this probably means your IP address or other " + "network connection parameters are not configured correctly. Please check your network configuration " + "on your computer."; + + break; + + case QAbstractSocket::SocketError::ProxyAuthenticationRequiredError: + case QAbstractSocket::SocketError::ProxyConnectionRefusedError: + case QAbstractSocket::SocketError::ProxyConnectionClosedError: + case QAbstractSocket::SocketError::ProxyConnectionTimeoutError: + case QAbstractSocket::SocketError::ProxyNotFoundError: + case QAbstractSocket::SocketError::ProxyProtocolError: + m_results = FAIL; + + m_results_tip += "Your network may be using a proxy server to communicate to public IP addresses on the Internet, and " + "the wallet is not configured properly to use it. Please check the proxy settings under Options -> " + "Network -> Connect through SOCKS5 proxy."; + + break; + + // SSL errors will NOT be triggered unless we implement a test and site to actually do an SSL connection test. This + // is put here for completeness. + case QAbstractSocket::SocketError::SslHandshakeFailedError: + case QAbstractSocket::SocketError::SslInternalError: + case QAbstractSocket::SocketError::SslInvalidUserDataError: + m_results = FAIL; + + // This does not include the common text above on purpose. + m_results_tip = "The network is reporting an SSL error. If you also failed or got a warning on your clock test, you " + "should check your clock settings, including your time and time zone. If your clock is ok, please " + "check your computer's network configuration."; + + break; + + case QAbstractSocket::SocketError::TemporaryError: + case QAbstractSocket::SocketError::UnknownSocketError: + m_results = FAIL; + + // This does not include the common text above on purpose. + m_results_tip = "The network is reporting an unspecified socket error. If you also are failing the connection test, " + "then please check your computer's network configuration."; + } + + + return; +} + +void VerifyTCPPort::TCPFinished(){ + m_tcpSocket->close(); + m_results = PASS; + return; +} + diff --git a/src/qt/Diagnose.h b/src/qt/Diagnose.h new file mode 100644 index 0000000000..e1661f27aa --- /dev/null +++ b/src/qt/Diagnose.h @@ -0,0 +1,648 @@ + +#include +#include +#include +#include +#include +#include "fs.h" +#include "net.h" +#include "main.h" +#include "gridcoin/boinc.h" +#include "util.h" +#include +#include "qt/researcher/researchermodel.h" +#include +#include +#include +#include +#include +#include "gridcoin/upgrade.h" +#include "gridcoin/staking/difficulty.h" +#include + + +extern std::atomic g_nTimeBestReceived; +extern std::unique_ptr g_UpdateChecker; + +#ifndef BITCOIN_DIAGNOSTICSLIB_H +#define BITCOIN_DIAGNOSTICSLIB_H + +/* + * This class monitors the tests, upon construction, it will register the test + * upon destructor, it will mark the test complete + * if all tests are complete, it will clean the map + */ + +namespace DiagnoseLib{ + +/* + * This is the base class for all diagnostics than can be run + * m_results: an enum to indicate warning, failed, or passed test + * Note: Each derived class must declare its unique name using he member m_test_name, + * the m_test_name will be used to check the test status + */ +class Diagnose{ + + public: + static bool m_researcher_mode; + static const ResearcherModel* m_researcher_model; + + enum diagnoseResults {PASS,WARNING,FAIL,NONE}; + enum TestNames { + VerifyWalletIsSynced, + CheckClientVersion, + CheckConnectionCount, + CheckOutboundConnectionCount, + VerifyBoincPath, + VerifyCPIDHasRAC, + VerifyCPIDIsActive, + VerifyCPIDValid, + VerifyClock, + VerifyTCPPort, + CheckDifficulty, + TestSize //add any new test before this entry + }; + + Diagnose(){ + m_results=NONE; + m_results_string=""; + m_researcher_mode = false; + registerTest(this); + } + virtual ~Diagnose(){ + LOCK(cs_diagnostictests); + removeTestFromMap(m_test_name); + } + + /* runCheck(): calling the function will run the test*/ + virtual void runCheck()=0; + /*Get teh result of test , Fail, Warning, or PAss*/ + virtual diagnoseResults getResults(){return m_results;} + /*Return a string containing a tip for the test being done*/ + virtual std::string getResultsTip(){return m_results_tip;} + /*return the final result string*/ + virtual std::string getResultsString(){return m_results_string;} + /*The result can contains arguments using "$1". The functions returns the strings that should replace the argument*/ + virtual std::vector getStringArgs(){return m_results_string_arg;} + /*The Tip can contains arguments using "$1". The functions returns the strings that should replace the argument*/ + virtual std::vector getTipArgs(){return m_results_tip_arg;} + /*Register a running test to the map so we can check if it is runnin or not.*/ + static void registerTest(Diagnose* test){ + LOCK(cs_diagnostictests); + m_name_to_test_map[test->m_test_name]=test; + assert( m_name_to_test_map.size() < Diagnose::TestSize ); + }; + /*Set the research mode the wallet is running */ + static void setResearcherModel(ResearcherModel* model){ + m_researcher_model = model; + m_researcher_mode = !(m_researcher_model->configuredForInvestorMode() || m_researcher_model->detectedPoolMode()); + } + /*Get the research mode used during testing */ + static bool getResearcherModel(){return m_researcher_model;} + + /*Get a pointer to the test object if it exists + * retun nullptr if no test available + */ + static Diagnose* getTest(Diagnose::TestNames test_name){ + LOCK(cs_diagnostictests); + auto entry = m_name_to_test_map.find(test_name); + if (entry != m_name_to_test_map.end()){ + return entry->second; + }else{ + return nullptr; + } + } + //remove the test from the globa map so it can not be tracked any more + //make sure to call this function in the destructor of any test + static void removeTestFromMap(Diagnose::TestNames test_name){ + LOCK(cs_diagnostictests); + m_name_to_test_map.erase(test_name); + } + + + protected: + diagnoseResults m_results; + std::string m_results_tip; + std::string m_results_string; + std::vector m_results_string_arg; + std::vector m_results_tip_arg; + static CCriticalSection cs_diagnostictests; + TestNames m_test_name; + static std::unordered_map m_name_to_test_map; + +}; + +/* + * Diagnose class to check if wallet is synced and up to date + */ +class VerifyWalletIsSynced: public Diagnose{ + public: + VerifyWalletIsSynced(){ + m_test_name = Diagnose::VerifyWalletIsSynced; + } + void runCheck(){ + if (g_nTimeBestReceived == 0 && OutOfSyncByAge()){ + m_results = Diagnose::WARNING; + m_results_tip = "Your wallet is still in initial sync. If this is a sync from the beginning (genesis), the " + "sync process can take from 2 to 4 hours, or longer on a slow computer. If you have synced " + "your wallet before but you just started the wallet up, then wait a few more minutes and " + "retry the diagnostics again."; + } else if (g_nTimeBestReceived > 0 && OutOfSyncByAge()){ + m_results = Diagnose::FAIL; + m_results_tip = "Your wallet is out of sync with the network but was in sync before. If this fails there is " + "likely a severe problem that is preventing the wallet from syncing. If the lack of sync " + "is due to network connection issues, you will see failures on the network connection " + "test(s). If the network connections pass, but your wallet fails this test, and continues to " + "fail this test on repeated attempts with a few minutes in between, this could indicate a " + "more serious issue. In that case you should check the debug log to see if it sheds light " + "on the cause for no sync."; + }else{ + m_results = Diagnose::PASS; + m_results_tip = "Passed"; + } + m_results_string = ""; + } + ~VerifyWalletIsSynced(){ + + } +}; + +/* + * Diagnose class to check the number of connections to outer nodes + */ +class CheckOutboundConnectionCount: public Diagnose{ + + public: + CheckOutboundConnectionCount(){ + m_test_name = Diagnose::CheckOutboundConnectionCount; + } + void runCheck(){ + + LOCK(cs_vNodes); + + int outbound_connections = 0; + + for (const auto& vnodes : vNodes) + { + if (!vnodes->fInbound) ++outbound_connections; + } + + if (outbound_connections < 1){ + m_results_tip = "Your outbound connection count is critically low. Please check your the config file and " + "ensure your addnode entries are up-to-date. If you recently started the wallet, you may " + "want to wait another few minutes for connections to build up and then test again. Please see " + "https://gridcoin.us/wiki/config-file.html and https://addnodes.cycy.me/."; + m_results_string = "Failed: Count = %1"; + m_results = Diagnose::Diagnose::FAIL; + m_results_string_arg.push_back(std::to_string(outbound_connections)); + + }else if (outbound_connections < 3){ + m_results_tip = "Your outbound connection count is low. Please check your the config file and " + "ensure your addnode entries are up-to-date. If you recently started the wallet, you may " + "want to wait another few minutes for connections to build up and then test again. Please see " + "https://gridcoin.us/wiki/config-file.html and https://addnodes.cycy.me/."; + m_results = Diagnose::WARNING; + }else{ + m_results_tip = ""; + m_results_string = "Passed: Count = %1"; + m_results_string_arg.push_back(std::to_string(outbound_connections)); + m_results = Diagnose::PASS; + } + + } + +}; + +/* + * Diagnose class to check if number of connections is not very low + */ +class CheckConnectionCount: public Diagnose{ + + protected: + size_t m_connections; + public: + CheckConnectionCount(){ + m_connections = 0; + { + LOCK(cs_vNodes); + m_connections = vNodes.size(); + } + + m_test_name = Diagnose::CheckConnectionCount; + } + size_t getConnectionsNum(){return m_connections;} + void runCheck(){ + + size_t minimum_connections_to_stake = fTestNet ? 1 : 3; + + if (m_connections <= 7 && m_connections >= minimum_connections_to_stake){ + m_results_tip = "Please check your network and also check the config file and ensure your addnode entries " + "are up-to-date. If you recently started the wallet, you may want to wait another few " + "minutes for connections to build up and test again. Please see " + "https://gridcoin.us/wiki/config-file.html and https://addnodes.cycy.me/."; + m_results = Diagnose::WARNING; + m_results_string = "Warning: Count = %1 (Pass = 8+)"; + m_results_string_arg.push_back(std::to_string(m_connections)); + }else if (m_connections >= 8){ + m_results_tip = ""; + m_results_string = "Warning: Count = %1"; + m_results_string_arg.push_back(std::to_string(m_connections)); + m_results = Diagnose::PASS; + + }else{ + m_results_tip = "You will not be able to stake because you have less than %1 connection(s). Please check " + "your network and also check the config file and ensure your addnode entries are up-to-date. " + "If you recently started the wallet, you may want to wait another few minutes for connections " + "to build up and then test again. Please see https://gridcoin.us/wiki/config-file.html and " + "https://addnodes.cycy.me/."; + m_results = Diagnose::FAIL; + m_results_string = "Warning: Count = %1"; + m_results_string_arg.push_back(std::to_string(minimum_connections_to_stake)); + + } + + } + +}; + +/* + * Diagnose class to check number of connection counts + */ +class VerifyClock: public CheckConnectionCount, public QObject{ + Q_OBJECT + private slots: + void clkFinished(); + void clkStateChanged(QAbstractSocket::SocketState state); + void clkSocketError(); + + private: + QUdpSocket *m_udpSocket; + void clkReportResults(const int64_t& time_offset, const bool& timeout_during_check = false); + /*bool m_hasQAppstartedBefore = false; + static QCoreApplication * app = nullptr; + QUdpSocket *m_udpSocket;*/ + + public: + VerifyClock(){ + /*if (QCoreApplication::instance() == NULL){ + app = new QCoreApplication(argc, argv); + app->exec(); + m_hasQAppstartedBefore = false; + }else{ + m_hasQAppstartedBefore = true; + }*/ + m_test_name = Diagnose::VerifyClock; + } + ~VerifyClock(){} + void runCheck(){ + if (m_connections >= 5){ + int64_t time_offset = 0; + + { + LOCK(cs_main); + time_offset = GetTimeOffset(); + } + clkReportResults(time_offset); + }else{ + + QTimer *timerVerifyClock = new QTimer(); + + // Set up a timeout clock of 10 seconds as a fail-safe. + connect(timerVerifyClock, &QTimer::timeout, this, &VerifyClock::clkFinished); + timerVerifyClock->start(10 * 1000); + + QHostInfo NTPHost = QHostInfo::fromName("pool.ntp.org"); + m_udpSocket = new QUdpSocket(this); + + connect(m_udpSocket, &QUdpSocket::stateChanged, this, &VerifyClock::clkStateChanged); + + // For Qt 5.15 and above QAbstractSocket::error has been deprecated in favor of errorOccurred. +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + connect(m_udpSocket, static_cast(&QUdpSocket::error), + this, static_cast(&VerifyClock::clkSocketError)); +#else + connect(m_udpSocket, &QUdpSocket::errorOccurred, this, &VerifyClock::clkSocketError); +#endif + + if (!NTPHost.addresses().empty()){ + m_udpSocket->connectToHost(QHostAddress(NTPHost.addresses().first()), 123, QIODevice::ReadWrite); + }else{ + clkSocketError(); + } + + } + + } + +}; + + +/* + * Diagnose class to check the version of the wallet + */ +class CheckClientVersion: public Diagnose{ + public: + CheckClientVersion(){ + m_test_name = Diagnose::CheckClientVersion; + } + void runCheck(){ + std::string client_message; + + if (g_UpdateChecker->CheckForLatestUpdate(client_message, false) && client_message.find("mandatory") != std::string::npos){ + m_results_tip = "There is a new mandatory version available and you should upgrade as soon as possible to " + "ensure your wallet remains in consensus with the network."; + m_results=Diagnose::FAIL; + + }else if (g_UpdateChecker->CheckForLatestUpdate(client_message, false) && client_message.find("mandatory") == std::string::npos){ + m_results_tip = "There is a new leisure version available and you should upgrade as soon as practical."; + m_results=Diagnose::WARNING; + }else{ + m_results_tip = ""; + m_results = Diagnose::PASS; + + } + } +}; + +class VerifyBoincPath: public Diagnose{ + + public: + VerifyBoincPath(){ + m_test_name = Diagnose::VerifyBoincPath; + } + void runCheck(){ + // This test is only applicable if the wallet is in researcher mode. + if (!m_researcher_mode){ + m_results_tip = ""; + m_results_string = ""; + m_results = NONE; + + return; + } + + + fs::path boincPath = (fs::path) GRC::GetBoincDataDir(); + + if (boincPath.empty()){ + boincPath = (fs::path) gArgs.GetArg("-boincdatadir", ""); + + boincPath = boincPath / "client_state.xml"; + + if (fs::exists(boincPath)){ + m_results_tip = ""; + m_results_string = ""; + m_results = Diagnose::PASS; + }else{ + m_results_tip = "Check that BOINC is installed and that you have the correct path in the config file " + "if you installed it to a nonstandard location."; + + m_results_string = ""; + m_results = Diagnose::FAIL; + } + } + } +}; + +/* + * If research mode, then this diagnose class checks the validity of the Boinc CPIC + */ +class VerifyCPIDValid: public Diagnose{ + + public: + VerifyCPIDValid(){ + m_test_name = Diagnose::VerifyCPIDValid; + } + void runCheck(){ + // This test is only applicable if the wallet is in researcher mode. + if (!m_researcher_mode){ + m_results_tip = ""; + m_results_string = ""; + m_results = NONE; + return; + } + + if (m_researcher_model && m_researcher_model->hasEligibleProjects()){ + m_results = Diagnose::PASS; + }else{ + if (g_nTimeBestReceived == 0 && OutOfSyncByAge()){ + m_results_tip = "Your wallet is not in sync and has not previously been in sync during this run, please " + "wait for the wallet to sync and retest. If there are other failures preventing the " + "wallet from syncing, please correct those items and retest to see if this test passes."; + m_results_string = ""; + m_results = Diagnose::WARNING; + + }else{ + m_results_tip = "Verify (1) that you have BOINC installed correctly, (2) that you have attached at least " + "one whitelisted project, (3) that you advertised your beacon with the same email as you " + "use for your BOINC project(s), and (4) that the CPID on the overview screen matches the " + "CPID when you login to your BOINC project(s) online."; + m_results_string = ""; + m_results = Diagnose::FAIL; + } + } + + } +}; + +/* + * Check if the CPID has RAC + */ +class VerifyCPIDHasRAC: public Diagnose{ + + public: + VerifyCPIDHasRAC(){ + m_test_name = Diagnose::VerifyCPIDHasRAC; + } + void runCheck(){ + // This test is only applicable if the wallet is in researcher mode. + if (!m_researcher_mode){ + m_results_tip = ""; + m_results_string = ""; + m_results = NONE; + return; + } + + + if (m_researcher_model->hasActiveBeacon()){ + m_results = Diagnose::PASS; + m_results_tip = ""; + m_results_string = ""; + + }else{ + if (g_nTimeBestReceived == 0 && OutOfSyncByAge()){ + m_results_tip = "Your wallet is not in sync and has not previously been in sync during this run, please " + "wait for the wallet to sync and retest. If there are other failures preventing the " + "wallet from syncing, please correct those items and retest to see if this test passes."; + m_results_string = ""; + m_results = Diagnose::WARNING; + + }else{ + m_results_tip = "Please ensure that you have followed the process to advertise and verify your beacon. " + "You can use the research wizard (the beacon button on the overview screen)."; + m_results_string = ""; + m_results = Diagnose::FAIL; + } + } + + } +}; + +/* + * Diagnose class to Check that CPID is active + */ +class VerifyCPIDIsActive: public Diagnose{ + + public: + VerifyCPIDIsActive(){ + m_test_name = Diagnose::VerifyCPIDIsActive; + } + void runCheck(){ + // This test is only applicable if the wallet is in researcher mode. + if (!m_researcher_mode){ + m_results_tip = ""; + m_results_string = ""; + m_results = NONE; + return; + } + + + if (m_researcher_model->hasRAC()){ + m_results = Diagnose::PASS; + m_results_tip = ""; + m_results_string = ""; + + }else{ + if (g_nTimeBestReceived == 0 && OutOfSyncByAge()){ + m_results_tip = "Your wallet is not in sync and has not previously been in sync during this run, please " + "wait for the wallet to sync and retest. If there are other failures preventing the " + "wallet from syncing, please correct those items and retest to see if this test passes."; + m_results_string = ""; + m_results = Diagnose::WARNING; + }else{ + m_results_tip = "Verify that you have actually completed workunits for the projects you have attached and " + "that you have authorized the export of statistics. Please see " + "https://gridcoin.us/guides/whitelist.htm."; + m_results_string = ""; + m_results = Diagnose::FAIL; + } + } + + } +}; + +/* + * Diagnose class to verify correct tcp ports are open + */ +class VerifyTCPPort: public Diagnose, public QObject{ + Q_OBJECT + private: + QTcpSocket* m_tcpSocket; + + private slots: + void TCPFinished(); + void TCPFailed(QAbstractSocket::SocketError socket_error); + + public: + VerifyTCPPort(){ + /*if (QCoreApplication::instance() == NULL){ + app = new QCoreApplication(argc, argv); + app->exec(); + m_hasQAppstartedBefore = false; + }else{ + m_hasQAppstartedBefore = true; + }*/ + m_test_name = Diagnose::VerifyTCPPort; + } + ~VerifyTCPPort(){} + void runCheck(){ + auto CheckConnectionCount_Test = getTest(Diagnose::CheckConnectionCount); + if (CheckConnectionCount_Test + && CheckConnectionCount_Test->getResults() != Diagnose::NONE + && CheckConnectionCount_Test->getResults() != Diagnose::FAIL) { + m_results = Diagnose::PASS; + return; + } + m_tcpSocket = new QTcpSocket(this); + + connect(m_tcpSocket, &QTcpSocket::connected, this, &VerifyTCPPort::TCPFinished); + // For Qt 5.15 and above QAbstractSocket::error has been deprecated in favor of errorOccurred. +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + connect(m_tcpSocket, static_cast(&QTcpSocket::error), + this, static_cast( &VerifyTCPPort::TCPFailed) ); +#else + connect(m_tcpSocket, static_cast(&QTcpSocket::errorOccurred), + this, static_cast(&VerifyTCPPort::TCPFailed) ); +#endif + + m_tcpSocket->connectToHost("portquiz.net", GetListenPort()); + } + +}; + +/* + * Diagnose class to check if stacking difficulty is not very low or very high + */ +class CheckDifficulty: public Diagnose{ + public: + CheckDifficulty(){ + m_test_name = Diagnose::CheckDifficulty; + } + void runCheck(){ + double diff = 0; + double scale_factor = 1.0; + + { + LOCK(cs_main); + + scale_factor = fTestNet ? 0.1 : 1.0; + + diff = GRC::GetAverageDifficulty(80); + } + + double fail_diff = scale_factor; + double warn_diff = scale_factor * 5.0; + + // If g_nTimeBestReceived == 0, the wallet is still in the initial sync process. In that case use the failure + // standard and just warn, with a different explanation. + if (g_nTimeBestReceived == 0 && OutOfSyncByAge() && diff < fail_diff) + { + m_results_string = "Warning: 80 block difficulty is less than %1."; + m_results_string_arg.push_back(std::to_string(fail_diff)); + + m_results_tip = "Your difficulty is low but your wallet is still in initial sync. Please recheck it later " + "to see if this passes."; + m_results = Diagnose::WARNING; + } + // If the wallet has been in sync in the past in this run, then apply the normal standards, whether the wallet is + // in sync or not right now. + else if (g_nTimeBestReceived > 0 && diff < fail_diff) + { + m_results_string = "Failed: 80 block difficulty is less than %1. This wallet is almost certainly forked."; + m_results_string_arg.push_back( std::to_string(fail_diff)); + + m_results_tip = "Your difficulty is extremely low and your wallet is almost certainly forked. Please ensure " + "you are running the latest version and try removing the blockchain database and resyncing " + "from genesis using the menu option. (Note this will take 2-4 hours.)"; + m_results = Diagnose::FAIL; + } + else if (g_nTimeBestReceived > 0 && diff < warn_diff) + { + m_results_string = "Warning: 80 block difficulty is less than %1. This wallet is probably forked."; + m_results_string_arg.push_back( std::to_string(warn_diff)); + + m_results_tip ="Your difficulty is very low and your wallet is probably forked. Please ensure you are " + "running the latest version and try removing the blockchain database and resyncing from " + "genesis using the menu option. (Note this will take 2-4 hours.)"; + m_results = Diagnose::WARNING; + } + else + { + m_results_string = "Passed: 80 block difficulty is %1."; + m_results_string_arg.push_back( std::to_string(diff)); + m_results = Diagnose::PASS; + } + } +}; + +}; //end name space +#endif diff --git a/src/qt/diagnosticsdialog.cpp b/src/qt/diagnosticsdialog.cpp index 31f5dc839b..1941d4e235 100644 --- a/src/qt/diagnosticsdialog.cpp +++ b/src/qt/diagnosticsdialog.cpp @@ -19,6 +19,7 @@ #include "qt/researcher/researchermodel.h" #include +#include extern std::atomic g_nTimeBestReceived; @@ -34,6 +35,34 @@ DiagnosticsDialog::DiagnosticsDialog(QWidget *parent, ResearcherModel* researche GRC::ScaleFontPointSize(ui->diagnosticsLabel, 14); GRC::ScaleFontPointSize(ui->overallResultLabel, 12); GRC::ScaleFontPointSize(ui->overallResultResultLabel, 12); + + //Construct the teests needed. + //IF need to add a test m just add it to the below set. + //Check Diagnose.h for the base class to create tests. + diagnoseTestInsertInSet(ui->verifyWalletIsSyncedResultLabel, + std::make_unique()); + diagnoseTestInsertInSet(ui->checkOutboundConnectionCountResultLabel, + std::make_unique()); + diagnoseTestInsertInSet(ui->checkConnectionCountResultLabel, + std::make_unique()) ; + diagnoseTestInsertInSet(ui->checkConnectionCountResultLabel, + std::make_unique()) ; + diagnoseTestInsertInSet(ui->checkConnectionCountResultLabel, + std::make_unique() ); + diagnoseTestInsertInSet(ui->verifyBoincPathResultLabel, + std::make_unique()); + diagnoseTestInsertInSet(ui->verifyCPIDValidResultLabel, + std::make_unique()); + diagnoseTestInsertInSet(ui->verifyCPIDHasRACResultLabel, + std::make_unique()); + diagnoseTestInsertInSet(ui->verifyCPIDIsActiveResultLabel, + std::make_unique()); + diagnoseTestInsertInSet(ui->verifyTCPPortResultLabel, + std::make_unique()); + diagnoseTestInsertInSet(ui->checkDifficultyResultLabel, + std::make_unique()); + + } DiagnosticsDialog::~DiagnosticsDialog() @@ -264,27 +293,54 @@ void DiagnosticsDialog::on_testButton_clicked() m_researcher_mode = !(m_researcher_model->configuredForInvestorMode() || m_researcher_model->detectedPoolMode()); - VerifyWalletIsSynced(); + for(auto &i: m_diagnostic_tests){ + UpdateTestStatus(__func__, ui->checkOutboundConnectionCountResultLabel, pending, NA); + auto &dignosetest = i.second; + auto diagnoselabel = i.first; + dignosetest->runCheck(); + QString tooltip = tr( dignosetest->getResultsTip().c_str()); + QString resultString = tr( dignosetest->getResultsString().c_str() ); + for(auto &j: dignosetest->getStringArgs()) + resultString = resultString.arg(QString::fromStdString(j)); + for(auto &j: dignosetest->getTipArgs()) + tooltip = tooltip.arg(QString::fromStdString(j)); + + + if( dignosetest->getResults()==DiagnoseLib::Diagnose::NONE){ + UpdateTestStatus(__func__, diagnoselabel, completed, NA); + }else if( dignosetest->getResults()==DiagnoseLib::Diagnose::FAIL){ + UpdateTestStatus(__func__, diagnoselabel, completed, failed, + resultString, tooltip); + }else if( dignosetest->getResults()== DiagnoseLib::Diagnose::WARNING){ + UpdateTestStatus(__func__, diagnoselabel, completed, warning, + resultString, tooltip); + }else{ + UpdateTestStatus(__func__, diagnoselabel, completed, passed, + resultString); + } + } - unsigned int connections = CheckConnectionCount(); + // VerifyWalletIsSynced(); - CheckOutboundConnectionCount(); + // unsigned int connections = CheckConnectionCount(); - VerifyClock(connections); + //CheckOutboundConnectionCount(); - VerifyTCPPort(); + //VerifyClock(connections); + + //VerifyTCPPort(); double diff = CheckDifficulty(); - CheckClientVersion(); + //CheckClientVersion(); - VerifyBoincPath(); + //VerifyBoincPath(); - VerifyCPIDValid(); + //VerifyCPIDValid(); - VerifyCPIDHasRAC(); + //VerifyCPIDHasRAC(); - VerifyCPIDIsActive(); + //VerifyCPIDIsActive(); CheckETTS(diff); @@ -293,8 +349,9 @@ void DiagnosticsDialog::on_testButton_clicked() void DiagnosticsDialog::VerifyWalletIsSynced() { + UpdateTestStatus(__func__, ui->verifyWalletIsSyncedResultLabel, pending, NA); - + if (g_nTimeBestReceived == 0 && OutOfSyncByAge()) { QString tooltip = tr("Your wallet is still in initial sync. If this is a sync from the beginning (genesis), the " @@ -367,8 +424,7 @@ int DiagnosticsDialog::CheckConnectionCount() void DiagnosticsDialog::CheckOutboundConnectionCount() { - UpdateTestStatus(__func__, ui->checkOutboundConnectionCountResultLabel, pending, NA); - + LOCK(cs_vNodes); int outbound_connections = 0; @@ -571,7 +627,7 @@ void DiagnosticsDialog::VerifyTCPPort() // Note that if the CheckConnectionCount test has been run already (which it must given the order the tests are // run above), then the test result cannot be NA. So if it did not fail, it must be either warning or passed, // which is sufficient in and of itself to mark VerifyTCPPort() successful without further testing, since - // to get valid outbound connections, the port must be working. + // to get valid oresultString = utbound connections, the port must be working. if (GetTestStatus("CheckConnectionCount") == completed && GetTestResult("CheckConnectionCount") != failed) { UpdateTestStatus("VerifyTCPPort", ui->verifyTCPPortResultLabel, completed, passed); @@ -771,8 +827,7 @@ double DiagnosticsDialog::CheckDifficulty() void DiagnosticsDialog::CheckClientVersion() { - UpdateTestStatus(__func__, ui->checkClientVersionResultLabel, pending, NA); - + std::string client_message; if (g_UpdateChecker->CheckForLatestUpdate(client_message, false) @@ -800,6 +855,7 @@ void DiagnosticsDialog::CheckClientVersion() void DiagnosticsDialog::VerifyBoincPath() { + // This test is only applicable if the wallet is in researcher mode. if (!m_researcher_mode) { @@ -832,6 +888,7 @@ void DiagnosticsDialog::VerifyBoincPath() void DiagnosticsDialog::VerifyCPIDValid() { + // This test is only applicable if the wallet is in researcher mode. if (!m_researcher_mode) { @@ -868,8 +925,9 @@ void DiagnosticsDialog::VerifyCPIDValid() } } -void DiagnosticsDialog::VerifyCPIDHasRAC() -{ +void DiagnosticsDialog::VerifyCPIDHasRAC(){ + + // This test is only applicable if the wallet is in researcher mode. if (!m_researcher_mode) { @@ -907,6 +965,7 @@ void DiagnosticsDialog::VerifyCPIDHasRAC() void DiagnosticsDialog::VerifyCPIDIsActive() { + // This test is only applicable if the wallet is in researcher mode. if (!m_researcher_mode) { @@ -929,7 +988,6 @@ void DiagnosticsDialog::VerifyCPIDIsActive() "wait for the wallet to sync and retest. If there are other failures preventing the " "wallet from syncing, please correct those items and retest to see if this test passes."); - UpdateTestStatus(__func__, ui->verifyCPIDIsActiveResultLabel, completed, warning, QString(), tooltip); } else { diff --git a/src/qt/diagnosticsdialog.h b/src/qt/diagnosticsdialog.h index adea82eb26..ca9e720c81 100644 --- a/src/qt/diagnosticsdialog.h +++ b/src/qt/diagnosticsdialog.h @@ -13,6 +13,10 @@ #include #include "sync.h" +#include +#include +#include +#include "Diagnose.h" class ResearcherModel; @@ -59,6 +63,10 @@ class DiagnosticsDialog : public QDialog void VerifyCPIDIsActive(); void CheckETTS(const double& diff); + typedef std::set>> DiagnoseLabelTestPter_set; + //Set the contains a pair