diff --git a/build-msw.txt b/build-msw.txt index eb6348bad..a159a71ae 100644 --- a/build-msw.txt +++ b/build-msw.txt @@ -8,7 +8,7 @@ the OpenSSL Toolkit (http://www.openssl.org/). This product includes cryptographic software written by Eric Young (eay@cryptsoft.com). - WINDOWS BUILD NOTES +WINDOWS BUILD NOTES Compilers Supported diff --git a/build-unix.txt b/build-unix.txt index 6b29e1e5c..ceb61ad54 100644 --- a/build-unix.txt +++ b/build-unix.txt @@ -13,6 +13,7 @@ UNIX BUILD NOTES Dependencies ------------ +Install the dev files for the shared libraries: apt-get install build-essential apt-get install libgtk2.0-dev apt-get install libssl-dev diff --git a/db.cpp b/db.cpp index 61d602533..a9c42880a 100644 --- a/db.cpp +++ b/db.cpp @@ -505,6 +505,13 @@ bool CWalletDB::LoadWallet(vector& vchDefaultKeyRet) { vchDefaultKeyRet.clear(); + // Modify defaults +#ifndef __WXMSW__ + // Reports that tray icon can disappear on gnome, leaving no way to access the program + fMinimizeToTray = false; + fMinimizeOnClose = false; +#endif + //// todo: shouldn't we catch exceptions and try to recover and continue? CRITICAL_BLOCK(cs_mapKeys) CRITICAL_BLOCK(cs_mapWallet) @@ -638,7 +645,7 @@ bool LoadWallet(bool& fFirstRunRet) CWalletDB().WriteDefaultKey(keyUser.GetPubKey()); } - _beginthread(ThreadFlushWalletDB, 0, NULL); + CreateThread(ThreadFlushWalletDB, NULL); return true; } diff --git a/headers.h b/headers.h index 76881bb93..22bb830f7 100644 --- a/headers.h +++ b/headers.h @@ -75,7 +75,6 @@ #include #include #include -#include #include #endif diff --git a/irc.cpp b/irc.cpp index 3b232caea..8432c6d14 100644 --- a/irc.cpp +++ b/irc.cpp @@ -54,7 +54,7 @@ static bool Send(SOCKET hSocket, const char* pszSend) const char* pszEnd = psz + strlen(psz); while (psz < pszEnd) { - int ret = send(hSocket, psz, pszEnd - psz, 0); + int ret = send(hSocket, psz, pszEnd - psz, MSG_NOSIGNAL); if (ret < 0) return false; psz += ret; @@ -156,7 +156,7 @@ bool Wait(int nSeconds) void ThreadIRCSeed(void* parg) { - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); + SetThreadPriority(THREAD_PRIORITY_NORMAL); int nErrorWait = 10; int nRetryWait = 10; @@ -256,6 +256,7 @@ void ThreadIRCSeed(void* parg) CAddress addr; if (DecodeAddress(pszName, addr)) { + addr.nTime = GetAdjustedTime() - 51 * 60; CAddrDB addrdb; if (AddAddress(addrdb, addr)) printf("IRC got new address\n"); diff --git a/main.cpp b/main.cpp index b957027f0..13a9f9b72 100644 --- a/main.cpp +++ b/main.cpp @@ -42,6 +42,9 @@ map > mapPubKeys; CCriticalSection cs_mapKeys; CKey keyUser; +map mapRequestCount; +CCriticalSection cs_mapRequestCount; + // Settings int fGenerateBitcoins = false; int64 nTransactionFee = 0; @@ -274,7 +277,44 @@ int64 CWalletTx::GetTxTime() const return nTimeReceived; } +int CWalletTx::GetRequestCount() const +{ + // Returns -1 if it wasn't being tracked + int nRequests = -1; + CRITICAL_BLOCK(cs_mapRequestCount) + { + if (IsCoinBase()) + { + // Generated block + if (hashBlock != 0) + { + map::iterator mi = mapRequestCount.find(hashBlock); + if (mi != mapRequestCount.end()) + nRequests = (*mi).second; + } + } + else + { + // Did anyone request this transaction? + map::iterator mi = mapRequestCount.find(GetHash()); + if (mi != mapRequestCount.end()) + { + nRequests = (*mi).second; + // How about the block it's in? + if (nRequests == 0 && hashBlock != 0) + { + map::iterator mi = mapRequestCount.find(hashBlock); + if (mi != mapRequestCount.end()) + nRequests = (*mi).second; + else + nRequests = 1; // If it's in someone else's block it must have got out + } + } + } + } + return nRequests; +} @@ -295,7 +335,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) CTxIndex txindex; if (!CTxDB("r").ReadTxIndex(GetHash(), txindex)) return 0; - if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, true)) + if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos)) return 0; pblock = &blockTmp; } @@ -1003,7 +1043,7 @@ bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) foreach(CBlockIndex* pindex, vDisconnect) { CBlock block; - if (!block.ReadFromDisk(pindex->nFile, pindex->nBlockPos, true)) + if (!block.ReadFromDisk(pindex->nFile, pindex->nBlockPos)) return error("Reorganize() : ReadFromDisk for disconnect failed"); if (!block.DisconnectBlock(txdb, pindex)) return error("Reorganize() : DisconnectBlock failed"); @@ -1020,7 +1060,7 @@ bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) { CBlockIndex* pindex = vConnect[i]; CBlock block; - if (!block.ReadFromDisk(pindex->nFile, pindex->nBlockPos, true)) + if (!block.ReadFromDisk(pindex->nFile, pindex->nBlockPos)) return error("Reorganize() : ReadFromDisk for connect failed"); if (!block.ConnectBlock(txdb, pindex)) { @@ -1380,7 +1420,7 @@ bool CheckDiskSpace(int64 nAdditionalBytes) { fShutdown = true; ThreadSafeMessageBox("Warning: Your disk space is low ", "Bitcoin", wxOK | wxICON_EXCLAMATION); - _beginthread(Shutdown, 0, NULL); + CreateThread(Shutdown, NULL); return false; } return true; @@ -1547,7 +1587,7 @@ void PrintBlockTree() // print item CBlock block; - block.ReadFromDisk(pindex, true); + block.ReadFromDisk(pindex); printf("%d (%u,%u) %s %s tx %d", pindex->nHeight, pindex->nFile, @@ -1623,7 +1663,8 @@ bool ProcessMessages(CNode* pfrom) CDataStream& vRecv = pfrom->vRecv; if (vRecv.empty()) return true; - //printf("ProcessMessages(%d bytes)\n", vRecv.size()); + //if (fDebug) + // printf("ProcessMessages(%d bytes)\n", vRecv.size()); // // Message format @@ -1666,7 +1707,8 @@ bool ProcessMessages(CNode* pfrom) { // Rewind and wait for rest of message ///// need a mechanism to give up waiting for overlong message size error - //printf("message-break\n"); + //if (fDebug) + // printf("message-break\n"); vRecv.insert(vRecv.begin(), BEGIN(hdr), END(hdr)); Sleep(100); break; @@ -1718,6 +1760,8 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { static map > mapReuseKey; RandAddSeedPerfmon(); + if (fDebug) + printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); printf("received: %s (%d bytes)\n", strCommand.c_str(), vRecv.size()); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { @@ -1739,18 +1783,19 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CAddress addrMe; CAddress addrFrom; uint64 nNonce = 1; + string strSubVer; vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; if (pfrom->nVersion >= 106 && !vRecv.empty()) vRecv >> addrFrom >> nNonce; + if (pfrom->nVersion >= 106 && !vRecv.empty()) + vRecv >> strSubVer; if (pfrom->nVersion == 0) return false; // Disconnect if we connected to ourself - if (nNonce == nLocalHostNonce) + if (nNonce == nLocalHostNonce && nNonce > 1) { pfrom->fDisconnect = true; - pfrom->vRecv.clear(); - pfrom->vSend.clear(); return true; } @@ -1776,10 +1821,6 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fSuccessfullyConnected = true; - // Update the last seen time - if (pfrom->fNetworkNode) - AddressCurrentlyConnected(pfrom->addr); - printf("version message: version %d\n", pfrom->nVersion); } @@ -1824,10 +1865,6 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vector vInv; vRecv >> vInv; - // Update the last seen time for this node's address - if (pfrom->fNetworkNode) - AddressCurrentlyConnected(pfrom->addr); - CTxDB txdb("r"); foreach(const CInv& inv, vInv) { @@ -1842,6 +1879,14 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->AskFor(inv); else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) pfrom->PushMessage("getblocks", CBlockLocator(pindexBest), GetOrphanRoot(mapOrphanBlocks[inv.hash])); + + // Track requests for our stuff + CRITICAL_BLOCK(cs_mapRequestCount) + { + map::iterator mi = mapRequestCount.find(inv.hash); + if (mi != mapRequestCount.end()) + (*mi).second++; + } } } @@ -1879,6 +1924,14 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->PushMessage(inv.GetCommand(), (*mi).second); } } + + // Track requests for our stuff + CRITICAL_BLOCK(cs_mapRequestCount) + { + map::iterator mi = mapRequestCount.find(inv.hash); + if (mi != mapRequestCount.end()) + (*mi).second++; + } } } @@ -2086,11 +2139,23 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } + else if (strCommand == "ping") + { + } + + else { // Ignore unknown commands for extensibility } + + // Update the last seen time for this node's address + if (pfrom->fNetworkNode) + if (strCommand == "version" || strCommand == "addr" || strCommand == "inv" || strCommand == "getdata" || strCommand == "ping") + AddressCurrentlyConnected(pfrom->addr); + + return true; } @@ -2129,6 +2194,10 @@ bool SendMessages(CNode* pto) } } + // Keep-alive ping + if (pto->nLastSend && GetTime() - pto->nLastSend > 12 * 60 && pto->vSend.empty()) + pto->PushMessage("ping"); + // // Message: addr @@ -2139,7 +2208,14 @@ bool SendMessages(CNode* pto) { // returns true if wasn't already contained in the set if (pto->setAddrKnown.insert(addr).second) + { vAddrToSend.push_back(addr); + if (vAddrToSend.size() >= 1000) + { + pto->PushMessage("addr", vAddrToSend); + vAddrToSend.clear(); + } + } } pto->vAddrToSend.clear(); if (!vAddrToSend.empty()) @@ -2157,7 +2233,14 @@ bool SendMessages(CNode* pto) { // returns true if wasn't already contained in the set if (pto->setInventoryKnown.insert(inv).second) + { vInventoryToSend.push_back(inv); + if (vInventoryToSend.size() >= 1000) + { + pto->PushMessage("inv", vInventoryToSend); + vInventoryToSend.clear(); + } + } } pto->vInventoryToSend.clear(); pto->setInventoryKnown2.clear(); @@ -2179,6 +2262,11 @@ bool SendMessages(CNode* pto) { printf("sending getdata: %s\n", inv.ToString().c_str()); vAskFor.push_back(inv); + if (vAskFor.size() >= 1000) + { + pto->PushMessage("getdata", vAskFor); + vAskFor.clear(); + } } pto->mapAskFor.erase(pto->mapAskFor.begin()); } @@ -2226,8 +2314,8 @@ void GenerateBitcoins(bool fGenerate) int nAddThreads = nProcessors - vnThreadsRunning[3]; printf("Starting %d BitcoinMiner threads\n", nAddThreads); for (int i = 0; i < nAddThreads; i++) - if (_beginthread(ThreadBitcoinMiner, 0, NULL) == -1) - printf("Error: _beginthread(ThreadBitcoinMiner) failed\n"); + if (!CreateThread(ThreadBitcoinMiner, NULL)) + printf("Error: CreateThread(ThreadBitcoinMiner) failed\n"); } } @@ -2304,7 +2392,7 @@ void BitcoinMiner() CBigNum bnExtraNonce = 0; while (fGenerateBitcoins) { - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST); + SetThreadPriority(THREAD_PRIORITY_LOWEST); Sleep(50); if (fShutdown) return; @@ -2440,7 +2528,7 @@ void BitcoinMiner() printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); pblock->print(); - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); + SetThreadPriority(THREAD_PRIORITY_NORMAL); CRITICAL_BLOCK(cs_main) { if (pindexPrev == pindexBest) @@ -2450,12 +2538,16 @@ void BitcoinMiner() return; key.MakeNewKey(); + // Track how many getdata requests this block gets + CRITICAL_BLOCK(cs_mapRequestCount) + mapRequestCount[pblock->GetHash()] = 0; + // Process this block the same as if we had received it from another node if (!ProcessBlock(NULL, pblock.release())) printf("ERROR in BitcoinMiner, ProcessBlock, block not accepted\n"); } } - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST); + SetThreadPriority(THREAD_PRIORITY_LOWEST); Sleep(500); break; @@ -2534,7 +2626,7 @@ bool SelectCoins(int64 nTargetValue, set& setCoinsRet) setCoinsRet.clear(); // List of values less than target - int64 nLowestLarger = _I64_MAX; + int64 nLowestLarger = INT64_MAX; CWalletTx* pcoinLowestLarger = NULL; vector > vValue; int64 nTotalLower = 0; @@ -2777,6 +2869,10 @@ bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew) return error("SendMoney() : Error finalizing transaction"); } + // Track how many getdata requests our transaction gets + CRITICAL_BLOCK(cs_mapRequestCount) + mapRequestCount[wtxNew.GetHash()] = 0; + printf("SendMoney: %s\n", wtxNew.GetHash().ToString().substr(0,6).c_str()); // Broadcast diff --git a/main.h b/main.h index 853fdfa5b..8bb1e19e8 100644 --- a/main.h +++ b/main.h @@ -34,6 +34,8 @@ extern int nBestHeight; extern uint256 hashBestChain; extern CBlockIndex* pindexBest; extern unsigned int nTransactionsUpdated; +extern map mapRequestCount; +extern CCriticalSection cs_mapRequestCount; // Settings extern int fGenerateBitcoins; @@ -647,6 +649,15 @@ public: nGetCreditCached = 0; } + IMPLEMENT_SERIALIZE + ( + nSerSize += SerReadWrite(s, *(CTransaction*)this, nType, nVersion, ser_action); + nVersion = this->nVersion; + READWRITE(hashBlock); + READWRITE(vMerkleBranch); + READWRITE(nIndex); + ) + int64 GetCredit(bool fUseCache=false) const { // Must wait until coinbase is safely deep enough in the chain before valuing it @@ -661,15 +672,6 @@ public: return nGetCreditCached; } - IMPLEMENT_SERIALIZE - ( - nSerSize += SerReadWrite(s, *(CTransaction*)this, nType, nVersion, ser_action); - nVersion = this->nVersion; - READWRITE(hashBlock); - READWRITE(vMerkleBranch); - READWRITE(nIndex); - ) - int SetMerkleBranch(const CBlock* pblock=NULL); int GetDepthInMainChain() const; @@ -749,6 +751,7 @@ public: int64 GetTxTime() const; + int GetRequestCount() const; void AddSupportingTransactions(CTxDB& txdb); @@ -978,7 +981,7 @@ public: return true; } - bool ReadFromDisk(unsigned int nFile, unsigned int nBlockPos, bool fReadTransactions) + bool ReadFromDisk(unsigned int nFile, unsigned int nBlockPos, bool fReadTransactions=true) { SetNull(); @@ -1027,7 +1030,7 @@ public: int64 GetBlockValue(int64 nFees) const; bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex); bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex); - bool ReadFromDisk(const CBlockIndex* blockindex, bool fReadTransactions); + bool ReadFromDisk(const CBlockIndex* blockindex, bool fReadTransactions=true); bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos); bool CheckBlock() const; bool AcceptBlock(); diff --git a/net.cpp b/net.cpp index 0569604ba..71295d5de 100644 --- a/net.cpp +++ b/net.cpp @@ -13,7 +13,6 @@ bool OpenNetworkConnection(const CAddress& addrConnect); - // // Global state variables // @@ -25,6 +24,7 @@ uint64 nLocalHostNonce = 0; bool fShutdown = false; array vnThreadsRunning; SOCKET hListenSocket = INVALID_SOCKET; +int64 nThreadSocketHandlerHeartbeat = INT64_MAX; vector vNodes; CCriticalSection cs_vNodes; @@ -65,7 +65,7 @@ bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet) if (fProxy) { - printf("Proxy connecting %s\n", addrConnect.ToStringLog().c_str()); + printf("proxy connecting %s\n", addrConnect.ToStringLog().c_str()); char pszSocks4IP[] = "\4\1\0\0\0\0\0\0user"; memcpy(pszSocks4IP + 2, &addrConnect.port, 2); memcpy(pszSocks4IP + 4, &addrConnect.ip, 4); @@ -87,9 +87,11 @@ bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet) if (pchRet[1] != 0x5a) { closesocket(hSocket); - return error("Proxy returned error %d", pchRet[1]); + if (pchRet[1] != 0x5b) + printf("ERROR: Proxy returned error %d\n", pchRet[1]); + return false; } - printf("Proxy connection established %s\n", addrConnect.ToStringLog().c_str()); + printf("proxy connected %s\n", addrConnect.ToStringLog().c_str()); } hSocketRet = hSocket; @@ -219,6 +221,7 @@ bool AddAddress(CAddrDB& addrdb, CAddress addr, bool fCurrentlyOnline) if (it == mapAddresses.end()) { // New address + printf("AddAddress(%s)\n", addr.ToStringLog().c_str()); mapAddresses.insert(make_pair(addr.GetKey(), addr)); addrdb.WriteAddress(addr); return true; @@ -256,7 +259,7 @@ void AddressCurrentlyConnected(const CAddress& addr) if (it != mapAddresses.end()) { CAddress& addrFound = (*it).second; - int64 nUpdateInterval = 60 * 60; + int64 nUpdateInterval = 20 * 60; if (addrFound.nTime < GetAdjustedTime() - nUpdateInterval) { // Periodically update most recently seen time @@ -417,7 +420,13 @@ CNode* ConnectNode(CAddress addrConnect, int64 nTimeout) } /// debug print - printf("trying connection %s\n", addrConnect.ToStringLog().c_str()); + printf("trying connection %s lastseen=%.1fhrs lasttry=%.1fhrs\n", + addrConnect.ToStringLog().c_str(), + (double)(addrConnect.nTime - GetAdjustedTime())/3600.0, + (double)(addrConnect.nLastTry - GetAdjustedTime())/3600.0); + + CRITICAL_BLOCK(cs_mapAddresses) + mapAddresses[addrConnect.GetKey()].nLastTry = GetAdjustedTime(); // Connect SOCKET hSocket; @@ -428,7 +437,7 @@ CNode* ConnectNode(CAddress addrConnect, int64 nTimeout) // Set to nonblocking #ifdef __WXMSW__ - u_long nOne = 1; + u_long nOne = 1; if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) printf("ConnectSocket() : ioctlsocket nonblocking setting failed, error %d\n", WSAGetLastError()); #else @@ -445,29 +454,23 @@ CNode* ConnectNode(CAddress addrConnect, int64 nTimeout) CRITICAL_BLOCK(cs_vNodes) vNodes.push_back(pnode); - CRITICAL_BLOCK(cs_mapAddresses) - mapAddresses[addrConnect.GetKey()].nLastFailed = 0; + pnode->nTimeConnected = GetTime(); return pnode; } else { - CRITICAL_BLOCK(cs_mapAddresses) - mapAddresses[addrConnect.GetKey()].nLastFailed = GetAdjustedTime(); return NULL; } } void CNode::DoDisconnect() { + if (fDebug) + printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); printf("disconnecting node %s\n", addr.ToStringLog().c_str()); closesocket(hSocket); - // If outbound and never got version message, mark address as failed - if (!fInbound && !fSuccessfullyConnected) - CRITICAL_BLOCK(cs_mapAddresses) - mapAddresses[addr.GetKey()].nLastFailed = GetAdjustedTime(); - // All of a nodes broadcasts and subscriptions are automatically torn down // when it goes down, so a node has to stay up to keep its broadcast going. @@ -508,7 +511,7 @@ void ThreadSocketHandler(void* parg) PrintException(&e, "ThreadSocketHandler()"); } catch (...) { vnThreadsRunning[0]--; - PrintException(NULL, "ThreadSocketHandler()"); + throw; // support pthread_cancel() } printf("ThreadSocketHandler exiting\n"); @@ -531,15 +534,18 @@ void ThreadSocketHandler2(void* parg) vector vNodesCopy = vNodes; foreach(CNode* pnode, vNodesCopy) { - if (pnode->ReadyToDisconnect() && pnode->vRecv.empty() && pnode->vSend.empty()) + if (pnode->fDisconnect || + (pnode->GetRefCount() <= 0 && pnode->vRecv.empty() && pnode->vSend.empty())) { // remove from vNodes vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); + + // close socket pnode->DoDisconnect(); // hold in disconnected pool until all refs are released pnode->nReleaseTime = max(pnode->nReleaseTime, GetTime() + 5 * 60); - if (pnode->fNetworkNode) + if (pnode->fNetworkNode || pnode->fInbound) pnode->Release(); vNodesDisconnected.push_back(pnode); } @@ -582,8 +588,10 @@ void ThreadSocketHandler2(void* parg) fd_set fdsetRecv; fd_set fdsetSend; + fd_set fdsetError; FD_ZERO(&fdsetRecv); FD_ZERO(&fdsetSend); + FD_ZERO(&fdsetError); SOCKET hSocketMax = 0; FD_SET(hListenSocket, &fdsetRecv); hSocketMax = max(hSocketMax, hListenSocket); @@ -592,6 +600,7 @@ void ThreadSocketHandler2(void* parg) foreach(CNode* pnode, vNodes) { FD_SET(pnode->hSocket, &fdsetRecv); + FD_SET(pnode->hSocket, &fdsetError); hSocketMax = max(hSocketMax, pnode->hSocket); TRY_CRITICAL_BLOCK(pnode->cs_vSend) if (!pnode->vSend.empty()) @@ -600,30 +609,21 @@ void ThreadSocketHandler2(void* parg) } vnThreadsRunning[0]--; - int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, NULL, &timeout); + int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, &fdsetError, &timeout); vnThreadsRunning[0]++; if (fShutdown) return; if (nSelect == SOCKET_ERROR) { int nErr = WSAGetLastError(); - printf("select failed: %d\n", nErr); + printf("socket select error %d\n", nErr); for (int i = 0; i <= hSocketMax; i++) - { FD_SET(i, &fdsetRecv); - FD_SET(i, &fdsetSend); - } + FD_ZERO(&fdsetSend); + FD_ZERO(&fdsetError); Sleep(timeout.tv_usec/1000); } - //// debug print - //foreach(CNode* pnode, vNodes) - //{ - // printf("vRecv = %-5d ", pnode->vRecv.size()); - // printf("vSend = %-5d ", pnode->vSend.size()); - //} - //printf("\n"); - // // Accept new connections @@ -641,7 +641,7 @@ void ThreadSocketHandler2(void* parg) if (hSocket == INVALID_SOCKET) { if (WSAGetLastError() != WSAEWOULDBLOCK) - printf("ERROR ThreadSocketHandler accept failed: %d\n", WSAGetLastError()); + printf("socket error accept failed: %d\n", WSAGetLastError()); } else { @@ -669,7 +669,7 @@ void ThreadSocketHandler2(void* parg) // // Receive // - if (FD_ISSET(hSocket, &fdsetRecv)) + if (FD_ISSET(hSocket, &fdsetRecv) || FD_ISSET(hSocket, &fdsetError)) { TRY_CRITICAL_BLOCK(pnode->cs_vRecv) { @@ -677,25 +677,29 @@ void ThreadSocketHandler2(void* parg) unsigned int nPos = vRecv.size(); // typical socket buffer is 8K-64K - const unsigned int nBufSize = 0x10000; - vRecv.resize(nPos + nBufSize); - int nBytes = recv(hSocket, &vRecv[nPos], nBufSize, 0); - vRecv.resize(nPos + max(nBytes, 0)); - if (nBytes == 0) + char pchBuf[0x10000]; + int nBytes = recv(hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); + if (nBytes > 0) + { + vRecv.resize(nPos + nBytes); + memcpy(&vRecv[nPos], pchBuf, nBytes); + pnode->nLastRecv = GetTime(); + } + else if (nBytes == 0) { // socket closed gracefully if (!pnode->fDisconnect) - printf("recv: socket closed\n"); + printf("socket closed\n"); pnode->fDisconnect = true; } else if (nBytes < 0) { - // socket error + // error int nErr = WSAGetLastError(); if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) { if (!pnode->fDisconnect) - printf("recv failed: %d\n", nErr); + printf("socket recv error %d\n", nErr); pnode->fDisconnect = true; } } @@ -712,28 +716,63 @@ void ThreadSocketHandler2(void* parg) CDataStream& vSend = pnode->vSend; if (!vSend.empty()) { - int nBytes = send(hSocket, &vSend[0], vSend.size(), MSG_NOSIGNAL); + int nBytes = send(hSocket, &vSend[0], vSend.size(), MSG_NOSIGNAL | MSG_DONTWAIT); if (nBytes > 0) { vSend.erase(vSend.begin(), vSend.begin() + nBytes); + pnode->nLastSend = GetTime(); } - else if (nBytes == 0) - { - if (pnode->ReadyToDisconnect()) - pnode->vSend.clear(); - } - else + else if (nBytes < 0) { - printf("send error %d\n", nBytes); - if (pnode->ReadyToDisconnect()) - pnode->vSend.clear(); + // error + int nErr = WSAGetLastError(); + if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) + { + printf("socket send error %d\n", nErr); + pnode->fDisconnect = true; + } } } } } + + // + // Inactivity checking + // + if (pnode->vSend.empty()) + pnode->nLastSendEmpty = GetTime(); + if (GetTime() - pnode->nTimeConnected > 60) + { + if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) + { + printf("socket no message in first 60 seconds, %d %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0); + pnode->fDisconnect = true; + } + else if (GetTime() - pnode->nLastSend > 10 * 60 && GetTime() - pnode->nLastSendEmpty > 10 * 60) + { + printf("socket not sending\n"); + pnode->fDisconnect = true; + } + else if (GetTime() - pnode->nLastRecv > (pnode->nVersion >= 107 ? 15*60 : 90*60)) + { + printf("socket inactivity timeout\n"); + pnode->fDisconnect = true; + } + } + } + + + //// debug heartbeat + static int64 nHeartbeat1; + if (GetTime() - nHeartbeat1 >= 5 * 60) + { + printf("%s sendrecv\n", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); + nHeartbeat1 = GetTime(); + fDebug = true; } + nThreadSocketHandlerHeartbeat = GetTime(); Sleep(10); } } @@ -772,15 +811,20 @@ void ThreadOpenConnections2(void* parg) { printf("ThreadOpenConnections started\n"); - // Connect to one specified address + // Connect to specific addresses while (mapArgs.count("-connect")) { - OpenNetworkConnection(CAddress(mapArgs["-connect"])); - for (int i = 0; i < 10; i++) + foreach(string strAddr, mapMultiArgs["-connect"]) { - Sleep(1000); - if (fShutdown) - return; + CAddress addr(strAddr, NODE_NETWORK); + if (addr.IsValid()) + OpenNetworkConnection(addr); + for (int i = 0; i < 10; i++) + { + Sleep(1000); + if (fShutdown) + return; + } } } @@ -821,12 +865,7 @@ void ThreadOpenConnections2(void* parg) // Choose an address to connect to based on most recently seen // CAddress addrConnect; - int64 nBestTime = 0; - int64 nDelay = ((60 * 60) << vNodes.size()); - if (vNodes.size() >= 3) - nDelay *= 4; - if (nGotIRCAddresses > 0) - nDelay *= 100; + int64 nBest = INT64_MIN; // Do this here so we don't have to critsect vNodes inside mapAddresses critsect set setConnected; @@ -841,24 +880,51 @@ void ThreadOpenConnections2(void* parg) const CAddress& addr = item.second; if (!addr.IsIPv4() || !addr.IsValid() || setConnected.count(addr.ip)) continue; + int64 nSinceLastSeen = GetAdjustedTime() - addr.nTime; + int64 nSinceLastTry = GetAdjustedTime() - addr.nLastTry; // Randomize the order in a deterministic way, putting the standard port first - int64 nRandomizer = (uint64)(addr.nLastFailed * 9567851 + addr.ip * 7789) % (1 * 60 * 60); + int64 nRandomizer = (uint64)(addr.nLastTry * 9567851 + addr.ip * 7789) % (30 * 60); if (addr.port != DEFAULT_PORT) - nRandomizer += 1 * 60 * 60; + nRandomizer += 30 * 60; + + // Last seen Base retry frequency + // <1 hour 10 min + // 1 hour 1 hour + // 4 hours 2 hours + // 24 hours 5 hours + // 48 hours 7 hours + // 7 days 13 hours + // 30 days 27 hours + // 90 days 46 hours + // 365 days 93 hours + int64 nDelay = 3600.0 * sqrt(fabs(nSinceLastSeen) / 3600.0) + nRandomizer; + + // Fast reconnect for one hour after last seen + if (nSinceLastSeen < 60 * 60) + nDelay = 10 * 60; // Limit retry frequency - if (GetAdjustedTime() < addr.nLastFailed + nDelay + nRandomizer) + if (nSinceLastTry < nDelay) continue; - // Try again only after all addresses had a first attempt - int64 nTime = addr.nTime - nRandomizer; - if (addr.nLastFailed > addr.nTime) - nTime -= 365 * 24 * 60 * 60; + // If we have IRC, we'll be notified when they first come online, + // and again every 24 hours by the refresh broadcast. + if (nGotIRCAddresses > 0 && vNodes.size() >= 2 && nSinceLastSeen > 24 * 60 * 60) + continue; - if (nTime > nBestTime) + // Only try the old stuff if we don't have enough connections + if (vNodes.size() >= 2 && nSinceLastSeen > 7 * 24 * 60 * 60) + continue; + if (vNodes.size() >= 4 && nSinceLastSeen > 24 * 60 * 60) + continue; + + // If multiple addresses are ready, prioritize by time since + // last seen and time since last tried. + int64 nScore = min(nSinceLastTry, (int64)24 * 60 * 60) - nSinceLastSeen - nRandomizer; + if (nScore > nBest) { - nBestTime = nTime; + nBest = nScore; addrConnect = addr; } } @@ -941,7 +1007,7 @@ void ThreadMessageHandler(void* parg) void ThreadMessageHandler2(void* parg) { printf("ThreadMessageHandler started\n"); - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); + SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); loop { // Poll the connected nodes for messages @@ -1063,39 +1129,31 @@ bool BindListenPort(string& strError) return true; } -bool StartNode(string& strError) +void StartNode(void* parg) { - strError = ""; if (pnodeLocalHost == NULL) pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress("127.0.0.1", nLocalServices)); #ifdef __WXMSW__ // Get local host ip - char pszHostName[255]; - if (gethostname(pszHostName, sizeof(pszHostName)) == SOCKET_ERROR) + char pszHostName[1000] = ""; + if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) { - strError = strprintf("Error: Unable to get IP address of this computer (gethostname returned error %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); - return false; - } - struct hostent* phostent = gethostbyname(pszHostName); - if (!phostent) - { - strError = strprintf("Error: Unable to get IP address of this computer (gethostbyname returned error %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); - return false; - } - - // Take the first IP that isn't loopback 127.x.x.x - for (int i = 0; phostent->h_addr_list[i] != NULL; i++) - printf("host ip %d: %s\n", i, CAddress(*(unsigned int*)phostent->h_addr_list[i]).ToStringIP().c_str()); - for (int i = 0; phostent->h_addr_list[i] != NULL; i++) - { - CAddress addr(*(unsigned int*)phostent->h_addr_list[i], DEFAULT_PORT, nLocalServices); - if (addr.IsValid() && addr.GetByte(3) != 127) + struct hostent* phostent = gethostbyname(pszHostName); + if (phostent) { - addrLocalHost = addr; - break; + // Take the first IP that isn't loopback 127.x.x.x + for (int i = 0; phostent->h_addr_list[i] != NULL; i++) + printf("host ip %d: %s\n", i, CAddress(*(unsigned int*)phostent->h_addr_list[i]).ToStringIP().c_str()); + for (int i = 0; phostent->h_addr_list[i] != NULL; i++) + { + CAddress addr(*(unsigned int*)phostent->h_addr_list[i], DEFAULT_PORT, nLocalServices); + if (addr.IsValid() && addr.GetByte(3) != 127) + { + addrLocalHost = addr; + break; + } + } } } #else @@ -1145,45 +1203,85 @@ bool StartNode(string& strError) } else { - if (addrIncoming.ip) + if (addrIncoming.IsValid()) addrLocalHost.ip = addrIncoming.ip; if (GetMyExternalIP(addrLocalHost.ip)) { addrIncoming = addrLocalHost; CWalletDB().WriteSetting("addrIncoming", addrIncoming); + printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); } } - // Get addresses from IRC and advertise ours - if (_beginthread(ThreadIRCSeed, 0, NULL) == -1) - printf("Error: _beginthread(ThreadIRCSeed) failed\n"); - // // Start threads // - if (_beginthread(ThreadSocketHandler, 0, NULL) == -1) - { - strError = "Error: _beginthread(ThreadSocketHandler) failed"; - printf("%s\n", strError.c_str()); - return false; - } - if (_beginthread(ThreadOpenConnections, 0, NULL) == -1) - { - strError = "Error: _beginthread(ThreadOpenConnections) failed"; - printf("%s\n", strError.c_str()); - return false; - } + // Get addresses from IRC and advertise ours + if (!CreateThread(ThreadIRCSeed, NULL)) + printf("Error: CreateThread(ThreadIRCSeed) failed\n"); + + // Send and receive from sockets, accept connections + pthread_t hThreadSocketHandler = CreateThread(ThreadSocketHandler, NULL, true); + + // Initiate outbound connections + if (!CreateThread(ThreadOpenConnections, NULL)) + printf("Error: CreateThread(ThreadOpenConnections) failed\n"); + + // Process messages + if (!CreateThread(ThreadMessageHandler, NULL)) + printf("Error: CreateThread(ThreadMessageHandler) failed\n"); - if (_beginthread(ThreadMessageHandler, 0, NULL) == -1) + // Generate coins in the background + GenerateBitcoins(fGenerateBitcoins); + + // + // Thread monitoring + // + loop { - strError = "Error: _beginthread(ThreadMessageHandler) failed"; - printf("%s\n", strError.c_str()); - return false; + Sleep(15000); + if (GetTime() - nThreadSocketHandlerHeartbeat > 4 * 60) + { + // First see if closing sockets will free it + printf("*** ThreadSocketHandler is stopped ***\n"); + CRITICAL_BLOCK(cs_vNodes) + { + foreach(CNode* pnode, vNodes) + { + bool fGot = false; + TRY_CRITICAL_BLOCK(pnode->cs_vRecv) + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + fGot = true; + if (!fGot) + { + printf("*** closing socket\n"); + closesocket(pnode->hSocket); + pnode->fDisconnect = true; + } + } + } + Sleep(10000); + if (GetTime() - nThreadSocketHandlerHeartbeat < 60) + continue; + + // Hopefully it never comes to this. + // We know it'll always be hung in the recv or send call. + // cs_vRecv or cs_vSend may be left permanently unreleased, + // but we always only use TRY_CRITICAL_SECTION on them. + printf("*** Restarting ThreadSocketHandler ***\n"); + TerminateThread(hThreadSocketHandler, 0); + #ifdef __WXMSW__ + CloseHandle(hThreadSocketHandler); + #endif + vnThreadsRunning[0] = 0; + + // Restart + hThreadSocketHandler = CreateThread(ThreadSocketHandler, NULL, true); + nThreadSocketHandlerHeartbeat = GetTime(); + } } - - return true; } bool StopNode() diff --git a/net.h b/net.h index 7b83d462f..6300d3efc 100644 --- a/net.h +++ b/net.h @@ -29,7 +29,7 @@ CNode* ConnectNode(CAddress addrConnect, int64 nTimeout=0); void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1); bool AnySubscribed(unsigned int nChannel); bool BindListenPort(string& strError=REF(string())); -bool StartNode(string& strError=REF(string())); +void StartNode(void* parg); bool StopNode(); @@ -39,7 +39,6 @@ bool StopNode(); - // // Message header // (4) message start @@ -139,7 +138,7 @@ public: unsigned int nTime; // memory only - unsigned int nLastFailed; + unsigned int nLastTry; CAddress() { @@ -183,7 +182,7 @@ public: ip = INADDR_NONE; port = DEFAULT_PORT; nTime = GetAdjustedTime(); - nLastFailed = 0; + nLastTry = 0; } bool SetAddress(const char* pszIn) @@ -458,6 +457,7 @@ extern uint64 nLocalHostNonce; extern bool fShutdown; extern array vnThreadsRunning; extern SOCKET hListenSocket; +extern int64 nThreadSocketHandlerHeartbeat; extern vector vNodes; extern CCriticalSection cs_vNodes; @@ -486,6 +486,10 @@ public: CDataStream vRecv; CCriticalSection cs_vSend; CCriticalSection cs_vRecv; + int64 nLastSend; + int64 nLastRecv; + int64 nLastSendEmpty; + int64 nTimeConnected; unsigned int nPushPos; CAddress addr; int nVersion; @@ -523,6 +527,10 @@ public: hSocket = hSocketIn; vSend.SetType(SER_NETWORK); vRecv.SetType(SER_NETWORK); + nLastSend = 0; + nLastRecv = 0; + nLastSendEmpty = GetTime(); + nTimeConnected = GetTime(); nPushPos = -1; addr = addrIn; nVersion = 0; @@ -542,7 +550,7 @@ public: CAddress addrYou = (fUseProxy ? CAddress("0.0.0.0") : addr); CAddress addrMe = (fUseProxy ? CAddress("0.0.0.0") : addrLocalHost); RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); - PushMessage("version", VERSION, nLocalServices, nTime, addrYou, addrMe, nLocalHostNonce); + PushMessage("version", VERSION, nLocalServices, nTime, addrYou, addrMe, nLocalHostNonce, "linux-test5"); } ~CNode() @@ -557,11 +565,6 @@ private: public: - bool ReadyToDisconnect() - { - return fDisconnect || GetRefCount() <= 0; - } - int GetRefCount() { return max(nRefCount, 0) + (GetTime() < nReleaseTime ? 1 : 0); @@ -635,6 +638,8 @@ public: AbortMessage(); nPushPos = vSend.size(); vSend << CMessageHeader(pszCommand, 0); + if (fDebug) + printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); printf("sending: %s ", pszCommand); } diff --git a/ui.cpp b/ui.cpp index e12970b81..9d7556cb5 100644 --- a/ui.cpp +++ b/ui.cpp @@ -25,6 +25,7 @@ map mapAddressBook; bool fRandSendTest = false; void RandSend(); extern int g_isPainting; +bool fClosedToTray = false; // Settings int fShowGenerated = true; @@ -413,16 +414,17 @@ void Shutdown(void* parg) void CMainFrame::OnClose(wxCloseEvent& event) { - if (fMinimizeToTray && fMinimizeOnClose && event.CanVeto() && !IsIconized()) + if (fMinimizeOnClose && event.CanVeto() && !IsIconized()) { // Divert close to minimize event.Veto(); + fClosedToTray = true; Iconize(true); } else { Destroy(); - _beginthread(Shutdown, 0, NULL); + CreateThread(Shutdown, NULL); } } @@ -430,7 +432,16 @@ void CMainFrame::OnIconize(wxIconizeEvent& event) { // Hide the task bar button when minimized. // Event is sent when the frame is minimized or restored. - Show(!fMinimizeToTray || !event.Iconized()); + if (!event.Iconized()) + fClosedToTray = false; +#ifndef __WXMSW__ + // Tray is not reliable on Linux gnome + fClosedToTray = false; +#endif + if (fMinimizeToTray && event.Iconized()) + fClosedToTray = true; + Show(!fClosedToTray); + ptaskbaricon->Show(fMinimizeToTray || fClosedToTray); } void CMainFrame::OnMouseEvents(wxMouseEvent& event) @@ -527,7 +538,6 @@ bool CMainFrame::DeleteLine(uint256 hashKey) string FormatTxStatus(const CWalletTx& wtx) { // Status - int nDepth = wtx.GetDepthInMainChain(); if (!wtx.IsFinal()) { if (wtx.nLockTime < 500000000) @@ -535,10 +545,16 @@ string FormatTxStatus(const CWalletTx& wtx) else return strprintf("Open until %s", DateTimeStr(wtx.nLockTime).c_str()); } - else if (nDepth < 6) - return strprintf("%d/unconfirmed", nDepth); else - return strprintf("%d blocks", nDepth); + { + int nDepth = wtx.GetDepthInMainChain(); + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + return strprintf("%d/offline?", nDepth); + else if (nDepth < 6) + return strprintf("%d/unconfirmed", nDepth); + else + return strprintf("%d blocks", nDepth); + } } string SingleLine(const string& strIn) @@ -629,9 +645,17 @@ bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex) foreach(const CTxOut& txout, wtx.vout) nUnmatured += txout.GetCredit(); if (wtx.IsInMainChain()) - strDescription += strprintf(" (%s matures in %d more blocks)", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity()); + { + strDescription = strprintf("Generated (%s matures in %d more blocks)", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity()); + + // Check if the block was requested by anyone + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + strDescription = "Generated - Warning: This block was not received by any other nodes and will probably not be accepted!"; + } else - strDescription += " (not accepted)"; + { + strDescription = "Generated (not accepted)"; + } } } else if (!mapValue["from"].empty() || !mapValue["message"].empty()) @@ -701,8 +725,11 @@ bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex) strStatus, nTime ? DateTimeStr(nTime) : "", "Payment to yourself", - FormatMoney(nNet - nValue, true), - FormatMoney(nValue, true)); + "", + ""); + /// issue: can't tell which is the payment and which is the change anymore + // FormatMoney(nNet - nValue, true), + // FormatMoney(nValue, true)); } else if (fAllFromMe) { @@ -1028,6 +1055,9 @@ void CMainFrame::OnPaintListCtrl(wxPaintEvent& event) string strStatus = strprintf(" %d connections %d blocks %d transactions", vNodes.size(), nBestHeight + 1, nTransactionCount); m_statusBar->SetStatusText(strStatus, 2); + if (fDebug && GetTime() - nThreadSocketHandlerHeartbeat > 60) + m_statusBar->SetStatusText(" ERROR: ThreadSocketHandler has stopped", 0); + // Pass through to listctrl to actually do the paint, we're just hooking the message m_listCtrl->Disconnect(wxEVT_PAINT, (wxObjectEventFunction)NULL, NULL, this); m_listCtrl->GetEventHandler()->ProcessEvent(event); @@ -1237,7 +1267,19 @@ CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetails - strHTML += "Status: " + FormatTxStatus(wtx) + "
"; + strHTML += "Status: " + FormatTxStatus(wtx); + int nRequests = wtx.GetRequestCount(); + if (nRequests != -1) + { + if (nRequests == 0) + strHTML += ", has not been successfully broadcast yet"; + else if (nRequests == 1) + strHTML += strprintf(", broadcast through %d node", nRequests); + else + strHTML += strprintf(", broadcast through %d nodes", nRequests); + } + strHTML += "
"; + strHTML += "Date: " + (nTime ? DateTimeStr(nTime) : "") + "
"; @@ -1366,9 +1408,10 @@ CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetails if (fAllToMe) { // Payment to self - int64 nValue = wtx.vout[0].nValue; - strHTML += "Debit: " + FormatMoney(-nValue) + "
"; - strHTML += "Credit: " + FormatMoney(nValue) + "
"; + /// issue: can't tell which is the payment and which is the change anymore + //int64 nValue = wtx.vout[0].nValue; + //strHTML += "Debit: " + FormatMoney(-nValue) + "
"; + //strHTML += "Credit: " + FormatMoney(nValue) + "
"; } int64 nTxFee = nDebit - wtx.GetValueOut(); @@ -1469,6 +1512,9 @@ COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent) //m_listBox->Append("Test 2"); m_listBox->SetSelection(0); SelectPage(0); +#ifndef __WXMSW__ + m_checkBoxMinimizeOnClose->SetLabel("&Minimize on close"); +#endif // Init values m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee)); @@ -1481,9 +1527,7 @@ COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent) m_spinCtrlLimitProcessors->SetRange(1, nProcessors); m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup()); m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray); - m_checkBoxMinimizeOnClose->Enable(fMinimizeToTray); - m_checkBoxMinimizeOnClose->SetValue(fMinimizeToTray && fMinimizeOnClose); - fTmpMinimizeOnClose = fMinimizeOnClose; + m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose); m_checkBoxUseProxy->SetValue(fUseProxy); m_textCtrlProxyIP->Enable(fUseProxy); m_textCtrlProxyPort->Enable(fUseProxy); @@ -1521,22 +1565,6 @@ void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event) m_spinCtrlLimitProcessors->Enable(event.IsChecked()); } -void COptionsDialog::OnCheckBoxMinimizeToTray(wxCommandEvent& event) -{ - m_checkBoxMinimizeOnClose->Enable(event.IsChecked()); - - // Save the value in fTmpMinimizeOnClose so we can - // show the checkbox unchecked when its parent is unchecked - if (event.IsChecked()) - m_checkBoxMinimizeOnClose->SetValue(fTmpMinimizeOnClose); - else - { - fTmpMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue(); - m_checkBoxMinimizeOnClose->SetValue(false); - } - -} - void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event) { m_textCtrlProxyIP->Enable(event.IsChecked()); @@ -1608,12 +1636,12 @@ void COptionsDialog::OnButtonApply(wxCommandEvent& event) { fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue(); walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray); - ptaskbaricon->Show(fMinimizeToTray); + ptaskbaricon->Show(fMinimizeToTray || fClosedToTray); } - if (fMinimizeOnClose != (fMinimizeToTray ? m_checkBoxMinimizeOnClose->GetValue() : fTmpMinimizeOnClose)) + if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue()) { - fMinimizeOnClose = (fMinimizeToTray ? m_checkBoxMinimizeOnClose->GetValue() : fTmpMinimizeOnClose); + fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue(); walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose); } @@ -1643,6 +1671,9 @@ CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent) if (str.Find('Â') != wxNOT_FOUND) str.Remove(str.Find('Â'), 1); m_staticTextMain->SetLabel(str); +#ifndef __WXMSW__ + SetSize(510, 380); +#endif } void CAboutDialog::OnButtonOK(wxCommandEvent& event) @@ -1849,7 +1880,7 @@ CSendingDialog::CSendingDialog(wxWindow* parent, const CAddress& addrIn, int64 n SetTitle(strprintf("Sending %s to %s", FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str())); m_textCtrlStatus->SetValue(""); - _beginthread(SendingDialogStartTransfer, 0, this); + CreateThread(SendingDialogStartTransfer, this); } CSendingDialog::~CSendingDialog() @@ -2856,7 +2887,7 @@ CViewProductDialog::CViewProductDialog(wxWindow* parent, const CProduct& product this->Layout(); // Request details from seller - _beginthread(ThreadRequestProductDetails, 0, new pair(product, GetEventHandler())); + CreateThread(ThreadRequestProductDetails, new pair(product, GetEventHandler())); } CViewProductDialog::~CViewProductDialog() @@ -3256,6 +3287,7 @@ void CEditReviewDialog::GetReview(CReview& review) enum { ID_TASKBAR_RESTORE = 10001, + ID_TASKBAR_OPTIONS, ID_TASKBAR_GENERATE, ID_TASKBAR_EXIT, }; @@ -3263,6 +3295,7 @@ enum BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon) EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick) EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore) + EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions) EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate) EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate) EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit) @@ -3312,9 +3345,18 @@ void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event) Restore(); } +void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event) +{ + // Since it's modal, get the main window to do it + wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_MENUOPTIONSOPTIONS); + pframeMain->AddPendingEvent(event2); +} + void CMyTaskBarIcon::Restore() { pframeMain->Show(); + wxIconizeEvent event(0, false); + pframeMain->AddPendingEvent(event); pframeMain->Iconize(false); pframeMain->Raise(); } @@ -3344,6 +3386,7 @@ wxMenu* CMyTaskBarIcon::CreatePopupMenu() { wxMenu* pmenu = new wxMenu; pmenu->Append(ID_TASKBAR_RESTORE, "&Open Bitcoin"); + pmenu->Append(ID_TASKBAR_OPTIONS, "O&ptions..."); pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, "&Generate Coins")->Check(fGenerateBitcoins); #ifndef __WXMAC_OSX__ // Mac has built-in quit menu pmenu->AppendSeparator(); @@ -3582,7 +3625,7 @@ bool CMyApp::OnInit2() { CBlockIndex* pindex = (*mi).second; CBlock block; - block.ReadFromDisk(pindex, true); + block.ReadFromDisk(pindex); block.BuildMerkleTree(); block.print(); printf("\n"); @@ -3632,20 +3675,20 @@ bool CMyApp::OnInit2() if (mapArgs.count("-min")) pframeMain->Iconize(true); pframeMain->Show(true); // have to show first to get taskbar button to hide - pframeMain->Show(!fMinimizeToTray || !pframeMain->IsIconized()); - ptaskbaricon->Show(fMinimizeToTray); + if (fMinimizeToTray && pframeMain->IsIconized()) + fClosedToTray = true; + pframeMain->Show(!fClosedToTray); + ptaskbaricon->Show(fMinimizeToTray || fClosedToTray); - _beginthread(ThreadDelayedRepaint, 0, NULL); + CreateThread(ThreadDelayedRepaint, NULL); if (!CheckDiskSpace()) return false; RandAddSeedPerfmon(); - if (!StartNode(strErrors)) - wxMessageBox(strErrors, "Bitcoin"); - - GenerateBitcoins(fGenerateBitcoins); + if (!CreateThread(StartNode, NULL)) + wxMessageBox("Error: CreateThread(StartNode) failed", "Bitcoin"); if (fFirstRun) SetStartOnSystemStartup(true); diff --git a/ui.h b/ui.h index a919c3668..c4bf8b66f 100644 --- a/ui.h +++ b/ui.h @@ -112,7 +112,6 @@ protected: void OnListBox(wxCommandEvent& event); void OnKillFocusTransactionFee(wxFocusEvent& event); void OnCheckBoxLimitProcessors(wxCommandEvent& event); - void OnCheckBoxMinimizeToTray(wxCommandEvent& event); void OnCheckBoxUseProxy(wxCommandEvent& event); void OnKillFocusProxy(wxFocusEvent& event); @@ -447,6 +446,7 @@ protected: // Event handlers void OnLeftButtonDClick(wxTaskBarIconEvent& event); void OnMenuRestore(wxCommandEvent& event); + void OnMenuOptions(wxCommandEvent& event); void OnUpdateUIGenerate(wxUpdateUIEvent& event); void OnMenuGenerate(wxCommandEvent& event); void OnMenuExit(wxCommandEvent& event); diff --git a/uibase.cpp b/uibase.cpp index 7bc8081f0..f05f1095f 100644 --- a/uibase.cpp +++ b/uibase.cpp @@ -45,7 +45,7 @@ CMainFrameBase::CMainFrameBase( wxWindow* parent, wxWindowID id, const wxString& m_menuOptions->Append( m_menuOptionsChangeYourAddress ); wxMenuItem* m_menuOptionsOptions; - m_menuOptionsOptions = new wxMenuItem( m_menuOptions, wxID_ANY, wxString( wxT("&Options...") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuOptionsOptions = new wxMenuItem( m_menuOptions, wxID_MENUOPTIONSOPTIONS, wxString( wxT("&Options...") ) , wxEmptyString, wxITEM_NORMAL ); m_menuOptions->Append( m_menuOptionsOptions ); m_menubar->Append( m_menuOptions, wxT("&Options") ); @@ -428,21 +428,13 @@ COptionsDialogBase::COptionsDialogBase( wxWindow* parent, wxWindowID id, const w bSizer69->Add( m_checkBoxStartOnSystemStartup, 0, wxALL, 5 ); - m_checkBoxMinimizeToTray = new wxCheckBox( m_panelMain, wxID_ANY, wxT("&Minimize to the system tray instead of the taskbar"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxMinimizeToTray = new wxCheckBox( m_panelMain, wxID_ANY, wxT("&Minimize to the tray instead of the taskbar"), wxDefaultPosition, wxDefaultSize, 0 ); bSizer69->Add( m_checkBoxMinimizeToTray, 0, wxALL, 5 ); - wxBoxSizer* bSizer101; - bSizer101 = new wxBoxSizer( wxHORIZONTAL ); + m_checkBoxMinimizeOnClose = new wxCheckBox( m_panelMain, wxID_ANY, wxT("M&inimize to the tray on close"), wxDefaultPosition, wxDefaultSize, 0 ); - - bSizer101->Add( 16, 0, 0, 0, 5 ); - - m_checkBoxMinimizeOnClose = new wxCheckBox( m_panelMain, wxID_ANY, wxT("Mi&nimize to system tray on close"), wxDefaultPosition, wxDefaultSize, 0 ); - - bSizer101->Add( m_checkBoxMinimizeOnClose, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - bSizer69->Add( bSizer101, 1, wxEXPAND, 5 ); + bSizer69->Add( m_checkBoxMinimizeOnClose, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); wxBoxSizer* bSizer102; bSizer102 = new wxBoxSizer( wxHORIZONTAL ); diff --git a/uibase.h b/uibase.h index 2cb99e9a5..e0c152185 100644 --- a/uibase.h +++ b/uibase.h @@ -43,51 +43,52 @@ #define wxID_MAINFRAME 1000 #define wxID_VIEWSHOWGENERATED 1001 #define wxID_OPTIONSGENERATEBITCOINS 1002 -#define wxID_BUTTONSEND 1003 -#define wxID_BUTTONRECEIVE 1004 -#define wxID_TEXTCTRLADDRESS 1005 -#define wxID_BUTTONCOPY 1006 -#define wxID_BUTTONCHANGE 1007 -#define wxID_TRANSACTIONFEE 1008 -#define wxID_PROXYIP 1009 -#define wxID_PROXYPORT 1010 -#define wxID_TEXTCTRLPAYTO 1011 -#define wxID_BUTTONPASTE 1012 -#define wxID_BUTTONADDRESSBOOK 1013 -#define wxID_TEXTCTRLAMOUNT 1014 -#define wxID_CHOICETRANSFERTYPE 1015 -#define wxID_LISTCTRL 1016 -#define wxID_BUTTONRENAME 1017 -#define wxID_BUTTONNEW 1018 -#define wxID_BUTTONEDIT 1019 -#define wxID_BUTTONDELETE 1020 -#define wxID_DEL0 1021 -#define wxID_DEL1 1022 -#define wxID_DEL2 1023 -#define wxID_DEL3 1024 -#define wxID_DEL4 1025 -#define wxID_DEL5 1026 -#define wxID_DEL6 1027 -#define wxID_DEL7 1028 -#define wxID_DEL8 1029 -#define wxID_DEL9 1030 -#define wxID_DEL10 1031 -#define wxID_DEL11 1032 -#define wxID_DEL12 1033 -#define wxID_DEL13 1034 -#define wxID_DEL14 1035 -#define wxID_DEL15 1036 -#define wxID_DEL16 1037 -#define wxID_DEL17 1038 -#define wxID_DEL18 1039 -#define wxID_DEL19 1040 -#define wxID_BUTTONPREVIEW 1041 -#define wxID_BUTTONSAMPLE 1042 -#define wxID_CANCEL2 1043 -#define wxID_BUTTONBACK 1044 -#define wxID_BUTTONNEXT 1045 -#define wxID_SUBMIT 1046 -#define wxID_TEXTCTRL 1047 +#define wxID_MENUOPTIONSOPTIONS 1003 +#define wxID_BUTTONSEND 1004 +#define wxID_BUTTONRECEIVE 1005 +#define wxID_TEXTCTRLADDRESS 1006 +#define wxID_BUTTONCOPY 1007 +#define wxID_BUTTONCHANGE 1008 +#define wxID_TRANSACTIONFEE 1009 +#define wxID_PROXYIP 1010 +#define wxID_PROXYPORT 1011 +#define wxID_TEXTCTRLPAYTO 1012 +#define wxID_BUTTONPASTE 1013 +#define wxID_BUTTONADDRESSBOOK 1014 +#define wxID_TEXTCTRLAMOUNT 1015 +#define wxID_CHOICETRANSFERTYPE 1016 +#define wxID_LISTCTRL 1017 +#define wxID_BUTTONRENAME 1018 +#define wxID_BUTTONNEW 1019 +#define wxID_BUTTONEDIT 1020 +#define wxID_BUTTONDELETE 1021 +#define wxID_DEL0 1022 +#define wxID_DEL1 1023 +#define wxID_DEL2 1024 +#define wxID_DEL3 1025 +#define wxID_DEL4 1026 +#define wxID_DEL5 1027 +#define wxID_DEL6 1028 +#define wxID_DEL7 1029 +#define wxID_DEL8 1030 +#define wxID_DEL9 1031 +#define wxID_DEL10 1032 +#define wxID_DEL11 1033 +#define wxID_DEL12 1034 +#define wxID_DEL13 1035 +#define wxID_DEL14 1036 +#define wxID_DEL15 1037 +#define wxID_DEL16 1038 +#define wxID_DEL17 1039 +#define wxID_DEL18 1040 +#define wxID_DEL19 1041 +#define wxID_BUTTONPREVIEW 1042 +#define wxID_BUTTONSAMPLE 1043 +#define wxID_CANCEL2 1044 +#define wxID_BUTTONBACK 1045 +#define wxID_BUTTONNEXT 1046 +#define wxID_SUBMIT 1047 +#define wxID_TEXTCTRL 1048 /////////////////////////////////////////////////////////////////////////////// /// Class CMainFrameBase @@ -203,7 +204,6 @@ class COptionsDialogBase : public wxDialog wxStaticText* m_staticText35; wxCheckBox* m_checkBoxStartOnSystemStartup; wxCheckBox* m_checkBoxMinimizeToTray; - wxCheckBox* m_checkBoxMinimizeOnClose; wxCheckBox* m_checkBoxUseProxy; diff --git a/uiproject.fbp b/uiproject.fbp index 3aa1c86bc..58a99bddd 100644 --- a/uiproject.fbp +++ b/uiproject.fbp @@ -70,7 +70,7 @@ - + 240,240,240 1 @@ -193,7 +193,7 @@ 0 1 - wxID_ANY + wxID_MENUOPTIONSOPTIONS wxITEM_NORMAL &Options... m_menuOptionsOptions @@ -2319,7 +2319,7 @@ 0 wxID_ANY - &Minimize to the system tray instead of the taskbar + &Minimize to the tray instead of the taskbar m_checkBoxMinimizeToTray @@ -2360,75 +2360,54 @@ 5 - wxEXPAND - 1 - + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + 0 + + 1 + + + 0 + wxID_ANY + M&inimize to the tray on close + - bSizer101 - wxHORIZONTAL - none - - 5 - - 0 - - 0 - protected - 16 - - - - 5 - wxALL|wxALIGN_CENTER_VERTICAL - 0 - - - 0 - - 1 - - - 0 - wxID_ANY - Mi&nimize to system tray on close - - - m_checkBoxMinimizeOnClose - protected - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + m_checkBoxMinimizeOnClose + protected + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/util.cpp b/util.cpp index 4a5b983d2..7a947730c 100644 --- a/util.cpp +++ b/util.cpp @@ -85,14 +85,14 @@ void RandAddSeed() void RandAddSeedPerfmon() { -#ifdef __WXMSW__ - // Don't need this on Linux, OpenSSL automatically uses /dev/urandom // This can take up to 2 seconds, so only do it every 10 minutes static int64 nLastPerfmon; if (GetTime() < nLastPerfmon + 10 * 60) return; nLastPerfmon = GetTime(); +#ifdef __WXMSW__ + // Don't need this on Linux, OpenSSL automatically uses /dev/urandom // Seed with the entire set of perfmon data unsigned char pdata[250000]; memset(pdata, 0, sizeof(pdata)); @@ -109,9 +109,30 @@ void RandAddSeedPerfmon() printf("%s RandAddSeed() %d bytes\n", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str(), nSize); } +#else + printf("%s RandAddSeed()\n", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); #endif } +uint64 GetRand(uint64 nMax) +{ + if (nMax == 0) + return 0; + + // The range of the random source must be a multiple of the modulus + // to give every possible output value an equal possibility + uint64 nRange = (UINT64_MAX / nMax) * nMax; + uint64 nRand = 0; + do + RAND_bytes((unsigned char*)&nRand, sizeof(nRand)); + while (nRand >= nRange); + return (nRand % nMax); +} + + + + + @@ -449,28 +470,6 @@ string GetDataDir() -uint64 GetRand(uint64 nMax) -{ - if (nMax == 0) - return 0; - - // The range of the random source must be a multiple of the modulus - // to give every possible output value an equal possibility - uint64 nRange = (_UI64_MAX / nMax) * nMax; - uint64 nRand = 0; - do - RAND_bytes((unsigned char*)&nRand, sizeof(nRand)); - while (nRand >= nRange); - return (nRand % nMax); -} - - - - - - - - // @@ -483,7 +482,6 @@ uint64 GetRand(uint64 nMax) // note: NTP isn't implemented yet, so until then we just use the median // of other nodes clocks to correct ours. // - int64 GetTime() { return time(NULL); diff --git a/util.h b/util.h index 8fcfcd0dc..ddac44944 100644 --- a/util.h +++ b/util.h @@ -54,9 +54,13 @@ inline T& REF(const T& val) return (T&)val; } -#ifndef __WXMSW__ -#define _UI64_MAX UINT64_MAX -#define _I64_MAX INT64_MAX +#ifdef __WXMSW__ +#define MSG_NOSIGNAL 0 +#define MSG_DONTWAIT 0 +#define UINT64_MAX _UI64_MAX +#define INT64_MAX _I64_MAX +#define INT64_MIN _I64_MIN +#else #define WSAGetLastError() errno #define WSAEWOULDBLOCK EWOULDBLOCK #define WSAEMSGSIZE EMSGSIZE @@ -74,18 +78,6 @@ typedef u_int SOCKET; #define MAX_PATH 1024 #define Sleep(n) wxMilliSleep(n) #define Beep(n1,n2) (0) -inline int _beginthread(void(*pfn)(void*), unsigned nStack, void* parg) { thread(bind(pfn, parg)); return 0; } -inline void _endthread() { pthread_exit(NULL); } -inline int GetCurrentThread() { return 0; } -// threads are processes on linux, so setpriority affects just the one thread -inline void SetThreadPriority(int nThread, int nPriority) { setpriority(PRIO_PROCESS, getpid(), nPriority); } -#define THREAD_PRIORITY_LOWEST PRIO_MIN -#define THREAD_PRIORITY_BELOW_NORMAL 2 -#define THREAD_PRIORITY_NORMAL 0 -#define THREAD_PRIORITY_ABOVE_NORMAL 0 -#endif -#ifndef MSG_NOSIGNAL -#define MSG_NOSIGNAL 0 #endif @@ -133,6 +125,7 @@ void AddTimeData(unsigned int ip, int64 nTime); + // Wrapper to automatically initialize critical sections class CCriticalSection { @@ -201,8 +194,6 @@ public: - - inline int OutputDebugStringF(const char* pszFormat, ...) { int ret = 0; @@ -498,3 +489,83 @@ inline uint160 Hash160(const vector& vch) RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); return hash2; } + + + + + + + + + + + +// Note: It turns out we might have been able to use boost::thread +// by using TerminateThread(boost::thread.native_handle(), 0); +#ifdef __WXMSW__ +typedef HANDLE pthread_t; + +inline pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=false) +{ + DWORD nUnused = 0; + HANDLE hthread = + CreateThread( + NULL, // default security + 0, // inherit stack size from parent + (LPTHREAD_START_ROUTINE)pfn, // function pointer + parg, // argument + 0, // creation option, start immediately + &nUnused); // thread identifier + if (hthread == NULL) + { + printf("Error: CreateThread() returned %d\n", GetLastError()); + return (pthread_t)0; + } + if (!fWantHandle) + { + CloseHandle(hthread); + return (pthread_t)-1; + } + return hthread; +} + +inline void SetThreadPriority(int nPriority) +{ + SetThreadPriority(GetCurrentThread(), nPriority); +} +#else +inline pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=false) +{ + pthread_t hthread = 0; + int ret = pthread_create(&hthread, NULL, (void*(*)(void*))pfn, parg); + if (ret != 0) + { + printf("Error: pthread_create() returned %d\n", ret); + return (pthread_t)0; + } + if (!fWantHandle) + return (pthread_t)-1; + return hthread; +} + +#define THREAD_PRIORITY_LOWEST PRIO_MIN +#define THREAD_PRIORITY_BELOW_NORMAL 2 +#define THREAD_PRIORITY_NORMAL 0 +#define THREAD_PRIORITY_ABOVE_NORMAL 0 + +inline void SetThreadPriority(int nPriority) +{ + // threads are processes on linux, so PRIO_PROCESS affects just the one thread + setpriority(PRIO_PROCESS, getpid(), nPriority); +} + +inline bool TerminateThread(pthread_t hthread, unsigned int nExitCode) +{ + return (pthread_cancel(hthread) == 0); +} + +inline void ExitThread(unsigned int nExitCode) +{ + pthread_exit((void*)nExitCode); +} +#endif